diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index dc65659..02ba950 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -827,6 +827,9 @@ "distanceKm": "{} كم", "discountPercentPlus": "{}%+", "visitMerchantWebsite": "زيارة موقع التاجر", - "merchantWebsiteNotAvailable": "موقع التاجر غير متوفر" + "merchantWebsiteNotAvailable": "موقع التاجر غير متوفر", + "favoriteAddedSuccess": "تمت إضافة التاجر إلى المفضلة بنجاح", + "favoriteRemovedSuccess": "تمت إزالة التاجر من المفضلة بنجاح", + "favoriteError": "حدث خطأ أثناء تحديث المفضلة" } diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 2a6c0d7..8dea94b 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -827,6 +827,9 @@ "distanceKm": "{} km", "discountPercentPlus": "{}%+", "visitMerchantWebsite": "Visit Merchant Website", - "merchantWebsiteNotAvailable": "Merchant website not available" + "merchantWebsiteNotAvailable": "Merchant website not available", + "favoriteAddedSuccess": "Added to favorites successfully", + "favoriteRemovedSuccess": "Removed from favorites successfully", + "favoriteError": "An error occurred while updating favorites" } diff --git a/lib/api/offers_and_discounts_api_client.dart b/lib/api/offers_and_discounts_api_client.dart index c904196..589110f 100644 --- a/lib/api/offers_and_discounts_api_client.dart +++ b/lib/api/offers_and_discounts_api_client.dart @@ -7,6 +7,7 @@ import 'package:geolocator/geolocator.dart'; import 'package:mohem_flutter_app/api/api_client.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/consts.dart'; +import 'package:mohem_flutter_app/models/generic_response_model.dart'; import 'package:mohem_flutter_app/models/offers_and_discounts/get_categories_list.dart'; import 'package:mohem_flutter_app/models/offers_and_discounts/get_offers_list.dart'; import 'package:mohem_flutter_app/models/offers_and_discounts/wala_plus_category_model.dart'; @@ -259,6 +260,7 @@ class OffersAndDiscountsApiClient { BuildContext? context, String? mobileNumber, String? query, + String? userReference, }) async { String url = ApiConsts.walaPlusRetailers; String pUserName = _resolveUserName(); @@ -299,6 +301,13 @@ class OffersAndDiscountsApiClient { } _cleanWalaPlusParams(postParams); + // Pass WP_User_Reference: if filled then user ID, else null value + if (userReference != null && userReference.trim().isNotEmpty) { + postParams["WP_User_Reference"] = userReference.trim(); + } else { + postParams["WP_User_Reference"] = null; + } + return await ApiClient().postJsonForObject( (response) { if (response != null) { @@ -333,11 +342,17 @@ class OffersAndDiscountsApiClient { ); } - Future getWalaPlusRetailerById({required String retailerId, BuildContext? context, String? mobileNumber}) async { + Future getWalaPlusRetailerById({ + required String retailerId, + BuildContext? context, + String? mobileNumber, + String? userReference, + }) async { String url = ApiConsts.walaPlusRetailersByID; String pUserName = _resolveUserName(); String pMobileNumber = _resolveMobileNumber(overrideMobile: mobileNumber); String pDepNationalId = _resolveDepNationalId(); + String pUserReference = _resolveUserReference(overrideUserReference: userReference); Map postParams = _getBaseWalaPlusParams(); postParams["WP_RetailerID"] = retailerId; @@ -352,6 +367,7 @@ class OffersAndDiscountsApiClient { postParams["CompanyID"] = 1; if (context != null) { + postParams["LanguageID"] = AppState().getLanguageID(context); try { Position? position = await _getCurrentPositionSilently(context); log("context position: $position"); @@ -366,7 +382,14 @@ class OffersAndDiscountsApiClient { _cleanWalaPlusParams(postParams); - debugPrint("getWalaPlusRetailerById: calling $url with WP_RetailerID: $retailerId"); + // Pass WP_User_Reference: if filled then user ID, else null value + if (pUserReference.isNotEmpty) { + postParams["WP_User_Reference"] = pUserReference; + } else { + postParams["WP_User_Reference"] = null; + } + + debugPrint("getWalaPlusRetailerById: calling $url with params: $postParams"); return await ApiClient().postJsonForObject( (response) { @@ -396,6 +419,13 @@ class OffersAndDiscountsApiClient { } else { model = WalaPlusRetailerModel.fromJson(map); } + if (model?.isFavorite == null) { + if (map.containsKey('isfavorite')) { + model?.isFavorite = map['isfavorite'] == true || map['isfavorite'] == 1 || map['isfavorite'] == 'true' || map['isfavorite'] == '1'; + } else if (map.containsKey('is_favorite')) { + model?.isFavorite = map['is_favorite'] == true || map['is_favorite'] == 1 || map['is_favorite'] == 'true' || map['is_favorite'] == '1'; + } + } if ((model?.offers == null || model!.offers!.isEmpty) && (map.containsKey('offers') || map.containsKey('Offers'))) { model?.offers = WalaPlusRetailerModel.parseOffersList(map['offers'] ?? map['Offers']); } @@ -415,6 +445,13 @@ class OffersAndDiscountsApiClient { if (model != null) { if (response is Map) { + if (model.isFavorite == null) { + if (response.containsKey('isfavorite')) { + model.isFavorite = response['isfavorite'] == true || response['isfavorite'] == 1 || response['isfavorite'] == 'true' || response['isfavorite'] == '1'; + } else if (response.containsKey('is_favorite')) { + model.isFavorite = response['is_favorite'] == true || response['is_favorite'] == 1 || response['is_favorite'] == 'true' || response['is_favorite'] == '1'; + } + } if (model.website == null || model.website!.isEmpty || model.website == "null") { String? web = (response['website'] ?? response['merchant_website'] ?? response['website_url'] ?? response['url'] ?? response['merchant_url'])?.toString(); if (web != null && web.trim().isNotEmpty && web != "null") { @@ -429,7 +466,7 @@ class OffersAndDiscountsApiClient { model.locations = WalaPlusRetailerModel.parseLocationsList(response['locations'] ?? response['Locations'] ?? response['location'] ?? response['Location']); } } - debugPrint("getWalaPlusRetailerById: parsed retailer '${model.name}', offers: ${model.offers?.length ?? 0}, locations: ${model.locations?.length ?? 0}"); + debugPrint("getWalaPlusRetailerById: parsed retailer '${model.name}', isFavorite: ${model.isFavorite}, offers: ${model.offers?.length ?? 0}, locations: ${model.locations?.length ?? 0}"); return model; } } @@ -440,6 +477,10 @@ class OffersAndDiscountsApiClient { ); } + String resolveUserReference({String? overrideUserReference}) { + return _resolveUserReference(overrideUserReference: overrideUserReference); + } + String _resolveUserReference({String? overrideUserReference}) { if (overrideUserReference != null && overrideUserReference.trim().isNotEmpty) { return overrideUserReference.trim(); @@ -505,4 +546,134 @@ class OffersAndDiscountsApiClient { postParams, ); } + + Future addWalaPlusFavorite({ + Object? retailerId, + List? retailerIds, + BuildContext? context, + String? mobileNumber, + String? userReference, + }) async { + String url = ApiConsts.walaPlusFavoritesAdd; + String pUserName = _resolveUserName(); + String pMobileNumber = _resolveMobileNumber(overrideMobile: mobileNumber); + String pUserReference = _resolveUserReference(overrideUserReference: userReference); + + List ids = []; + if (retailerIds != null && retailerIds.isNotEmpty) { + ids.addAll(retailerIds); + } else if (retailerId != null) { + if (retailerId is List) { + ids.addAll(retailerId.map((e) => e.toString())); + } else { + ids.add(retailerId.toString()); + } + } + + Map postParams = _getBaseWalaPlusParams(); + postParams["P_USER_NAME"] = pUserName; + postParams["UserName"] = pUserName; + if (pMobileNumber.isNotEmpty) { + postParams["MobileNumber"] = pMobileNumber; + } + if (pUserReference.isNotEmpty) { + postParams["WP_User_Reference"] = pUserReference; + } + postParams["WP_Retailer_Ids"] = ids; + + if (context != null) { + postParams["LanguageID"] = AppState().getLanguageID(context); + } + + if (ApiConsts.isDependetLogin) { + String pDepNationalId = _resolveDepNationalId(); + if (pDepNationalId.isNotEmpty) { + postParams["P_DEP_NATIONAL_ID"] = pDepNationalId; + } + postParams["MemberType"] = 2; + } else { + postParams["MemberType"] = 1; + } + + _cleanWalaPlusParams(postParams); + + debugPrint("addWalaPlusFavorite calling $url with params: $postParams"); + + return await ApiClient().postJsonForObject( + (response) { + if (response != null) { + debugPrint("addWalaPlusFavorite response: $response"); + return GenericResponseModel.fromJson(response); + } + return null; + }, + url, + postParams, + ); + } + + Future removeWalaPlusFavorite({ + Object? retailerId, + List? retailerIds, + BuildContext? context, + String? mobileNumber, + String? userReference, + }) async { + String url = ApiConsts.walaPlusFavoritesRemove; + String pUserName = _resolveUserName(); + String pMobileNumber = _resolveMobileNumber(overrideMobile: mobileNumber); + String pUserReference = _resolveUserReference(overrideUserReference: userReference); + + List ids = []; + if (retailerIds != null && retailerIds.isNotEmpty) { + ids.addAll(retailerIds); + } else if (retailerId != null) { + if (retailerId is List) { + ids.addAll(retailerId.map((e) => e.toString())); + } else { + ids.add(retailerId.toString()); + } + } + + Map postParams = _getBaseWalaPlusParams(); + postParams["P_USER_NAME"] = pUserName; + postParams["UserName"] = pUserName; + if (pMobileNumber.isNotEmpty) { + postParams["MobileNumber"] = pMobileNumber; + } + if (pUserReference.isNotEmpty) { + postParams["WP_User_Reference"] = pUserReference; + } + postParams["WP_Retailer_Ids"] = ids; + + if (context != null) { + postParams["LanguageID"] = AppState().getLanguageID(context); + } + + if (ApiConsts.isDependetLogin) { + String pDepNationalId = _resolveDepNationalId(); + if (pDepNationalId.isNotEmpty) { + postParams["P_DEP_NATIONAL_ID"] = pDepNationalId; + } + postParams["MemberType"] = 2; + } else { + postParams["MemberType"] = 1; + } + + _cleanWalaPlusParams(postParams); + + debugPrint("removeWalaPlusFavorite calling $url with params: $postParams"); + + return await ApiClient().postJsonForObject( + (response) { + if (response != null) { + debugPrint("removeWalaPlusFavorite response: $response"); + return GenericResponseModel.fromJson(response); + } + return null; + }, + url, + postParams, + ); + } } diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index 1fb1db4..3debbc7 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -45,6 +45,8 @@ class ApiConsts { static String walaPlusRetailers = erpRest + "WalaPlus_Retailers"; static String walaPlusRetailersByID = erpRest + "WalaPlus_RetailersByID"; static String walaPlusOfferRedeem = erpRest + "WalaPlus_OfferRedeem"; + static String walaPlusFavoritesAdd = erpRest + "WalaPlus_FavoritesAdd"; + static String walaPlusFavoritesRemove = erpRest + "WalaPlus_FavoritesRemove"; static String processContactMazayaAccess = erpRest + "PROCESS_CONTACT_MAZAYA_ACCESS"; static String ssoAuthRedirection = ssoBaseUrl + "/api/auth/connect"; diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index 61743a2..46087d0 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -812,4 +812,7 @@ abstract class LocaleKeys { static const discountPercentPlus = 'discountPercentPlus'; static const visitMerchantWebsite = 'visitMerchantWebsite'; static const merchantWebsiteNotAvailable = 'merchantWebsiteNotAvailable'; + static const favoriteAddedSuccess = 'favoriteAddedSuccess'; + static const favoriteRemovedSuccess = 'favoriteRemovedSuccess'; + static const favoriteError = 'favoriteError'; } diff --git a/lib/models/generic_response_model.dart b/lib/models/generic_response_model.dart index 59edbde..4329653 100644 --- a/lib/models/generic_response_model.dart +++ b/lib/models/generic_response_model.dart @@ -404,6 +404,7 @@ class GenericResponseModel { GetWalaplusDepInfoModel? getWalaplusDepInfoModel; dynamic getWalaplusDepInfoModelInternal; List? walaPlusCategories; + String? wpFavoritesAddRemoveMessage; String? businessCardQROutside; EmpBusinessCardInfoModel? empBusinessCardInfoModel; @@ -683,6 +684,7 @@ class GenericResponseModel { this.getWalaplusDepInfoModel, this.getWalaplusDepInfoModelInternal, this.walaPlusCategories, + this.wpFavoritesAddRemoveMessage, this.businessCardQROutside, this.empBusinessCardInfoModel, this.isEmpLastLoginToDevice @@ -1493,6 +1495,7 @@ class GenericResponseModel { walaPlusCategories!.add(WalaPlusCategoryModel.fromJson(v)); }); } + wpFavoritesAddRemoveMessage = json['WP_Favorites_AddRemove_Message'] ?? json['wp_Favorites_AddRemove_Message']; isEmpLastLoginToDevice = json["IsEmpLastLoginToDevice"]; } @@ -1994,6 +1997,9 @@ class GenericResponseModel { if (walaPlusCategories != null) { data['WalaPlus_Categories'] = walaPlusCategories!.map((WalaPlusCategoryModel v) => v.toJson()).toList(); } + if (this.wpFavoritesAddRemoveMessage != null) { + data['WP_Favorites_AddRemove_Message'] = this.wpFavoritesAddRemoveMessage; + } data["IsEmpLastLoginToDevice"] = this.isEmpLastLoginToDevice; return data; } diff --git a/lib/models/offers_and_discounts/wala_plus_retailer_model.dart b/lib/models/offers_and_discounts/wala_plus_retailer_model.dart index 7760b1c..a5cac49 100644 --- a/lib/models/offers_and_discounts/wala_plus_retailer_model.dart +++ b/lib/models/offers_and_discounts/wala_plus_retailer_model.dart @@ -1,5 +1,4 @@ import 'dart:convert'; -import 'dart:developer'; import 'package:easy_localization/easy_localization.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; import 'package:mohem_flutter_app/models/offers_and_discounts/wala_plus_category_model.dart'; @@ -81,6 +80,10 @@ class WalaPlusRetailersByIDResponse { if ((model.website == null || model.website!.isEmpty || model.website == "null") && (json['website'] != null || json['merchant_website'] != null || json['url'] != null)) { model.website = (json['website'] ?? json['merchant_website'] ?? json['url'])?.toString(); } + if (model.isFavorite == null && (json.containsKey('isfavorite') || json.containsKey('is_favorite'))) { + var favVal = json['isfavorite'] ?? json['is_favorite']; + model.isFavorite = favVal == true || favVal == 1 || favVal == 'true' || favVal == '1'; + } retList.add(model); } else if (r is Map) { var model = WalaPlusRetailerModel.fromJson(Map.from(r)); @@ -89,6 +92,10 @@ class WalaPlusRetailersByIDResponse { if ((model.website == null || model.website!.isEmpty || model.website == "null") && (json['website'] != null || json['merchant_website'] != null || json['url'] != null)) { model.website = (json['website'] ?? json['merchant_website'] ?? json['url'])?.toString(); } + if (model.isFavorite == null && (json.containsKey('isfavorite') || json.containsKey('is_favorite'))) { + var favVal = json['isfavorite'] ?? json['is_favorite']; + model.isFavorite = favVal == true || favVal == 1 || favVal == 'true' || favVal == '1'; + } retList.add(model); } } @@ -99,6 +106,10 @@ class WalaPlusRetailersByIDResponse { if ((model.website == null || model.website!.isEmpty || model.website == "null") && (json['website'] != null || json['merchant_website'] != null || json['url'] != null)) { model.website = (json['website'] ?? json['merchant_website'] ?? json['url'])?.toString(); } + if (model.isFavorite == null && (json.containsKey('isfavorite') || json.containsKey('is_favorite'))) { + var favVal = json['isfavorite'] ?? json['is_favorite']; + model.isFavorite = favVal == true || favVal == 1 || favVal == 'true' || favVal == '1'; + } retList.add(model); } @@ -358,6 +369,29 @@ class WalaPlusRetailerModel { } } + bool? parsedIsFavorite; + if (targetJson.containsKey('isfavorite')) { + parsedIsFavorite = targetJson['isfavorite'] == true || + targetJson['isfavorite'] == 1 || + targetJson['isfavorite'] == 'true' || + targetJson['isfavorite'] == '1'; + } else if (targetJson.containsKey('is_favorite')) { + parsedIsFavorite = targetJson['is_favorite'] == true || + targetJson['is_favorite'] == 1 || + targetJson['is_favorite'] == 'true' || + targetJson['is_favorite'] == '1'; + } else if (targetJson.containsKey('isFavorite')) { + parsedIsFavorite = targetJson['isFavorite'] == true || + targetJson['isFavorite'] == 1 || + targetJson['isFavorite'] == 'true' || + targetJson['isFavorite'] == '1'; + } else if (targetJson.containsKey('favorite')) { + parsedIsFavorite = targetJson['favorite'] == true || + targetJson['favorite'] == 1 || + targetJson['favorite'] == 'true' || + targetJson['favorite'] == '1'; + } + return WalaPlusRetailerModel( id: targetJson['id']?.toString() ?? @@ -375,11 +409,7 @@ class WalaPlusRetailerModel { logo: targetJson['logo']?.toString(), logoThumb: targetJson['logo_thumb']?.toString(), website: foundWebsite, - isFavorite: targetJson['is_favorite'] == true || - targetJson['is_favorite'] == 1 || - targetJson['is_favorite'] == 'true' || - targetJson['isFavorite'] == true || - targetJson['favorite'] == true, + isFavorite: parsedIsFavorite, type: targetJson['type']?.toString(), usage: targetJson['usage'] is String ? decodeUnicodeString(targetJson['usage']) : targetJson['usage'], weight: targetJson['weight'], @@ -488,6 +518,7 @@ class WalaPlusRetailerModel { data['country_name_ar'] = countryNameAr; data['country_name_en'] = countryNameEn; data['is_favorite'] = isFavorite; + data['isfavorite'] = isFavorite; if (offers != null) { data['offers'] = offers!.map((v) => v.toJson()).toList(); } diff --git a/lib/ui/landing/dashboard_screen_for_dependet.dart b/lib/ui/landing/dashboard_screen_for_dependet.dart index ca8cd86..f84cf78 100644 --- a/lib/ui/landing/dashboard_screen_for_dependet.dart +++ b/lib/ui/landing/dashboard_screen_for_dependet.dart @@ -52,6 +52,7 @@ class _DashboardScreenForDependetState extends State String _filterDistance = '5km'; String _filterMinDiscount = 'any'; bool isNearestOffers = true; + bool isFavoriteOnly = false; bool isLoading = true; bool isLoadingMore = false; bool isCategoriesLoading = true; @@ -150,6 +151,14 @@ class _DashboardScreenForDependetState extends State return name.replaceAll(RegExp(r'\s*-\s*'), ' ').replaceAll(RegExp(r'\s+'), ' ').trim(); } + String? _getUserReferenceForFavorites() { + String ref = OffersAndDiscountsApiClient().resolveUserReference(); + if (ref.trim().isNotEmpty) return ref.trim(); + String? userName = AppState().getUserName; + if (userName != null && userName.trim().isNotEmpty) return userName.trim(); + return null; + } + @override void dispose() { _debounceTimer?.cancel(); @@ -294,7 +303,8 @@ class _DashboardScreenForDependetState extends State String catParam = (targetCategoryId == "all" || targetCategoryId == "0") ? "" : targetCategoryId; String? offerTypeParam = OffersAndDiscountsApiClient.mapOfferTypeFilter(selectedTypeFilter); String? queryParam = searchQuery.trim().isNotEmpty ? searchQuery.trim() : null; - print("Fetching WalaPlus retailers: category='$catParam', offerType='$offerTypeParam', query='$queryParam', page=1, per_page=$pageSize"); + String? userRef = isFavoriteOnly ? _getUserReferenceForFavorites() : null; + print("Fetching WalaPlus retailers: category='$catParam', offerType='$offerTypeParam', query='$queryParam', userReference='$userRef', page=1, per_page=$pageSize"); WalaPlusRetailersResponse? response = await OffersAndDiscountsApiClient().getWalaPlusRetailers( categoryId: catParam, page: 1, @@ -302,9 +312,15 @@ class _DashboardScreenForDependetState extends State offerType: offerTypeParam, query: queryParam, context: isNearestOffers ? context : null, + userReference: userRef, ); retailersList = response?.retailers ?? []; + if (isFavoriteOnly) { + for (var r in retailersList) { + r.isFavorite = true; + } + } _filteredRetailersList = retailersList; currentPage = 1; totalPages = response?.pagesCount ?? 1; @@ -335,8 +351,9 @@ class _DashboardScreenForDependetState extends State String catParam = (currentCategoryId == "all" || currentCategoryId == "0") ? "" : currentCategoryId; String? offerTypeParam = OffersAndDiscountsApiClient.mapOfferTypeFilter(selectedTypeFilter); String? queryParam = searchQuery.trim().isNotEmpty ? searchQuery.trim() : null; + String? userRef = isFavoriteOnly ? _getUserReferenceForFavorites() : null; - print("Fetching WalaPlus retailers next page: $nextPage of $totalPages, category: '$catParam', offerType: '$offerTypeParam', query: '$queryParam'"); + print("Fetching WalaPlus retailers next page: $nextPage of $totalPages, category: '$catParam', offerType: '$offerTypeParam', query: '$queryParam', userReference='$userRef'"); WalaPlusRetailersResponse? response = await OffersAndDiscountsApiClient().getWalaPlusRetailers( categoryId: catParam, @@ -345,10 +362,16 @@ class _DashboardScreenForDependetState extends State offerType: offerTypeParam, query: queryParam, context: isNearestOffers ? context : null, + userReference: userRef, ); List newRetailers = response?.retailers ?? []; if (newRetailers.isNotEmpty) { + if (isFavoriteOnly) { + for (var r in newRetailers) { + r.isFavorite = true; + } + } currentPage = nextPage; totalPages = response?.pagesCount ?? totalPages; hasMore = currentPage < totalPages; @@ -385,8 +408,15 @@ class _DashboardScreenForDependetState extends State } } - void _openRetailer(WalaPlusRetailerModel item) { - Navigator.pushNamed(context, AppRoutes.walaPlusRetailerDetails, arguments: item); + void _openRetailer(WalaPlusRetailerModel item) async { + await Navigator.pushNamed(context, AppRoutes.walaPlusRetailerDetails, arguments: item); + if (mounted) { + if (isFavoriteOnly) { + fetchOffers(categoryId: currentCategoryId.isEmpty ? "all" : currentCategoryId); + } else { + setState(() {}); + } + } } Future _saveDependentSessionToPrefs() async { @@ -577,11 +607,18 @@ class _DashboardScreenForDependetState extends State child: InkWell( borderRadius: BorderRadius.circular(8), onTap: () { - // Favorite + setState(() { + isFavoriteOnly = !isFavoriteOnly; + }); + fetchOffers(categoryId: currentCategoryId.isEmpty ? "all" : currentCategoryId); }, - child: const Padding( - padding: EdgeInsets.all(3), - child: Icon(Icons.favorite_border_rounded, size: 24, color: Color(0xFF1E293B)), + child: Padding( + padding: const EdgeInsets.all(3), + child: Icon( + isFavoriteOnly ? Icons.favorite_rounded : Icons.favorite_border_rounded, + size: 24, + color: isFavoriteOnly ? const Color(0xFFE11D48) : const Color(0xFF1E293B), + ), ), ), ), diff --git a/lib/ui/landing/wala_plus_retailer_details_screen.dart b/lib/ui/landing/wala_plus_retailer_details_screen.dart index 88ec533..61588c2 100644 --- a/lib/ui/landing/wala_plus_retailer_details_screen.dart +++ b/lib/ui/landing/wala_plus_retailer_details_screen.dart @@ -5,16 +5,19 @@ import 'package:flutter/material.dart'; import 'package:flutter_html/flutter_html.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:mohem_flutter_app/api/api_client.dart'; import 'package:mohem_flutter_app/api/offers_and_discounts_api_client.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; import 'package:mohem_flutter_app/classes/consts.dart'; import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/config/routes.dart'; +import 'package:mohem_flutter_app/exceptions/api_exception.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; +import 'package:mohem_flutter_app/models/generic_response_model.dart'; import 'package:mohem_flutter_app/models/offers_and_discounts/wala_plus_retailer_model.dart'; import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; import 'package:mohem_flutter_app/widgets/app_in_app_webview_screen.dart'; @@ -28,8 +31,9 @@ import 'package:url_launcher/url_launcher.dart'; class WalaPlusRetailerDetailsScreen extends StatefulWidget { final WalaPlusRetailerModel? retailer; final String? retailerId; + final String? userReference; - const WalaPlusRetailerDetailsScreen({Key? key, this.retailer, this.retailerId}) : super(key: key); + const WalaPlusRetailerDetailsScreen({Key? key, this.retailer, this.retailerId, this.userReference}) : super(key: key); @override State createState() => _WalaPlusRetailerDetailsScreenState(); @@ -40,15 +44,18 @@ class _WalaPlusRetailerDetailsScreenState extends State _toggleFavorite() async { + if (_isTogglingFavorite) return; + String retailerId = _retailer?.id ?? _effectiveRetailerId ?? ""; + if (retailerId.isEmpty) { + return; + } + + setState(() { + _isTogglingFavorite = true; + }); + Utils.showLoading(context); + + try { + bool targetFavorite = !_isFavorite; + GenericResponseModel? response; + if (targetFavorite) { + response = await OffersAndDiscountsApiClient().addWalaPlusFavorite( + retailerId: retailerId, + context: context, + userReference: _effectiveUserReference, + ); + } else { + response = await OffersAndDiscountsApiClient().removeWalaPlusFavorite( + retailerId: retailerId, + context: context, + userReference: _effectiveUserReference, + ); + } + + Utils.hideLoading(context); + + if (response != null && response.errorMessage == null) { + if (mounted) { + setState(() { + _isFavorite = targetFavorite; + _retailer?.isFavorite = targetFavorite; + }); + } + bool isAr = Directionality.of(context) == ui.TextDirection.rtl || AppState().isArabic(context); + String? msg; + if (isAr) { + msg = targetFavorite ? LocaleKeys.favoriteAddedSuccess.tr() : LocaleKeys.favoriteRemovedSuccess.tr(); + } else { + msg = response.wpFavoritesAddRemoveMessage; + if (msg == null || msg.trim().isEmpty) { + msg = targetFavorite ? LocaleKeys.favoriteAddedSuccess.tr() : LocaleKeys.favoriteRemovedSuccess.tr(); + } + } + Utils.showToast(msg); + } else { + String error = response?.errorEndUserMessage ?? response?.errorMessage ?? LocaleKeys.favoriteError.tr(); + Utils.showToast(error); + } + } catch (e) { + Utils.hideLoading(context); + debugPrint("Error toggling favorite: $e"); + String errorMsg = LocaleKeys.favoriteError.tr(); + if (e is APIException && e.error?.errorMessage != null && e.error!.errorMessage!.isNotEmpty) { + errorMsg = e.error!.errorMessage!; + } + Utils.showToast(errorMsg); + } finally { + if (mounted) { + setState(() { + _isTogglingFavorite = false; + }); + } else { + _isTogglingFavorite = false; + } + } + } + Future _openInAppBrowser(String url, {String? offerType}) async { String title = _retailer?.name ?? LocaleKeys.offerDetails.tr(); // Offer types seen so far: "promo", "discount", "unique_promo", "promo_unique_qr" (and @@ -588,12 +690,7 @@ class _WalaPlusRetailerDetailsScreenState extends State