diff --git a/mih_ui/lib/main.dart b/mih_ui/lib/main.dart index b9c221fd..ebf3c1d0 100644 --- a/mih_ui/lib/main.dart +++ b/mih_ui/lib/main.dart @@ -4,8 +4,8 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:ken_logger/ken_logger.dart'; -import 'package:mih_package_toolkit/mih_package_toolkit.dart'; import 'package:mzansi_innovation_hub/mih_hive/mzansi_profile_hive_data.dart'; +import 'package:mzansi_innovation_hub/mih_hive/mzansi_wallet_hive_data.dart'; import 'package:mzansi_innovation_hub/mih_providers/about_mih_provider.dart'; import 'package:mzansi_innovation_hub/mih_providers/mih_access_controlls_provider.dart'; import 'package:mzansi_innovation_hub/mih_providers/mih_authentication_provider.dart'; @@ -123,10 +123,14 @@ class _MzansiInnovationHubState extends State { create: (context) => MihAuthenticationProvider(), ), ChangeNotifierProvider( - create: (context) => MzansiProfileProvider(MzansiProfileHiveData()), + create: (context) => MzansiProfileProvider( + MzansiProfileHiveData(), + ), ), ChangeNotifierProvider( - create: (context) => MzansiWalletProvider(), + create: (context) => MzansiWalletProvider( + MzansiWalletHiveData(), + ), ), ChangeNotifierProvider( create: (context) => MzansiAiProvider(), diff --git a/mih_ui/lib/main_dev.dart b/mih_ui/lib/main_dev.dart index 645315c3..55881da2 100644 --- a/mih_ui/lib/main_dev.dart +++ b/mih_ui/lib/main_dev.dart @@ -14,6 +14,7 @@ import 'package:mzansi_innovation_hub/mih_objects/app_user.dart'; import 'package:mzansi_innovation_hub/mih_objects/business.dart'; import 'package:mzansi_innovation_hub/mih_objects/business_employee.dart'; import 'package:mzansi_innovation_hub/mih_objects/business_user.dart'; +import 'package:mzansi_innovation_hub/mih_objects/loyalty_card.dart'; import 'package:mzansi_innovation_hub/mih_objects/profile_link.dart'; import 'package:mzansi_innovation_hub/mih_objects/user_consent.dart'; import 'package:pwa_install/pwa_install.dart'; @@ -29,8 +30,10 @@ void main() async { apiDomain: AppEnviroment.baseApiUrl, apiBasePath: "/auth", ); + // Offine Hive Data await Hive.initFlutter('mih_offline_storage'); Hive.registerAdapters(); + // Mzansi Profile Data await Hive.openBox('user_box'); await Hive.openBox('business_box'); await Hive.openBox('business_user_box'); @@ -39,6 +42,9 @@ void main() async { await Hive.openBox('personal_profile_links_box'); await Hive.openBox('business_profile_links_box'); await Hive.openBox('business_employees_box'); + // Mzansi Wallet Data + await Hive.openBox('loyalty_card_box'); + await Hive.openBox('fav_loyalty_card_box'); // await Firebase.initializeApp( // // options: DefaultFirebaseOptions.currentPlatform, diff --git a/mih_ui/lib/mih_hive/hive_adapters.dart b/mih_ui/lib/mih_hive/hive_adapters.dart index 40db3c0e..78d43de4 100644 --- a/mih_ui/lib/mih_hive/hive_adapters.dart +++ b/mih_ui/lib/mih_hive/hive_adapters.dart @@ -4,6 +4,7 @@ import 'package:mzansi_innovation_hub/mih_objects/app_user.dart'; import 'package:mzansi_innovation_hub/mih_objects/business.dart'; import 'package:mzansi_innovation_hub/mih_objects/business_employee.dart'; import 'package:mzansi_innovation_hub/mih_objects/business_user.dart'; +import 'package:mzansi_innovation_hub/mih_objects/loyalty_card.dart'; import 'package:mzansi_innovation_hub/mih_objects/profile_link.dart'; import 'package:mzansi_innovation_hub/mih_objects/user_consent.dart'; @@ -14,5 +15,6 @@ import 'package:mzansi_innovation_hub/mih_objects/user_consent.dart'; AdapterSpec(), AdapterSpec(), AdapterSpec(), + AdapterSpec(), ]) part 'hive_adapters.g.dart'; diff --git a/mih_ui/lib/mih_hive/hive_adapters.g.dart b/mih_ui/lib/mih_hive/hive_adapters.g.dart index 0431eab4..df0aa419 100644 --- a/mih_ui/lib/mih_hive/hive_adapters.g.dart +++ b/mih_ui/lib/mih_hive/hive_adapters.g.dart @@ -338,3 +338,55 @@ class BusinessEmployeeAdapter extends TypeAdapter { runtimeType == other.runtimeType && typeId == other.typeId; } + +class MIHLoyaltyCardAdapter extends TypeAdapter { + @override + final typeId = 6; + + @override + MIHLoyaltyCard read(BinaryReader reader) { + final numOfFields = reader.readByte(); + final fields = { + for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), + }; + return MIHLoyaltyCard( + idloyalty_cards: (fields[0] as num).toInt(), + app_id: fields[1] as String, + shop_name: fields[2] as String, + card_number: fields[3] as String, + favourite: fields[4] as String, + priority_index: (fields[5] as num).toInt(), + nickname: fields[6] as String, + ); + } + + @override + void write(BinaryWriter writer, MIHLoyaltyCard obj) { + writer + ..writeByte(7) + ..writeByte(0) + ..write(obj.idloyalty_cards) + ..writeByte(1) + ..write(obj.app_id) + ..writeByte(2) + ..write(obj.shop_name) + ..writeByte(3) + ..write(obj.card_number) + ..writeByte(4) + ..write(obj.favourite) + ..writeByte(5) + ..write(obj.priority_index) + ..writeByte(6) + ..write(obj.nickname); + } + + @override + int get hashCode => typeId.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is MIHLoyaltyCardAdapter && + runtimeType == other.runtimeType && + typeId == other.typeId; +} diff --git a/mih_ui/lib/mih_hive/hive_adapters.g.yaml b/mih_ui/lib/mih_hive/hive_adapters.g.yaml index c8fb856c..7193a532 100644 --- a/mih_ui/lib/mih_hive/hive_adapters.g.yaml +++ b/mih_ui/lib/mih_hive/hive_adapters.g.yaml @@ -1,7 +1,7 @@ # Generated by Hive CE # Manual modifications may be necessary for certain migrations # Check in to version control -nextTypeId: 6 +nextTypeId: 7 types: AppUser: typeId: 0 @@ -125,3 +125,21 @@ types: index: 6 username: index: 7 + MIHLoyaltyCard: + typeId: 6 + nextIndex: 7 + fields: + idloyalty_cards: + index: 0 + app_id: + index: 1 + shop_name: + index: 2 + card_number: + index: 3 + favourite: + index: 4 + priority_index: + index: 5 + nickname: + index: 6 diff --git a/mih_ui/lib/mih_hive/hive_registrar.g.dart b/mih_ui/lib/mih_hive/hive_registrar.g.dart index 641002d4..9d9d484b 100644 --- a/mih_ui/lib/mih_hive/hive_registrar.g.dart +++ b/mih_ui/lib/mih_hive/hive_registrar.g.dart @@ -11,6 +11,7 @@ extension HiveRegistrar on HiveInterface { registerAdapter(BusinessAdapter()); registerAdapter(BusinessEmployeeAdapter()); registerAdapter(BusinessUserAdapter()); + registerAdapter(MIHLoyaltyCardAdapter()); registerAdapter(ProfileLinkAdapter()); registerAdapter(UserConsentAdapter()); } @@ -22,6 +23,7 @@ extension IsolatedHiveRegistrar on IsolatedHiveInterface { registerAdapter(BusinessAdapter()); registerAdapter(BusinessEmployeeAdapter()); registerAdapter(BusinessUserAdapter()); + registerAdapter(MIHLoyaltyCardAdapter()); registerAdapter(ProfileLinkAdapter()); registerAdapter(UserConsentAdapter()); } diff --git a/mih_ui/lib/mih_hive/mzansi_wallet_hive_data.dart b/mih_ui/lib/mih_hive/mzansi_wallet_hive_data.dart new file mode 100644 index 00000000..66fa4187 --- /dev/null +++ b/mih_ui/lib/mih_hive/mzansi_wallet_hive_data.dart @@ -0,0 +1,59 @@ +import 'package:hive_ce_flutter/hive_ce_flutter.dart'; +import 'package:ken_logger/ken_logger.dart'; +import 'package:mzansi_innovation_hub/mih_objects/loyalty_card.dart'; +import 'package:mzansi_innovation_hub/mih_providers/mzansi_profile_provider.dart'; +import 'package:mzansi_innovation_hub/mih_services/mih_mzansi_wallet_services.dart'; + +class MzansiWalletHiveData { + final Box _loyaltyCardBox = + Hive.box('loyalty_card_box'); + final Box _favLoyaltyCardBox = + Hive.box('fav_loyalty_card_box'); + + // Get Offline Data + List getCachedLoyaltyCards() { + final cards = _loyaltyCardBox.values.toList(); + cards.sort((a, b) => a.shop_name.compareTo(b.shop_name)); + return cards; + } + + List getCachedFavLoyaltyCards() { + final cards = _favLoyaltyCardBox.values.toList(); + cards.sort((a, b) => a.shop_name.compareTo(b.shop_name)); + return cards; + } + + // Cache data for offline use + Future cacheFavLoyaltyCardsData( + List remoteLoyaltyCards) async { + await _favLoyaltyCardBox.clear(); + await _favLoyaltyCardBox.addAll(remoteLoyaltyCards); + KenLogger.success("Favourite Loyalty Cards Cached"); + } + + Future cacheLoyaltyCardsData( + List remoteFavLoyaltyCards) async { + await _loyaltyCardBox.clear(); + await _loyaltyCardBox.addAll(remoteFavLoyaltyCards); + KenLogger.success("Loyalty Cards Cached"); + } + + // Sync Local Data from data from MIH Server + Future syncWalletWithServer( + MzansiProfileProvider profileProvider) async { + try { + final remoteLoyaltyCards = await MIHMzansiWalletApis.getLoyaltyCardsV2( + profileProvider.user!.app_id); + cacheLoyaltyCardsData(remoteLoyaltyCards); + final remoteFavLoyaltyCards = + await MIHMzansiWalletApis.getFavouriteLoyaltyCardsV2( + profileProvider.user!.app_id); + cacheFavLoyaltyCardsData(remoteFavLoyaltyCards); + return true; + } catch (error) { + KenLogger.warning("App Operating in Offline Mode. Sync Paused"); + return false; + // KenLogger.warning("App operating offline mode. Sync paused: $error"); + } + } +} diff --git a/mih_ui/lib/mih_packages/mzansi_wallet/mih_wallet.dart b/mih_ui/lib/mih_packages/mzansi_wallet/mih_wallet.dart index f9b20e4b..cf61c013 100644 --- a/mih_ui/lib/mih_packages/mzansi_wallet/mih_wallet.dart +++ b/mih_ui/lib/mih_packages/mzansi_wallet/mih_wallet.dart @@ -20,50 +20,32 @@ class MihWallet extends StatefulWidget { class _MihWalletState extends State { bool _isLoadingInitialData = true; - late final MihCards _cards; - late final MihCardFavourites _cardFavourites; Future _loadInitialData() async { - setState(() { - _isLoadingInitialData = true; - }); MzansiProfileProvider mzansiProfileProvider = context.read(); + mzansiProfileProvider.loadCachedProfileState(); MzansiWalletProvider walletProvider = context.read(); + walletProvider.loadCachedWallet(); if (mzansiProfileProvider.user == null) { - await MihDataHelperServices().loadUserDataWithBusinessesData( - mzansiProfileProvider, - ); + mzansiProfileProvider.syncWithMihServerData(); + } + if (walletProvider.loyaltyCards.isEmpty) { + walletProvider.syncWithMihServerData(mzansiProfileProvider); } - await setLoyaltyCards(mzansiProfileProvider, walletProvider); - await setFavouritesCards(mzansiProfileProvider, walletProvider); setState(() { _isLoadingInitialData = false; }); } - Future setLoyaltyCards( - MzansiProfileProvider mzansiProfileProvider, - MzansiWalletProvider walletProvider, - ) async { - await MIHMzansiWalletApis.getLoyaltyCards( - walletProvider, mzansiProfileProvider.user!.app_id, context); - } - - Future setFavouritesCards( - MzansiProfileProvider mzansiProfileProvider, - MzansiWalletProvider walletProvider, - ) async { - await MIHMzansiWalletApis.getFavouriteLoyaltyCards( - walletProvider, mzansiProfileProvider.user!.app_id, context); - } - @override void initState() { super.initState(); - _cards = MihCards(); - _cardFavourites = MihCardFavourites(); - _loadInitialData(); + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted) { + _loadInitialData(); + } + }); } @override @@ -122,8 +104,8 @@ class _MihWalletState extends State { List getToolBody() { return [ - _cards, - _cardFavourites, + MihCards(), + MihCardFavourites(), ]; } diff --git a/mih_ui/lib/mih_packages/mzansi_wallet/package_tools/mih_cards.dart b/mih_ui/lib/mih_packages/mzansi_wallet/package_tools/mih_cards.dart index bc10d4ad..71f715c0 100644 --- a/mih_ui/lib/mih_packages/mzansi_wallet/package_tools/mih_cards.dart +++ b/mih_ui/lib/mih_packages/mzansi_wallet/package_tools/mih_cards.dart @@ -1,5 +1,6 @@ import 'package:flutter_speed_dial/flutter_speed_dial.dart'; import 'package:mih_package_toolkit/mih_package_toolkit.dart'; +import 'package:mzansi_innovation_hub/mih_providers/mzansi_profile_provider.dart'; import 'package:mzansi_innovation_hub/mih_providers/mzansi_wallet_provider.dart'; import 'package:mzansi_innovation_hub/mih_packages/mzansi_wallet/components/mih_add_card_window.dart'; import 'package:mzansi_innovation_hub/mih_objects/loyalty_card.dart'; @@ -161,6 +162,37 @@ class _MihCardsState extends State { onTap: () { addCardWindow(context, width); }, + ), + SpeedDialChild( + child: Icon( + Icons.cloud_sync_rounded, + color: MihColors.primary(), + ), + label: "Sync Wallet", + labelBackgroundColor: MihColors.green(), + labelStyle: TextStyle( + color: MihColors.primary(), + fontWeight: FontWeight.bold, + ), + backgroundColor: MihColors.green(), + onTap: () async { + MzansiProfileProvider profileProvider = + context.read(); + bool success = await walletProvider + .syncWithMihServerData(profileProvider); + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar( + MihSnackBar( + child: Text( + success + ? "Wallet Synced with MIH Server." + : "MIH App operation in Offline Mode", + ), + // backgroundColor: success ? null : MihColors.red(), + ), + ); + } + }, ) ]), ) diff --git a/mih_ui/lib/mih_providers/mzansi_wallet_provider.dart b/mih_ui/lib/mih_providers/mzansi_wallet_provider.dart index ac9b13ac..2517561d 100644 --- a/mih_ui/lib/mih_providers/mzansi_wallet_provider.dart +++ b/mih_ui/lib/mih_providers/mzansi_wallet_provider.dart @@ -1,17 +1,38 @@ import 'package:flutter/material.dart'; +import 'package:ken_logger/ken_logger.dart'; +import 'package:mzansi_innovation_hub/mih_hive/mzansi_wallet_hive_data.dart'; import 'package:mzansi_innovation_hub/mih_objects/loyalty_card.dart'; +import 'package:mzansi_innovation_hub/mih_providers/mzansi_profile_provider.dart'; class MzansiWalletProvider extends ChangeNotifier { + final MzansiWalletHiveData _hiveData; + List loyaltyCards; List favouriteCards; int toolIndex; - MzansiWalletProvider({ + MzansiWalletProvider( + this._hiveData, { this.loyaltyCards = const [], this.favouriteCards = const [], this.toolIndex = 0, }); + void loadCachedWallet() { + loyaltyCards = _hiveData.getCachedLoyaltyCards(); + favouriteCards = _hiveData.getCachedFavLoyaltyCards(); + + KenLogger.success("Mzansi Wallet Loaded from Cache"); + notifyListeners(); + } + + Future syncWithMihServerData( + MzansiProfileProvider profileProvider) async { + bool success = await _hiveData.syncWalletWithServer(profileProvider); + loadCachedWallet(); + return success; + } + void reset() { toolIndex = 0; loyaltyCards = []; diff --git a/mih_ui/lib/mih_services/mih_mzansi_wallet_services.dart b/mih_ui/lib/mih_services/mih_mzansi_wallet_services.dart index c2fcd967..7290ab5d 100644 --- a/mih_ui/lib/mih_services/mih_mzansi_wallet_services.dart +++ b/mih_ui/lib/mih_services/mih_mzansi_wallet_services.dart @@ -31,6 +31,21 @@ class MIHMzansiWalletApis { } } + static Future> getLoyaltyCardsV2( + String app_id, + ) async { + final response = await http.get(Uri.parse( + "${AppEnviroment.baseApiUrl}/mzasni-wallet/loyalty-cards/$app_id")); + if (response.statusCode == 200) { + Iterable l = jsonDecode(response.body); + List myCards = List.from( + l.map((model) => MIHLoyaltyCard.fromJson(model))); + return myCards; + } else { + throw Exception('failed to fatch loyalty cards'); + } + } + static Future getFavouriteLoyaltyCards( MzansiWalletProvider walletProvider, String app_id, @@ -44,10 +59,24 @@ class MIHMzansiWalletApis { List myCards = List.from( l.map((model) => MIHLoyaltyCard.fromJson(model))); walletProvider.setFavouriteCards(cards: myCards); + } else { + throw Exception('failed to fatch loyalty cards'); + } + } + + static Future> getFavouriteLoyaltyCardsV2( + String app_id, + ) async { + final response = await http.get(Uri.parse( + "${AppEnviroment.baseApiUrl}/mzasni-wallet/loyalty-cards/favourites/$app_id")); + if (response.statusCode == 200) { + Iterable l = jsonDecode(response.body); + List myCards = List.from( + l.map((model) => MIHLoyaltyCard.fromJson(model))); + return myCards; + } else { + throw Exception('failed to fatch loyalty cards'); } - // else { - // throw Exception('failed to fatch loyalty cards'); - // } } /// This function is used to Delete loyalty card from users mzansi wallet.