From 70870f1903e3671de92aec96d5d67da60560ae08 Mon Sep 17 00:00:00 2001 From: Faiz Hashmi Date: Sun, 8 Dec 2024 17:27:59 +0300 Subject: [PATCH] added lazy loading --- assets/langs/ar-SA.json | 3 +- assets/langs/en-US.json | 3 +- lib/classes/consts.dart | 2 +- lib/generated/codegen_loader.g.dart | 6 +- lib/generated/locale_keys.g.dart | 1 + .../advertisment_models/ad_details_model.dart | 5 +- lib/repositories/ads_repo.dart | 64 +++++++++++-- lib/repositories/branch_repo.dart | 44 ++++++--- lib/services/common_auth_service.dart | 4 +- lib/view_models/ad_view_model.dart | 92 ++++++++++++++++--- lib/view_models/requests_view_model.dart | 36 +++++++- lib/view_models/service_view_model.dart | 41 ++++++--- .../components/ads_list_widget.dart | 5 +- .../chat/widgets/chat_message_widget.dart | 2 +- lib/views/common_fragments/ads_fragment.dart | 32 +++++-- lib/views/requests/review_request_offer.dart | 5 +- lib/views/splash/splash_page.dart | 59 +++++++++--- 17 files changed, 321 insertions(+), 83 deletions(-) diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 9eba80f..d45f3c8 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -760,5 +760,6 @@ "tapToSeeItems": "اضغط لرؤية العناصر", "tapToSelect": "اضغط للاختيار", "noteCopyItemsExplanation": "ملاحظة: ستتمكن من نسخ العناصر من خدمة إلى أخرى في الفئة المحددة. يجب عليك إنشاء الخدمات أولاً ويجب أن تكون معتمدة. ثم ستتمكن من الحصول على الخدمات المتاحة التي يمكنك نسخ جميع العناصر منها أو تحديد العناصر التي تريد نسخها.", - "requestCreatedOn": "تم إنشاء الطلب في" + "requestCreatedOn": "تم إنشاء الطلب في", + "online": "عبر الإنترنت" } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 7027c81..d0521bd 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -758,5 +758,6 @@ "tapToSelect": "Tap to select", "tapToSeeItems": "Tap to see items", "noteCopyItemsExplanation": "Note: You will be able to copy items from one service to another in a selected category. You must create the services first and they should be approved. Then you will be able to get the available services from which you can copy all or selected items.", - "requestCreatedOn": "Request created on" + "requestCreatedOn": "Request created on", + "online": "Online" } \ No newline at end of file diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index d5033d6..1036905 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -55,7 +55,7 @@ class ApiConsts { static String servicesGet = "${baseUrlServices}api/ServiceProviders/Services_Get"; static String serviceProviderServiceCreate = "${baseUrlServices}api/ServiceProviders/ServiceProviderService_Create"; static String serviceProviderServiceUpdate = "${baseUrlServices}api/ServiceProviders/ServiceProviderService_Update"; - static String serviceProviderServiceStatusUpdate = "${baseUrlServices}api/ServiceProviders/CategoryAndServiceDeactivateBySP"; + static String serviceProviderServiceStatusUpdate = "${baseUrlServices}api/ServiceProviders/CategoryAndService_ActivateDeactivateBySP"; static String getProviderServices = "${baseUrlServices}api/ServiceProviders/ServiceProviderService_Get"; static String setScheduleInactive = "${baseUrlServices}api/ServiceProviders/BranchAppointmentSchedule_IsActiveUpdate"; static String serviceProviderAppointmentGetByCategoryOrService = "${baseUrlServices}api/ServiceProviders/ServiceProviderAppointment_GetByCategoryOrService"; diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index 905622f..c60600c 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -776,7 +776,8 @@ class CodegenLoader extends AssetLoader{ "tapToSeeItems": "اضغط لرؤية العناصر", "tapToSelect": "اضغط للاختيار", "noteCopyItemsExplanation": "ملاحظة: ستتمكن من نسخ العناصر من خدمة إلى أخرى في الفئة المحددة. يجب عليك إنشاء الخدمات أولاً ويجب أن تكون معتمدة. ثم ستتمكن من الحصول على الخدمات المتاحة التي يمكنك نسخ جميع العناصر منها أو تحديد العناصر التي تريد نسخها.", - "requestCreatedOn": "تم إنشاء الطلب في" + "requestCreatedOn": "تم إنشاء الطلب في", + "online": "عبر الإنترنت" }; static const Map en_US = { "firstTimeLogIn": "First Time Log In", @@ -1538,7 +1539,8 @@ static const Map en_US = { "tapToSelect": "Tap to select", "tapToSeeItems": "Tap to see items", "noteCopyItemsExplanation": "Note: You will be able to copy items from one service to another in a selected category. You must create the services first and they should be approved. Then you will be able to get the available services from which you can copy all or selected items.", - "requestCreatedOn": "Request created on" + "requestCreatedOn": "Request created on", + "online": "Online" }; static const Map> mapLocales = {"ar_SA": ar_SA, "en_US": en_US}; } diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index 396b4fa..42bf0f8 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -740,5 +740,6 @@ abstract class LocaleKeys { static const tapToSelect = 'tapToSelect'; static const noteCopyItemsExplanation = 'noteCopyItemsExplanation'; static const requestCreatedOn = 'requestCreatedOn'; + static const online = 'online'; } diff --git a/lib/models/advertisment_models/ad_details_model.dart b/lib/models/advertisment_models/ad_details_model.dart index fb9ba56..83f4575 100644 --- a/lib/models/advertisment_models/ad_details_model.dart +++ b/lib/models/advertisment_models/ad_details_model.dart @@ -50,6 +50,7 @@ class AdDetailsModel { String? warrantyYears; CreatedByRoleEnum? createdByRoleEnum; List? adMessages; + int? totalItemsCount; AdDetailsModel({ this.id, @@ -95,6 +96,7 @@ class AdDetailsModel { this.createdByRoleEnum, this.modifiedOn, this.adMessages, + this.totalItemsCount, }); int getRandomValue({required int min, required int max}) { @@ -103,7 +105,7 @@ class AdDetailsModel { return randomNumber; } - AdDetailsModel.fromJson(Map json, bool isMyAds) { + AdDetailsModel.fromJson(Map json, bool isMyAds, int totalItems) { id = json['id']; startdate = json['startdate']; enddate = json['enddate']; @@ -150,6 +152,7 @@ class AdDetailsModel { createdByRoleEnum = (json['createdByRole'] as int).toCreatedByRoleEnum(); isMyAd = isMyAds; isReservedByMe = false; + totalItemsCount = totalItems; } } diff --git a/lib/repositories/ads_repo.dart b/lib/repositories/ads_repo.dart index d5d83be..91ab7b4 100644 --- a/lib/repositories/ads_repo.dart +++ b/lib/repositories/ads_repo.dart @@ -1,10 +1,12 @@ import 'dart:developer'; +import 'package:easy_localization/easy_localization.dart'; import 'package:mc_common_app/api/api_client.dart'; import 'package:mc_common_app/classes/app_state.dart'; import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/config/dependency_injection.dart'; import 'package:mc_common_app/extensions/string_extensions.dart'; +import 'package:mc_common_app/generated/locale_keys.g.dart'; import 'package:mc_common_app/models/advertisment_models/ad_details_model.dart'; import 'package:mc_common_app/models/advertisment_models/ads_bank_details_model.dart'; import 'package:mc_common_app/models/advertisment_models/ads_duration_model.dart'; @@ -14,6 +16,7 @@ import 'package:mc_common_app/models/advertisment_models/special_service_model.d import 'package:mc_common_app/models/chat_models/buyers_chat_for_ads_model.dart'; import 'package:mc_common_app/models/general_models/generic_resp_model.dart'; import 'package:mc_common_app/utils/enums.dart'; +import 'package:mc_common_app/utils/utils.dart'; abstract class AdsRepo { Future> getAdsDuration({required int? countryId}); @@ -22,7 +25,12 @@ abstract class AdsRepo { Future createOrUpdateAd({required AdsCreationPayloadModel adsCreationPayloadModel, required bool isCreateNew}); - Future> getAllAds({required bool isMyAds, AdPostStatus? adPostStatus, CreatedByRoleEnum? createdByRoleEnum}); + Future> getAllAds({ + required bool isMyAds, + AdPostStatus? adPostStatus, + CreatedByRoleEnum? createdByRoleEnum, + int? page, + }); Future> getExploreAdsBasedOnFilters({ List? cityIdsList, @@ -31,6 +39,8 @@ abstract class AdsRepo { List? createdByRolesIdsList, List? vehicleAdConditionIdsList, List? vehicleAdCreatedDateList, + int page, + bool isMyAds = false, }); Future> getMyReservedAds(); @@ -62,10 +72,10 @@ class AdsRepoImp implements AdsRepo { @override Future> getAdsDuration({required int? countryId}) async { - int roleID = appState.getUser!.data!.userInfo!.roleId ?? 0; + int roleID = appState.getUser.data!.userInfo!.roleId ?? 0; var param = { - "CountryID": countryId.toString() ?? "0", - "roleID": roleID.toString() ?? "0", + "CountryID": countryId.toString(), + "roleID": roleID.toString(), }; GenericRespModel adsGenericModel = await apiClient.getJsonForObject( token: appState.getUser.data!.accessToken, @@ -168,7 +178,12 @@ class AdsRepoImp implements AdsRepo { } @override - Future> getAllAds({required isMyAds, AdPostStatus? adPostStatus, CreatedByRoleEnum? createdByRoleEnum}) async { + Future> getAllAds({ + required isMyAds, + AdPostStatus? adPostStatus, + CreatedByRoleEnum? createdByRoleEnum, + int? page, + }) async { Map onlyMyAdsParams = { "userID": appState.getUser.data!.userInfo!.userId ?? "", }; @@ -176,6 +191,7 @@ class AdsRepoImp implements AdsRepo { onlyMyAdsParams.addAll({ "AdsStatuses": ["${adPostStatus.getIdFromAdPostStatusEnum()}"], "PageSize": "30", + "PageIndex": page != null ? page.toString() : "0", }); } var allAdsParams = { @@ -183,6 +199,7 @@ class AdsRepoImp implements AdsRepo { "isActive": "true", //only Active ADS "isExplore": "true", "PageSize": "30", + "PageIndex": page != null ? page.toString() : "0", }; if (!isMyAds && createdByRoleEnum != null) { @@ -196,7 +213,7 @@ class AdsRepoImp implements AdsRepo { ApiConsts.vehicleAdsGet, queryParameters: isMyAds ? onlyMyAdsParams : allAdsParams, ); - List vehicleAdsDetails = List.generate(adsGenericModel.data.length, (index) => AdDetailsModel.fromJson(adsGenericModel.data[index], isMyAds)); + List vehicleAdsDetails = List.generate(adsGenericModel.data.length, (index) => AdDetailsModel.fromJson(adsGenericModel.data[index], isMyAds, adsGenericModel.totalItemsCount ?? 1)); return vehicleAdsDetails; } @@ -208,6 +225,8 @@ class AdsRepoImp implements AdsRepo { List? createdByRolesIdsList, List? vehicleAdConditionIdsList, List? vehicleAdCreatedDateList, + int? page, + bool isMyAds = false, }) async { var parameters = { "CityIDs": cityIdsList ?? [], @@ -218,17 +237,29 @@ class AdsRepoImp implements AdsRepo { "VehicleNew": vehicleAdConditionIdsList ?? [], "CreatedOn": (vehicleAdCreatedDateList != null && vehicleAdCreatedDateList.isNotEmpty) ? vehicleAdCreatedDateList.first.toString() : "", "isActive": "true", //only Active ADS - "isExplore": "true", + "isExplore": isMyAds.toString(), "PageSize": "30", + "PageIndex": page != null ? page.toString() : "1", }; + if (isMyAds) { + parameters.addAll({ + "userID": appState.getUser.data!.userInfo!.userId ?? "", + }); + } + GenericRespModel adsGenericModel = await apiClient.getJsonForObject( token: appState.getUser.data!.accessToken, (json) => GenericRespModel.fromJson(json), ApiConsts.vehicleAdsGet, queryParameters: parameters, ); - List vehicleAdsDetails = List.generate(adsGenericModel.data.length, (index) => AdDetailsModel.fromJson(adsGenericModel.data[index], false)); + + if (adsGenericModel.messageStatus != 1 || adsGenericModel.data == null) { + Utils.showToast(adsGenericModel.message ?? LocaleKeys.somethingWrong.tr()); + return []; + } + List vehicleAdsDetails = List.generate(adsGenericModel.data.length, (index) => AdDetailsModel.fromJson(adsGenericModel.data[index], false, adsGenericModel.totalItemsCount ?? 1)); return vehicleAdsDetails; } @@ -244,6 +275,11 @@ class AdsRepoImp implements AdsRepo { ApiConsts.myAdsReserveGet, queryParameters: params, ); + + if (adsGenericModel.messageStatus != 1 || adsGenericModel.data == null) { + Utils.showToast(adsGenericModel.message ?? LocaleKeys.somethingWrong.tr()); + return []; + } List reservedAds = List.generate(adsGenericModel.data.length, (index) => MyReservedAdsRespModel.fromJson(adsGenericModel.data[index])); List selectedIdsString = reservedAds.map((component) => component.adsID.toString()).toList(); @@ -266,7 +302,11 @@ class AdsRepoImp implements AdsRepo { queryParameters: params, ApiConsts.vehicleAdsGet, ); - List vehicleAdsDetails = List.generate(adsGenericModel.data.length, (index) => AdDetailsModel.fromJson(adsGenericModel.data[index], true)); + if (adsGenericModel.messageStatus != 1 || adsGenericModel.data == null) { + Utils.showToast(adsGenericModel.message ?? LocaleKeys.somethingWrong.tr()); + return []; + } + List vehicleAdsDetails = List.generate(adsGenericModel.data.length, (index) => AdDetailsModel.fromJson(adsGenericModel.data[index], true, adsGenericModel.totalItemsCount ?? 1)); for (int i = 0; i < vehicleAdsDetails.length; i++) { vehicleAdsDetails[i].adReserveStatus = (reservedAds[i].adsReserveStatus ?? 0).toAdRserveStatusEnum(); @@ -288,7 +328,11 @@ class AdsRepoImp implements AdsRepo { queryParameters: params, ApiConsts.vehicleAdsGet, ); - List vehicleAdsDetails = List.generate(adsGenericModel.data.length, (index) => AdDetailsModel.fromJson(adsGenericModel.data[index], true)); + if (adsGenericModel.messageStatus != 1 || adsGenericModel.data == null) { + Utils.showToast(adsGenericModel.message ?? LocaleKeys.somethingWrong.tr()); + return []; + } + List vehicleAdsDetails = List.generate(adsGenericModel.data.length, (index) => AdDetailsModel.fromJson(adsGenericModel.data[index], true, adsGenericModel.totalItemsCount ?? 1)); return vehicleAdsDetails; } diff --git a/lib/repositories/branch_repo.dart b/lib/repositories/branch_repo.dart index 068c285..37f720d 100644 --- a/lib/repositories/branch_repo.dart +++ b/lib/repositories/branch_repo.dart @@ -75,6 +75,8 @@ abstract class BranchRepo { Future updateServiceStatus({required int branchId, required List serviceIds, required ServiceStatusEnum serviceStatusEnum}); + Future updateCategoryStatus({required int branchId, required int categoryId, required ServiceStatusEnum serviceStatusEnum}); + Future> getAppointmentsByCategoryOrService({required int branchId, required int serviceId}); Future getMatchedServices(int oldBranchId, int newBranchId, int categoryId); @@ -236,7 +238,7 @@ class BranchRepoImp implements BranchRepo { GenericRespModel adsGenericModel = await apiClient.getJsonForObject( token: t, - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), ApiConsts.serviceProviderDDLGet, ); List providersList = List.generate(adsGenericModel.data.length, (index) => ProviderBasicDataModel.fromJson(adsGenericModel.data[index])); @@ -268,8 +270,7 @@ class BranchRepoImp implements BranchRepo { serviceProviderBranchImages.add(imageMap); } - String lat = "0", - long = "0"; + String lat = "0", long = "0"; try { lat = latitude.toString().substring(0, 9); long = longitude.toString().substring(0, 9); @@ -332,6 +333,19 @@ class BranchRepoImp implements BranchRepo { return await apiClient.getJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.serviceProviderServiceStatusUpdate, queryParameters: map, token: t); } + @override + Future updateCategoryStatus({required int branchId, required int categoryId, required ServiceStatusEnum serviceStatusEnum}) async { + int providerID = AppState().getUser.data!.userInfo!.providerId; + var map = { + "ServiceProviderID": providerID.toString(), + "ProviderBranchID": branchId.toString(), + "ServiceCategoryID": categoryId.toString(), + "Status": serviceStatusEnum.getIdFromServiceStatusEnum().toString(), + }; + String t = AppState().getUser.data!.accessToken ?? ""; + return await apiClient.getJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.serviceProviderServiceStatusUpdate, queryParameters: map, token: t); + } + @override Future> getAppointmentsByCategoryOrService({required int branchId, required int serviceId}) async { var map = { @@ -341,7 +355,7 @@ class BranchRepoImp implements BranchRepo { String t = AppState().getUser.data!.accessToken ?? ""; GenericRespModel genericRespModel = await apiClient.getJsonForObject( - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), ApiConsts.serviceProviderAppointmentGetByCategoryOrService, queryParameters: map, token: t, @@ -416,7 +430,7 @@ class BranchRepoImp implements BranchRepo { String t = AppState().getUser.data!.accessToken ?? ""; GenericRespModel adsGenericModel = await apiClient.getJsonForObject( - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), ApiConsts.getAllNearBranches, token: appState.getUser.data!.accessToken, queryParameters: queryParameters, @@ -444,7 +458,7 @@ class BranchRepoImp implements BranchRepo { "longitude": longitude.toString(), }; GenericRespModel adsGenericModel = await apiClient.getJsonForObject( - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), ApiConsts.getAllNearBranches, token: appState.getUser.data!.accessToken, queryParameters: queryParameters, @@ -456,7 +470,7 @@ class BranchRepoImp implements BranchRepo { @override Future> getMyRecentBranchesWithServices() async { GenericRespModel adsGenericModel = await apiClient.getJsonForObject( - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), ApiConsts.getMyRecentBranches, token: appState.getUser.data!.accessToken, ); @@ -471,7 +485,7 @@ class BranchRepoImp implements BranchRepo { }; GenericRespModel adsGenericModel = await apiClient.getJsonForObject( - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), ApiConsts.getServiceItems, token: appState.getUser.data!.accessToken, queryParameters: serviceId.toString() != "-1" ? queryParameters : null, @@ -491,7 +505,7 @@ class BranchRepoImp implements BranchRepo { postParams.addAll({"Latitude": "$latitude"}); } GenericRespModel adsGenericModel = - await apiClient.getJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.branchesAndServices, token: appState.getUser.data!.accessToken, queryParameters: postParams); + await apiClient.getJsonForObject((json) => GenericRespModel.fromJson(json), ApiConsts.branchesAndServices, token: appState.getUser.data!.accessToken, queryParameters: postParams); return ProviderProfileModel.fromJson(adsGenericModel.data); } @@ -536,7 +550,7 @@ class BranchRepoImp implements BranchRepo { }; GenericRespModel adsGenericModel = await apiClient.getJsonForObject( - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), ApiConsts.getAllNearBranches, token: appState.getUser.data!.accessToken, queryParameters: postParams, @@ -550,7 +564,7 @@ class BranchRepoImp implements BranchRepo { var postParams = {"ServiceProviderBranchID": serviceProviderBranchID.toString()}; GenericRespModel adsGenericModel = await apiClient.getJsonForObject( - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), ApiConsts.getBranchRatings, token: appState.getUser.data!.accessToken, queryParameters: postParams, @@ -564,7 +578,7 @@ class BranchRepoImp implements BranchRepo { final customerID = appState.getUser.data!.userInfo!.customerId; final parameters = {"title": title, "review": review, "ratNo": ratingNo, "serviceProviderBranchID": serviceProviderBranchID, "customerID": "$customerID"}; GenericRespModel adsGenericModel = await apiClient.postJsonForObject( - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), ApiConsts.createBranchRatings, parameters, token: appState.getUser.data!.accessToken, @@ -580,7 +594,7 @@ class BranchRepoImp implements BranchRepo { "customerID": customerID.toString(), }; GenericRespModel adsGenericModel = await apiClient.postJsonForObject( - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), ApiConsts.favouriteServiceProviderCreate, parameters, token: appState.getUser.data!.accessToken, @@ -592,7 +606,7 @@ class BranchRepoImp implements BranchRepo { Future removeProviderFromFavourite({required int providerID}) async { final parameters = {"id": providerID.toString()}; GenericRespModel adsGenericModel = await apiClient.postJsonForObject( - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), ApiConsts.unFavouriteServiceProvider, parameters, token: appState.getUser.data!.accessToken, @@ -607,7 +621,7 @@ class BranchRepoImp implements BranchRepo { var postParams = {"customerID": customerID.toString()}; GenericRespModel adsGenericModel = await apiClient.getJsonForObject( - (json) => GenericRespModel.fromJson(json), + (json) => GenericRespModel.fromJson(json), ApiConsts.favouriteServiceProviderGet, token: appState.getUser.data!.accessToken, queryParameters: postParams, diff --git a/lib/services/common_auth_service.dart b/lib/services/common_auth_service.dart index 17baf4a..14b5ea2 100644 --- a/lib/services/common_auth_service.dart +++ b/lib/services/common_auth_service.dart @@ -6,7 +6,6 @@ import 'package:local_auth/local_auth.dart'; import 'package:local_auth_android/local_auth_android.dart'; import 'package:local_auth_darwin/types/auth_messages_ios.dart'; import 'package:mc_common_app/generated/locale_keys.g.dart'; -import 'package:mc_common_app/main.dart'; import 'package:easy_localization/easy_localization.dart'; abstract class CommonAuthServices { @@ -18,10 +17,11 @@ abstract class CommonAuthServices { class CommonAuthImp implements CommonAuthServices { final LocalAuthentication localAuth = LocalAuthentication(); + @override Future authenticate() async { try { final availableBiometrics = await localAuth.getAvailableBiometrics(); - if (availableBiometrics == null || availableBiometrics.isEmpty) { + if (availableBiometrics.isEmpty) { return false; } diff --git a/lib/view_models/ad_view_model.dart b/lib/view_models/ad_view_model.dart index c7b4568..9bbfe19 100644 --- a/lib/view_models/ad_view_model.dart +++ b/lib/view_models/ad_view_model.dart @@ -201,6 +201,8 @@ class AdVM extends BaseVM { } applyFilterOnExploreAds({required CreatedByRoleEnum createdByRoleFilter}) async { + pageIndexForExploreAds = 1; + hasMoreDataForExploreAds = true; if (exploreAdsFilterOptions.isEmpty) return; int index = exploreAdsFilterOptions.indexWhere((element) => element.id.toCreatedByRoleEnum() == createdByRoleFilter); @@ -219,18 +221,71 @@ class AdVM extends BaseVM { notifyListeners(); } - int currentPageForExploreAds = 1; - int currentPageForMyAds = 1; + int pageIndexForExploreAds = 1; + int pageIndexForMyAds = 1; + bool hasMoreDataForExploreAds = true; + bool hasMoreDataForMyAds = true; bool isLoadingMore = false; - bool hasMoreData = true; - fetchMoreAds() async {} + Future fetchMoreExploreAds() async { + if (isLoadingMore) return; + isLoadingMore = true; + hasMoreDataForExploreAds = true; + notifyListeners(); + try { + final List newAds = await adsRepo.getExploreAdsBasedOnFilters(page: pageIndexForExploreAds); + if (newAds.isEmpty) { + hasMoreDataForExploreAds = false; + } else { + exploreAdsFilteredList.addAll(newAds); + pageIndexForExploreAds++; + if (exploreAdsFilteredList.length < exploreAdsFilteredList.last.totalItemsCount!) { + hasMoreDataForExploreAds = true; + } else { + hasMoreDataForExploreAds = false; + } + } + } catch (error) { + isLoadingMore = false; + Utils.showToast(error.toString()); + } + isLoadingMore = false; + notifyListeners(); + } + + Future fetchMoreMyAds({required AdPostStatus adsStatus}) async { + if (isLoadingMore) return; + hasMoreDataForMyAds = true; + isLoadingMore = true; + notifyListeners(); + try { + final List newAds = await adsRepo.getAllAds(page: pageIndexForMyAds, isMyAds: true, adPostStatus: adsStatus); + if (newAds.isEmpty) { + hasMoreDataForMyAds = false; + } else { + myAdsFilteredList.addAll(newAds); + pageIndexForMyAds++; + if (myAdsFilteredList.length < myAdsFilteredList.last.totalItemsCount!) { + hasMoreDataForMyAds = true; + } else { + hasMoreDataForMyAds = false; + } + } + } catch (error) { + isLoadingMore = false; + Utils.showToast(error.toString()); + } + isLoadingMore = false; + notifyListeners(); + } Future> getAdsByFilter({AdPostStatus? adPostStatus, required bool isMyAds, CreatedByRoleEnum? createdByRoleEnum}) async { return await adsRepo.getAllAds(isMyAds: isMyAds, adPostStatus: adPostStatus, createdByRoleEnum: createdByRoleEnum); } applyFilterOnMyAds({required AdPostStatus adPostStatusEnum}) async { + pageIndexForMyAds = 1; + hasMoreDataForMyAds = true; if (myAdsFilterOptions.isEmpty) return; int index = myAdsFilterOptions.indexWhere((element) => element.id.toAdPostEnum() == adPostStatusEnum); @@ -265,7 +320,7 @@ class AdVM extends BaseVM { if (genericRespModel.messageStatus == null) { Utils.hideLoading(context); - Utils.showToast(genericRespModel.message ?? "Something went wrong!"); + Utils.showToast(genericRespModel.message ?? LocaleKeys.somethingWrong.tr()); return false; } Utils.hideLoading(context); @@ -273,6 +328,8 @@ class AdVM extends BaseVM { } Future getExploreAds() async { + pageIndexForExploreAds = 1; + hasMoreDataForExploreAds = true; setState(ViewState.busy); exploreAds = await adsRepo.getAllAds(isMyAds: false); exploreAdsFilteredList = exploreAds; @@ -281,6 +338,8 @@ class AdVM extends BaseVM { } Future getMyAds() async { + pageIndexForMyAds = 1; + hasMoreDataForMyAds = true; setState(ViewState.busy); myAds = await adsRepo.getAllAds(isMyAds: true); myAdsFilteredList = myAds; @@ -1581,6 +1640,11 @@ class AdVM extends BaseVM { Future populateDataForAdFilter() async { setState(ViewState.busy); + pageIndexForExploreAds = 1; + pageIndexForMyAds = 1; + hasMoreDataForExploreAds = true; + hasMoreDataForMyAds = true; + isLoadingMore = false; if (vehicleConditionsEnum.isEmpty) { vehicleConditionsEnum = await commonRepo.getEnumTypeValues(enumTypeID: AppEnums.conditionEnumId); @@ -1846,13 +1910,13 @@ class AdVM extends BaseVM { } exploreAdsFilteredList = await adsRepo.getExploreAdsBasedOnFilters( - cityIdsList: cityIdsList, - createdByRolesIdsList: adOwnerIdsList, - vehicleBrandIdsList: brandsIdsList, - vehicleModelYearIdsList: vehicleYearIdsList, - vehicleAdConditionIdsList: conditionsIdsList, - vehicleAdCreatedDateList: createdDatesList, - ); + cityIdsList: cityIdsList, + createdByRolesIdsList: adOwnerIdsList, + vehicleBrandIdsList: brandsIdsList, + vehicleModelYearIdsList: vehicleYearIdsList, + vehicleAdConditionIdsList: conditionsIdsList, + vehicleAdCreatedDateList: createdDatesList, + page: pageIndexForExploreAds); setState(ViewState.idle); } @@ -1944,7 +2008,9 @@ class AdVM extends BaseVM { updateSelectionVehicleConditionId(SelectionModel(selectedId: previousAdDetails!.vehicle!.condition!.id!, selectedOption: previousAdDetails!.vehicle!.condition!.label ?? "")); updateSelectionVehicleCategoryId(SelectionModel(selectedId: previousAdDetails!.vehicle!.category!.id!, selectedOption: previousAdDetails!.vehicle!.category!.label ?? "")); updateSelectionVehicleMileageId(SelectionModel( - selectedId: previousAdDetails!.vehicle!.mileage!.id!, selectedOption: "${previousAdDetails!.vehicle!.mileage!.mileageStart} - ${previousAdDetails!.vehicle!.mileage!.mileageEnd}")); + selectedId: previousAdDetails!.vehicle!.mileage!.id!, + selectedOption: "${previousAdDetails!.vehicle!.mileage!.mileageStart} - ${previousAdDetails!.vehicle!.mileage!.mileageEnd}", + )); updateSelectionVehicleTransmissionId(SelectionModel(selectedId: previousAdDetails!.vehicle!.transmission!.id!, selectedOption: previousAdDetails!.vehicle!.transmission!.label ?? "")); updateSelectionVehicleSellerTypeId(SelectionModel(selectedId: previousAdDetails!.vehicle!.sellertype!.id!, selectedOption: previousAdDetails!.vehicle!.sellertype!.label ?? "")); int indexCountry = vehicleCountries.indexWhere((element) => element.id == previousAdDetails!.vehicle!.countryID); diff --git a/lib/view_models/requests_view_model.dart b/lib/view_models/requests_view_model.dart index 9a1e7f1..600465f 100644 --- a/lib/view_models/requests_view_model.dart +++ b/lib/view_models/requests_view_model.dart @@ -92,6 +92,38 @@ class RequestsVM extends BaseVM { setState(ViewState.idle); } + int pageIndexForRequests = 1; + bool hasMoreDataRequests = true; + bool isLoadingMore = false; + + // Future fetchMoreRequests() async { + // log("isLoadingMore: $isLoadingMore"); + // log("hasMoreDataRequests: $hasMoreDataRequests"); + // if (isLoadingMore) return; + // isLoadingMore = true; + // hasMoreDataRequests = true; + // notifyListeners(); + // try { + // final List newRequests = await requestRepo.getRequestsBasedOnFilters(requestedDate: requestedDateFilter, requestTypeId: requestTypeId); + // if (newAds.isEmpty) { + // hasMoreDataForExploreAds = false; + // } else { + // exploreAdsFilteredList.addAll(newAds); + // pageIndexForExploreAds++; + // if (exploreAdsFilteredList.length < exploreAdsFilteredList.last.totalItemsCount!) { + // hasMoreDataForExploreAds = true; + // } else { + // hasMoreDataForExploreAds = false; + // } + // } + // } catch (error) { + // isLoadingMore = false; + // Utils.showToast(error.toString()); + // } + // isLoadingMore = false; + // notifyListeners(); + // } + Future getRequestsBasedOnFilters() async { myFilteredRequests.clear(); setState(ViewState.busy); @@ -127,7 +159,7 @@ class RequestsVM extends BaseVM { ); setState(ViewState.idle); return true; - } catch (e, s) { + } catch (e) { logger.i("e: ${e.toString()}"); setState(ViewState.idle); return false; @@ -1061,7 +1093,7 @@ class RequestsVM extends BaseVM { pop(context); } } - } catch (e, s) { + } catch (e) { Utils.hideLoading(context); log(e.toString()); Utils.showToast(e.toString()); diff --git a/lib/view_models/service_view_model.dart b/lib/view_models/service_view_model.dart index 11a5015..99b7d48 100644 --- a/lib/view_models/service_view_model.dart +++ b/lib/view_models/service_view_model.dart @@ -272,11 +272,7 @@ class ServiceVM extends BaseVM { context, allowMultiple: false, ); - if (files != null && files.any((element) => - element.path - .split('.') - .last - .toLowerCase() != 'pdf')) { + if (files != null && files.any((element) => element.path.split('.').last.toLowerCase() != 'pdf')) { Utils.showToast("Only PDF Files are allowed"); return; } @@ -290,8 +286,8 @@ class ServiceVM extends BaseVM { documentID == 1 ? commerceCertificates.addAll(imageModels) : documentID == 2 - ? commercialCertificates.addAll(imageModels) - : vatCertificates.addAll(imageModels); + ? commercialCertificates.addAll(imageModels) + : vatCertificates.addAll(imageModels); document!.data![index].document = Utils.convertFileToBase64(files.first); document!.data![index].fileExt = Utils.checkFileExt(files.first.path); document!.data![index].documentUrl = files.first.path; @@ -467,10 +463,10 @@ class ServiceVM extends BaseVM { DropValue( element.id ?? 0, ((element.categoryName!.isEmpty - ? "N/A" - : countryCode == "SA" - ? element.categoryNameN - : element.categoryName) ?? + ? "N/A" + : countryCode == "SA" + ? element.categoryNameN + : element.categoryName) ?? "N/A"), "", ), @@ -567,6 +563,25 @@ class ServiceVM extends BaseVM { } } + Future updateCategoryStatus({required BuildContext context, required ServiceStatusEnum serviceStatusEnum, required int branchId, required int categoryId}) async { + try { + Utils.showLoading(context); + GenericRespModel genericRespModel = await branchRepo.updateCategoryStatus( + branchId: branchId, + categoryId: categoryId, + serviceStatusEnum: serviceStatusEnum, + ); + Utils.hideLoading(context); + Utils.showToast(genericRespModel.message ?? ""); + return genericRespModel.messageStatus == 1; + } catch (e) { + Utils.hideLoading(context); + log(e.toString()); + Utils.showToast(e.toString()); + return false; + } + } + Future buildDealNotCompletedBottomSheetOptions({required BuildContext mainContext, required List appointments, required String branchName}) async { return actionConfirmationBottomSheet( isOnlyOneButton: true, @@ -751,9 +766,7 @@ class ServiceVM extends BaseVM { File file = File(imageModel.filePath!); List imageBytes = await file.readAsBytes(); String image = base64Encode(imageBytes); - String fileName = file.path - .split('/') - .last; + String fileName = file.path.split('/').last; branchPostingImages = BranchPostingImages( imageName: fileName, imageStr: image, diff --git a/lib/views/advertisement/components/ads_list_widget.dart b/lib/views/advertisement/components/ads_list_widget.dart index fff78fb..fb2f995 100644 --- a/lib/views/advertisement/components/ads_list_widget.dart +++ b/lib/views/advertisement/components/ads_list_widget.dart @@ -21,12 +21,14 @@ class AdsListWidget extends StatelessWidget { final ScrollPhysics? scrollPhysics; final bool isAdsFragment; final bool shouldShowAdStatus; + final bool hasMoreData; final Function()? onFetchMoreAds; const AdsListWidget({ super.key, required this.adsList, required this.shouldShowAdStatus, + required this.hasMoreData, this.onFetchMoreAds, this.scrollPhysics, this.isAdsFragment = false, @@ -44,7 +46,8 @@ class AdsListWidget extends StatelessWidget { } return NotificationListener( onNotification: (ScrollNotification scrollInfo) { - if (scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent && context.read().hasMoreData) { + log("hasMoreData: $hasMoreData"); + if (scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent && hasMoreData) { if (onFetchMoreAds != null) { onFetchMoreAds!(); } diff --git a/lib/views/chat/widgets/chat_message_widget.dart b/lib/views/chat/widgets/chat_message_widget.dart index 05ef366..9f169a0 100644 --- a/lib/views/chat/widgets/chat_message_widget.dart +++ b/lib/views/chat/widgets/chat_message_widget.dart @@ -771,7 +771,7 @@ class _ChatMessageCustomWidgetState extends State { ] else if (AppState().currentAppType == AppType.provider && chatMessageTypeEnum == ChatMessageTypeEnum.offer && widget.chatMessageModel.reqOffer!.requestOfferStatusEnum == RequestOfferStatusEnum.offer && - (widget.chatMessageModel.isMyMessage == true)) ...[ + (widget.chatMessageModel.isMyMessage == true && widget.chatMessageModel.reqOffer != null && widget.chatMessageModel.reqOffer!.id != null)) ...[ MyAssets.icEdit.buildSvg(color: MyColors.white, height: 15).onPress(() => onOfferEditIconPressed()), ], Expanded( diff --git a/lib/views/common_fragments/ads_fragment.dart b/lib/views/common_fragments/ads_fragment.dart index b0e29d4..9a30037 100644 --- a/lib/views/common_fragments/ads_fragment.dart +++ b/lib/views/common_fragments/ads_fragment.dart @@ -79,7 +79,9 @@ class AdsFragment extends StatelessWidget { title: LocaleKeys.exploreAds.tr(), txtColor: adVM.isExploreAdsTapped ? MyColors.white : MyColors.darkTextColor, onPressed: () { - print("accessToken: ${AppState().getUser.data!.accessToken}"); + log("accessToken: ${AppState().getUser.data!.accessToken}"); + log("AppState().getUser.data!.userInfo!.userId;: ${AppState().getUser.data!.userInfo!.userId}"); + if (adVM.myAds.isEmpty) { adVM.getMyAds(); } @@ -107,7 +109,9 @@ class AdsFragment extends StatelessWidget { 16.height, FiltersList( filterList: adVM.exploreAdsFilterOptions, - onFilterTapped: (index, selectedFilterId) => adVM.applyFilterOnExploreAds(createdByRoleFilter: selectedFilterId.toCreatedByRoleEnum()), + onFilterTapped: (index, selectedFilterId) { + adVM.applyFilterOnExploreAds(createdByRoleFilter: selectedFilterId.toCreatedByRoleEnum()); + }, needLeftPadding: false, ).paddingOnly(left: 21), ] @@ -115,7 +119,9 @@ class AdsFragment extends StatelessWidget { 16.height, FiltersList( filterList: adVM.myAdsFilterOptions, - onFilterTapped: (index, selectedFilterId) => adVM.applyFilterOnMyAds(adPostStatusEnum: selectedFilterId.toAdPostEnum()), + onFilterTapped: (index, selectedFilterId) { + adVM.applyFilterOnMyAds(adPostStatusEnum: selectedFilterId.toAdPostEnum()); + }, needLeftPadding: false, ).paddingOnly(left: 21), ], @@ -141,12 +147,26 @@ class AdsFragment extends StatelessWidget { isAdsFragment: true, shouldShowAdStatus: !adVM.isExploreAdsTapped, adsList: getAdsList(adVM), - onFetchMoreAds: () { - log("fetch more ads"); + hasMoreData: adVM.isExploreAdsTapped ? adVM.hasMoreDataForExploreAds : adVM.hasMoreDataForMyAds, + onFetchMoreAds: () async { + AdPostStatus adPostStatusEnum = adVM.myAdsFilterOptions.firstWhere((element) => element.isSelected).id.toAdPostEnum(); + + if (adVM.isExploreAdsTapped) { + await adVM.fetchMoreExploreAds(); + } else { + await adVM.fetchMoreMyAds(adsStatus: adPostStatusEnum); + } }, ), ), - ) + ), + if (adVM.isLoadingMore) ...[ + const Center( + child: Padding( + padding: EdgeInsets.all(8.0), + child: CircularProgressIndicator(), + )) + ] ], ), ), diff --git a/lib/views/requests/review_request_offer.dart b/lib/views/requests/review_request_offer.dart index 4fef205..f7c689e 100644 --- a/lib/views/requests/review_request_offer.dart +++ b/lib/views/requests/review_request_offer.dart @@ -162,6 +162,7 @@ class _ReviewRequestOfferState extends State { 16.height, SingleDetailWidget(text: requestVM.acceptedRequestOfferProviderName ?? "", type: LocaleKeys.providerName.tr()), 16.height, + SingleDetailWidget(text: requestVM.currentSelectedRequest!.description, type: LocaleKeys.description.tr()), ], ), ), @@ -186,12 +187,14 @@ class _ReviewRequestOfferState extends State { 16.height, SingleDetailWidget(text: requestCreatedOn, type: LocaleKeys.requestCreatedOn.tr()), ], + + 16.height, + SingleDetailWidget(text: LocaleKeys.online.tr(), type: LocaleKeys.paymentType.tr()), ], ), ), ], ), - SingleDetailWidget(text: requestVM.currentSelectedRequest!.description, type: LocaleKeys.description.tr()), ], ); } diff --git a/lib/views/splash/splash_page.dart b/lib/views/splash/splash_page.dart index e97cf7f..9c84fd5 100644 --- a/lib/views/splash/splash_page.dart +++ b/lib/views/splash/splash_page.dart @@ -1,15 +1,51 @@ +import 'package:flutter/material.dart'; import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/config/routes.dart'; import 'package:mc_common_app/utils/navigator.dart'; import 'package:mc_common_app/utils/utils.dart'; -import 'package:flutter/material.dart'; +import 'package:mc_common_app/view_models/ad_view_model.dart'; +import 'package:mc_common_app/view_models/chat_view_model.dart'; +import 'package:provider/provider.dart'; -class SplashPage extends StatelessWidget { - const SplashPage({Key? key}) : super(key: key); +class SplashPage extends StatefulWidget { + const SplashPage({super.key}); @override - Widget build(BuildContext context) { + SplashPageState createState() => SplashPageState(); +} + +class SplashPageState extends State with WidgetsBindingObserver { + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addObserver(this); performTimer(context); + } + + @override + void dispose() { + WidgetsBinding.instance.removeObserver(this); + super.dispose(); + } + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + super.didChangeAppLifecycleState(state); + if (state == AppLifecycleState.resumed) { + final chatVM = context.read(); + chatVM.buildHubConnection(context); + } + } + + // Timer function to navigate after a delay + performTimer(BuildContext context) { + Utils.delay(3).whenComplete(() { + navigateReplaceWithName(context, AppRoutes.registerSelection); + }); + } + + @override + Widget build(BuildContext context) { return Scaffold( body: Container( width: double.infinity, @@ -17,37 +53,36 @@ class SplashPage extends StatelessWidget { color: Colors.black, child: Column( children: [ - Utils. mExp(1), + // Spacer widget for flexible spacing + Utils.mExp(1), + // Logo Image Expanded( child: Image.asset( MyAssets.icLogoWhitePng, ), ), + // Car Image (flex value adjusted to make it more responsive) Expanded( flex: 10, child: Image.asset( MyAssets.bnCar, fit: BoxFit.cover, width: double.infinity, - height: 00, + height: double.infinity, // Use full height in this expanded section ), ), + // Engine Image Expanded( flex: 3, child: Image.asset( MyAssets.icEnginePng, ), ), + // Spacer widget again for spacing Utils.mExp(1), ], ), ), ); } - - performTimer(BuildContext context) { - Utils.delay(3).whenComplete(() { - navigateReplaceWithName(context, AppRoutes.registerSelection); - }); - } }