diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart index 59b5dfff..3bfbf6cb 100644 --- a/lib/core/api/api_client.dart +++ b/lib/core/api/api_client.dart @@ -211,10 +211,8 @@ class ApiClientImp implements ApiClient { } // body['TokenID'] = "@dm!n"; - // body['PatientID'] = 1018977; - // body['PatientID'] = 89754444; + // body['PatientID'] = 2122445; // body['PatientID'] = 53320; - // body['PatientID'] = 4769748; // body['PatientTypeID'] = 1; // body['PatientOutSA'] = 0; // body['SessionID'] = "45786230487560q"; diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index c4feec0b..2c64b13e 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -5,7 +5,7 @@ import 'package:hmg_patient_app_new/core/enums.dart'; class ApiConsts { static const maxSmallScreen = 660; - static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.uat; + static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod; // static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT @@ -301,7 +301,7 @@ class ApiConsts { static String googleCloudStorageENTranslationFileBaseURL = "https://storage.googleapis.com/hmg-patientapp-translations"; // ************ static values for Api **************** - static final double appVersionID = 21.5; + static final double appVersionID = 21.6; // static final double appVersionID = 50.7; static final int appChannelId = 3; diff --git a/lib/core/dependencies.dart b/lib/core/dependencies.dart index c4f7402c..ecfe1324 100644 --- a/lib/core/dependencies.dart +++ b/lib/core/dependencies.dart @@ -84,6 +84,7 @@ import 'package:hmg_patient_app_new/services/logger_service.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/services/notification_service.dart'; import 'package:hmg_patient_app_new/services/permission_service.dart'; +import 'package:hmg_patient_app_new/core/services/turnstile_service.dart'; import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_calendar_model.dart'; import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart'; import 'package:local_auth/local_auth.dart'; @@ -155,6 +156,7 @@ class AppDependencies { ); getIt.registerLazySingleton(() => PermissionService()); + getIt.registerLazySingleton(() => TurnstileService(getIt())); // Repositories getIt.registerLazySingleton(() => CommonRepoImp(loggerService: getIt())); @@ -255,7 +257,8 @@ class AppDependencies { dialogService: getIt(), appState: getIt(), errorHandlerService: getIt(), - localAuthService: getIt()), + localAuthService: getIt(), + turnstileService: getIt()), ); getIt.registerLazySingleton(() => ProfileSettingsViewModel( diff --git a/lib/core/services/turnstile_service.dart b/lib/core/services/turnstile_service.dart new file mode 100644 index 00000000..f6665190 --- /dev/null +++ b/lib/core/services/turnstile_service.dart @@ -0,0 +1,56 @@ +import 'dart:async'; +import 'package:cloudflare_turnstile/cloudflare_turnstile.dart'; +import 'package:flutter/foundation.dart'; +import 'package:hmg_patient_app_new/services/logger_service.dart'; + +/// Service to handle Cloudflare Turnstile captcha verification +class TurnstileService { + final LoggerService _loggerService; + + // Replace with your Cloudflare Turnstile site key + static const String _siteKey = '0x4AAAAAAAkmbvEy63pcxBBt'; + + TurnstileService(this._loggerService); + + /// Get Turnstile token using invisible mode + /// Returns the token on success, null on failure + Future getTurnstileToken() async { + // Initialize an instance of invisible Cloudflare Turnstile with your site key + final turnstile = CloudflareTurnstile.invisible( + siteKey: _siteKey, + ); + + try { + // Get the Turnstile token + final token = await turnstile.getToken(); + _loggerService.logInfo('Turnstile token retrieved successfully'); + return token; // Return the token upon success + } on TurnstileException catch (e) { + // Handle Turnstile failure + _loggerService.logError('Turnstile challenge failed: ${e.message}'); + if (kDebugMode) { + print('Challenge failed: ${e.message}'); + } + } catch (e) { + // Handle any other exceptions + _loggerService.logError('Unexpected error during Turnstile challenge: $e'); + if (kDebugMode) { + print('Unexpected error: $e'); + } + } finally { + // Ensure the Turnstile instance is properly disposed of + turnstile.dispose(); + } + + // Return null if the token couldn't be generated + return null; + } + + /// Get the site key for Turnstile widget + String getSiteKey() => _siteKey; + + /// Validate if Turnstile is properly configured + bool isConfigured() { + return _siteKey.isNotEmpty && _siteKey != '0x4AAAAAAAkmbvEy63pcxBBt'; + } +} diff --git a/lib/features/authentication/authentication_repo.dart b/lib/features/authentication/authentication_repo.dart index c355819e..291e4a9b 100644 --- a/lib/features/authentication/authentication_repo.dart +++ b/lib/features/authentication/authentication_repo.dart @@ -147,6 +147,9 @@ class AuthenticationRepoImp implements AuthenticationRepo { bool isRegister = false, bool isFormFamilyFile = false, }) async { + + + int isOutKsa = (sendActivationCodeReq.zipCode == '966' || sendActivationCodeReq.zipCode == '+966') ? 0 : 1; sendActivationCodeReq.patientOutSA = isOutKsa; sendActivationCodeReq.isDentalAllowedBackend = false; diff --git a/lib/features/authentication/authentication_view_model.dart b/lib/features/authentication/authentication_view_model.dart index b51b36e4..6ec5d2bf 100644 --- a/lib/features/authentication/authentication_view_model.dart +++ b/lib/features/authentication/authentication_view_model.dart @@ -42,6 +42,7 @@ import 'package:hmg_patient_app_new/services/dialog_service.dart'; import 'package:hmg_patient_app_new/services/error_handler_service.dart'; import 'package:hmg_patient_app_new/services/localauth_service.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart'; +import 'package:hmg_patient_app_new/core/services/turnstile_service.dart'; import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_calendar_model.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; @@ -60,6 +61,7 @@ class AuthenticationViewModel extends ChangeNotifier { final DialogService _dialogService; final NavigationService _navigationService; final LocalAuthService _localAuthService; + final TurnstileService _turnstileService; AuthenticationViewModel({ required AppState appState, @@ -69,12 +71,14 @@ class AuthenticationViewModel extends ChangeNotifier { required NavigationService navigationService, required CacheService cacheService, required LocalAuthService localAuthService, + required TurnstileService turnstileService, }) : _navigationService = navigationService, _dialogService = dialogService, _errorHandlerService = errorHandlerService, _appState = appState, _authenticationRepo = authenticationRepo, - _localAuthService = localAuthService {} + _localAuthService = localAuthService, + _turnstileService = turnstileService {} final TextEditingController nationalIdController = TextEditingController(), phoneNumberController = TextEditingController(), dobController = TextEditingController(), @@ -974,6 +978,15 @@ class AuthenticationViewModel extends ChangeNotifier { // TODO: GET APP SMS SIGNATURE HERE request.sMSSignature = await getSignature(); + + // Get Turnstile token if configured + final turnstileToken = await _turnstileService.getTurnstileToken(); + if (turnstileToken != null) { + request.turnstileToken = turnstileToken; + } else { + request.turnstileToken = ""; + } + if (isNeedLoading) { LoaderBottomSheet.showLoader(); } diff --git a/lib/features/authentication/models/request_models/send_activation_request_model.dart b/lib/features/authentication/models/request_models/send_activation_request_model.dart index 2908f7ec..6e2ddb93 100644 --- a/lib/features/authentication/models/request_models/send_activation_request_model.dart +++ b/lib/features/authentication/models/request_models/send_activation_request_model.dart @@ -30,6 +30,7 @@ class SendActivationRequest { int? status; int? familyRegionID; bool? isPatientExcluded; + String? turnstileToken; SendActivationRequest( {this.patientMobileNumber, @@ -62,7 +63,8 @@ class SendActivationRequest { this.responseID, this.status, this.familyRegionID, - this.isPatientExcluded}); + this.isPatientExcluded, + this.turnstileToken}); SendActivationRequest.fromJson(Map json) { patientMobileNumber = json['PatientMobileNumber']; @@ -96,6 +98,7 @@ class SendActivationRequest { status = json['Status']; familyRegionID = json['FamilyRegionID']; isPatientExcluded = json['IsPatientExcluded']; + turnstileToken = json['TurnstileToken']; } Map toJson() { @@ -135,6 +138,9 @@ class SendActivationRequest { data['Status'] = status; data['FamilyRegionID'] = familyRegionID; data['IsPatientExcluded'] = isPatientExcluded; + if (turnstileToken != null) { + data['TurnstileToken'] = turnstileToken; + } return data; } } diff --git a/lib/features/contact_us/contact_us_view_model.dart b/lib/features/contact_us/contact_us_view_model.dart index 648fab6b..323eb782 100644 --- a/lib/features/contact_us/contact_us_view_model.dart +++ b/lib/features/contact_us/contact_us_view_model.dart @@ -12,6 +12,7 @@ import 'package:hmg_patient_app_new/features/contact_us/models/resp_models/get_p import 'package:hmg_patient_app_new/features/contact_us/models/resp_models/get_status_coc_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart'; import 'package:hmg_patient_app_new/services/error_handler_service.dart'; +import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; import 'package:permission_handler/permission_handler.dart'; class ContactUsViewModel extends ChangeNotifier { @@ -308,7 +309,15 @@ class ContactUsViewModel extends ChangeNotifier { final result = await contactUsRepo.insertCOCItem(requestInsertCOCItem: requestInsertCOCItem, patientSelectedAppointment: patientFeedbackSelectedAppointment); result.fold( - (failure) async => await errorHandlerService.handleError(failure: failure), + (failure) async { + LoaderBottomSheet.hideLoader(); + await errorHandlerService.handleError(failure: failure); + }, + // (failure) async { + // if(onError != null) { + // onError(failure.toString()); + // } + // }, (apiResponse) { if (apiResponse.messageStatus == 2) { // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); diff --git a/lib/features/my_appointments/my_appointments_repo.dart b/lib/features/my_appointments/my_appointments_repo.dart index d7a025fc..87484e06 100644 --- a/lib/features/my_appointments/my_appointments_repo.dart +++ b/lib/features/my_appointments/my_appointments_repo.dart @@ -53,7 +53,7 @@ abstract class MyAppointmentsRepo { Future>>> getPatientAppointmentsForTimeLine(); - Future>>> getPatientDoctorsList(); + Future>>> getPatientDoctorsList({bool isTop8 = false}); Future>>> getFavouriteDoctorsList(); @@ -514,11 +514,12 @@ class MyAppointmentsRepoImp implements MyAppointmentsRepo { } @override - Future>>> getPatientDoctorsList() async { + Future>>> getPatientDoctorsList({bool isTop8 = false}) async { Map mapDevice = { "Top": 0, "beforeDays": 0, "exludType": 4, + "IsTop8": isTop8, }; try { diff --git a/lib/features/my_appointments/my_appointments_view_model.dart b/lib/features/my_appointments/my_appointments_view_model.dart index f28eb49b..b480a354 100644 --- a/lib/features/my_appointments/my_appointments_view_model.dart +++ b/lib/features/my_appointments/my_appointments_view_model.dart @@ -211,6 +211,11 @@ class MyAppointmentsViewModel extends ChangeNotifier { notifyListeners(); } + setIsMyDoctorsDataToBeLoaded(bool val) { + isMyDoctorsDataToBeLoaded = val; + notifyListeners(); + } + setIsAppointmentDataToBeLoaded(bool val) { if (val) { isMyAppointmentsLoading = true; @@ -675,13 +680,13 @@ class MyAppointmentsViewModel extends ChangeNotifier { ); } - Future getPatientMyDoctors({Function(dynamic)? onSuccess, Function(String)? onError}) async { + Future getPatientMyDoctors({bool isTop8 = false, Function(dynamic)? onSuccess, Function(String)? onError}) async { if (!isMyDoctorsDataToBeLoaded) return; isPatientMyDoctorsLoading = true; patientMyDoctorsList.clear(); notifyListeners(); - final result = await myAppointmentsRepo.getPatientDoctorsList(); + final result = await myAppointmentsRepo.getPatientDoctorsList(isTop8: isTop8); result.fold( // (failure) async => await errorHandlerService.handleError(failure: failure), diff --git a/lib/features/prescriptions/prescriptions_view_model.dart b/lib/features/prescriptions/prescriptions_view_model.dart index 5354daad..cdd85b9f 100644 --- a/lib/features/prescriptions/prescriptions_view_model.dart +++ b/lib/features/prescriptions/prescriptions_view_model.dart @@ -235,11 +235,16 @@ class PrescriptionsViewModel extends ChangeNotifier { result.fold( (failure) async { - onError!(failure.message); + if (onError != null) { + onError(failure.message); + } }, (apiResponse) { if (apiResponse.messageStatus == 2) { - onError!(apiResponse.errorMessage!); + if (onError != null) { + onError(apiResponse.errorMessage ?? "An error occurred while fetching the prescription instructions PDF."); + } + // onError!(apiResponse.errorMessage!); // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); } else if (apiResponse.messageStatus == 1) { prescriptionInstructionsPDFLink = apiResponse.data; diff --git a/lib/presentation/appointments/my_appointments_page.dart b/lib/presentation/appointments/my_appointments_page.dart index 1f54e3d1..3512472d 100644 --- a/lib/presentation/appointments/my_appointments_page.dart +++ b/lib/presentation/appointments/my_appointments_page.dart @@ -43,8 +43,10 @@ class _MyAppointmentsPageState extends State { @override void initState() { scheduleMicrotask(() { - myAppointmentsViewModel.initAppointmentsViewModel(); - myAppointmentsViewModel.getPatientAppointments(true, false); + if (!myAppointmentsViewModel.isMyAppointmentsLoading) { + myAppointmentsViewModel.initAppointmentsViewModel(); + myAppointmentsViewModel.getPatientAppointments(true, false); + } }); super.initState(); } diff --git a/lib/presentation/appointments/my_doctors_page.dart b/lib/presentation/appointments/my_doctors_page.dart index 9ccf471e..d05270e5 100644 --- a/lib/presentation/appointments/my_doctors_page.dart +++ b/lib/presentation/appointments/my_doctors_page.dart @@ -39,7 +39,7 @@ class _MyDoctorsPageState extends State { @override void initState() { scheduleMicrotask(() { - myAppointmentsViewModel.getPatientMyDoctors(); + myAppointmentsViewModel.getPatientMyDoctors(isTop8: false); }); super.initState(); } diff --git a/lib/presentation/appointments/widgets/appointment_doctor_card.dart b/lib/presentation/appointments/widgets/appointment_doctor_card.dart index f4d20944..f0dd2f5d 100644 --- a/lib/presentation/appointments/widgets/appointment_doctor_card.dart +++ b/lib/presentation/appointments/widgets/appointment_doctor_card.dart @@ -101,7 +101,7 @@ class AppointmentDoctorCard extends StatelessWidget { Row( children: [ Expanded( - child: patientAppointmentHistoryResponseModel.doctorNameObj!.toText16( + child: (patientAppointmentHistoryResponseModel.doctorNameObj ?? "").toText16( isBold: true, isEnglishOnly: !Utils.isArabicText(patientAppointmentHistoryResponseModel.doctorNameObj ?? ""), textOverflow: TextOverflow.ellipsis, diff --git a/lib/presentation/book_appointment/book_appointment_page.dart b/lib/presentation/book_appointment/book_appointment_page.dart index d542bb7f..55769fbe 100644 --- a/lib/presentation/book_appointment/book_appointment_page.dart +++ b/lib/presentation/book_appointment/book_appointment_page.dart @@ -66,14 +66,14 @@ class _BookAppointmentPageState extends State { // bookAppointmentsViewModel.getLocation(); immediateLiveCareViewModel.initImmediateLiveCare(); if (appState.isAuthenticated) { - getIt.get().getPatientMyDoctors(); + getIt.get().getPatientMyDoctors(isTop8: true); getIt.get().getPatientFavouriteDoctors(); } }); WidgetsBinding.instance.addPostFrameCallback((_) { if (bookAppointmentsViewModel.selectedTabIndex == 1) { if (appState.isAuthenticated) { - getIt.get().getPatientMyDoctors(); + getIt.get().getPatientMyDoctors(isTop8: true); getIt.get().getPatientFavouriteDoctors(); showUnKnownClinicBottomSheet(); } @@ -166,6 +166,7 @@ class _BookAppointmentPageState extends State { Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 14.h), ], ).paddingSymmetrical(24.h, 0.h).onPress(() { + myAppointmentsVM.setIsMyDoctorsDataToBeLoaded(true); Navigator.of(context).push( CustomPageRoute( page: MyDoctorsPage(), diff --git a/lib/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart b/lib/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart index 972606a9..ef21ccbd 100644 --- a/lib/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart +++ b/lib/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart @@ -544,12 +544,12 @@ class _ImmediateLiveCarePaymentPageState extends State createState() => _LabOrdersPageState(); } class _LabOrdersPageState extends State { late LabViewModel labProvider; late DateRangeSelectorRangeViewModel rangeViewModel; late AppState _appState; List?> labSuggestions = []; int? expandedIndex; String? selectedFilterText = ''; int activeIndex = 0; @override void initState() { scheduleMicrotask(() { labProvider.initLabProvider(); }); super.initState(); } @override Widget build(BuildContext context) { labProvider = Provider.of(context, listen: false); rangeViewModel = Provider.of(context); _appState = getIt(); return CollapsingListView( title: LocaleKeys.labResults.tr(context: context), search: () async { if (labProvider.isLabOrdersLoading) { return; } else { String? value = await Navigator.of(context).push( CustomPageRoute( page: SearchLabResultsContent(labSuggestionsList: labProvider.labSuggestions), fullScreenDialog: true, direction: AxisDirection.down, ), ); if (value != null) { selectedFilterText = value; labProvider.filterLabReports(value); } } }, child: Consumer( builder: (context, labViewModel, child) { return SingleChildScrollView( physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.all(24.h), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: CustomTabBar( activeTextColor: Color(0xffED1C2B), activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1), tabs: [ CustomTabBarModel(null, LocaleKeys.byVisit.tr()), CustomTabBarModel(null, LocaleKeys.byTest.tr()), // CustomTabBarModel(null, "Completed".needTranslation), ], onTabChange: (index) { activeIndex = index; setState(() {}); }, ), ), ], ), if (activeIndex == 0) Padding( padding: EdgeInsets.symmetric(vertical: 10.h), child: Row( children: [ // CustomButton( // text: LocaleKeys.byClinic.tr(context: context), // onPressed: () { // labViewModel.setIsSortByClinic(true); // }, // backgroundColor: labViewModel.isSortByClinic ? AppColors.bgRedLightColor : AppColors.whiteColor, // borderColor: labViewModel.isSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2), // textColor: labViewModel.isSortByClinic ? AppColors.primaryRedColor : AppColors.blackColor, // fontSize: 12.f, // isBold: true, // borderRadius: 10, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // height: 40.h, // ), // SizedBox(width: 8.h), // CustomButton( // text: LocaleKeys.byHospital.tr(context: context), // onPressed: () { // labViewModel.setIsSortByClinic(false); // }, // backgroundColor: labViewModel.isSortByClinic ? AppColors.whiteColor : AppColors.bgRedLightColor, // borderColor: labViewModel.isSortByClinic ? AppColors.textColor.withValues(alpha: 0.2) : AppColors.primaryRedColor, // textColor: labViewModel.isSortByClinic ? AppColors.blackColor : AppColors.primaryRedColor, // fontSize: 12, // isBold: true, // borderRadius: 10, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // height: 40.h, // ), ], ), ), SizedBox(height: 8.h), selectedFilterText!.isNotEmpty ? Column( children: [ AppCustomChipWidget( labelText: selectedFilterText!, backgroundColor: AppColors.alertColor, textColor: AppColors.whiteColor, deleteIcon: AppAssets.close_bottom_sheet_icon, deleteIconColor: AppColors.whiteColor, deleteIconHasColor: true, isEnglishOnly: true, onDeleteTap: () { selectedFilterText = ""; labProvider.filterLabReports(""); }, ), SizedBox(height: 8.h), ], ) : SizedBox(), activeIndex == 0 ? // By Visit - show grouped view when available labViewModel.isLabOrdersLoading ? ListView.builder( shrinkWrap: true, physics: AlwaysScrollableScrollPhysics(), padding: EdgeInsets.zero, itemCount: 5, itemBuilder: (context, index) => LabResultItemView( onTap: () {}, labOrder: null, index: index, isLoading: true, ), ) : (labViewModel.patientLabOrders.isNotEmpty ? ListView.builder( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, itemCount: labViewModel.filteredLabOrders.length, itemBuilder: (context, index) { final group = labViewModel.filteredLabOrders[index]; final isExpanded = expandedIndex == index; return AnimationConfiguration.staggeredList( position: index, duration: const Duration(milliseconds: 500), child: SlideAnimation( verticalOffset: 100.0, child: FadeInAnimation( child: AnimatedContainer( duration: Duration(milliseconds: 300), curve: Curves.easeInOut, margin: EdgeInsets.symmetric(vertical: 8.h), decoration: RoundedRectangleBorder() .toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: true), child: InkWell( onTap: () { setState(() { expandedIndex = isExpanded ? null : index; }); }, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: EdgeInsets.all(16.h), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Wrap( direction: Axis.horizontal, spacing: 4.h, runSpacing: 4.h, children: [ AppCustomChipWidget( richText: Row( children: [ "${group.testDetails!.length} ".toText10( color: AppColors.successColor, isEnglishOnly: true, weight: FontWeight.w600), LocaleKeys.tests .tr(context: context) .toText10(color: AppColors.successColor, weight: FontWeight.w600), ], ), labelText: "${group.testDetails!.length} ${LocaleKeys.tests.tr(context: context)}", backgroundColor: AppColors.successColor.withOpacity(0.1), textColor: AppColors.successColor, ), AppCustomChipWidget( labelText: "${_appState.isArabic() ? group.isInOutPatientDescriptionN : group.isInOutPatientDescription}", backgroundColor: AppColors.warningColorYellow.withOpacity(0.1), textColor: AppColors.warningColorYellow, ) ], ), Icon(isExpanded ? Icons.expand_less : Icons.expand_more), ], ), SizedBox(height: 8.h), Row( mainAxisSize: MainAxisSize.min, children: [ Image.network( group.doctorImageURL ?? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", width: 24.w, height: 24.h, fit: BoxFit.cover, ).circle(100), SizedBox(width: 8.h), Expanded( child: (group.doctorName ?? group.doctorNameEnglish ?? "") .toString() .toText14(isBold: true)), ], ), SizedBox(height: 8.h), Wrap( direction: Axis.horizontal, spacing: 4.h, runSpacing: 4.h, children: [ Directionality( textDirection: ui.TextDirection.ltr, child: AppCustomChipWidget( labelText: DateUtil.formatDateToDate( DateUtil.convertStringToDate(group.orderDate ?? ""), false), isEnglishOnly: true, )), AppCustomChipWidget( labelText: (group.projectName ?? ""), ), AppCustomChipWidget( labelText: (group.clinicDescription ?? ""), ), ], ), ], ), ), AnimatedSwitcher( duration: Duration(milliseconds: 500), switchInCurve: Curves.easeIn, switchOutCurve: Curves.easeOut, transitionBuilder: (Widget child, Animation animation) { return FadeTransition( opacity: animation, child: SizeTransition( sizeFactor: animation, axisAlignment: 0.0, child: child, ), ); }, child: isExpanded ? Container( key: ValueKey(index), padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 0.h), child: Column( children: [ ListView.separated( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, itemBuilder: (cxt, index) { PatientLabOrdersResponseModel order = group; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ "• ${order.testDetails![index].description!}" .toText14(isBold: true, isEnglishOnly: true), SizedBox(height: 4.h), order.testDetails![index].testDescriptionEn!.toText12( isBold: true, color: AppColors.textColorLight, isEnglishOnly: true), ], ).paddingOnly(top: 16, bottom: 16); }, separatorBuilder: (cxt, index) => Divider( color: AppColors.borderOnlyColor.withValues(alpha: 0.05), height: 1.h), itemCount: group.testDetails!.length > 3 ? 3 : group.testDetails!.length), SizedBox(height: 16.h), CustomButton( text: "${LocaleKeys.viewResults.tr()} (${group.testDetails!.length})", onPressed: () { labProvider.currentlySelectedPatientOrder = group; labProvider.getPatientLabResultByHospital(group); labProvider.getPatientSpecialResult(group); Navigator.of(context).push( CustomPageRoute( page: LabResultByClinic(labOrder: group), ), ); }, backgroundColor: AppColors.infoColor.withAlpha(20), borderColor: AppColors.infoColor.withAlpha(0), textColor: AppColors.infoColor, fontSize: (isFoldable || isTablet) ? 12.f : 14.f, isBold: true, borderRadius: 12.r, padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0), height: 40.h, iconSize: 14.h, icon: AppAssets.view_report_icon, iconColor: AppColors.infoColor, ), SizedBox(height: 16.h), ], ), ) : SizedBox.shrink(), ), ], ), ), ), ), )); }, ) : Utils.getNoDataWidget(context, noDataText: LocaleKeys.noLabResults.tr(context: context))) : // By Test or other tabs keep existing behavior (labViewModel.isLabOrdersLoading) ? Column( children: List.generate( 5, (index) => LabResultItemView( onTap: () {}, labOrder: null, index: index, isLoading: true, )), ) : AlphabeticScroll( alpahbetsAvailable: labViewModel.filteredIndexedCharacterForUniqueTest, details: labViewModel.filteredUniqueTestsList, labViewModel: labViewModel, rangeViewModel: rangeViewModel, appState: _appState, ) ], )); }, ), ); } Color getLabOrderStatusColor(num status) { switch (status) { case 44: return AppColors.warningColorYellow; case 45: return AppColors.warningColorYellow; case 16: return AppColors.successColor; case 17: return AppColors.successColor; default: return AppColors.greyColor; } } String getLabOrderStatusText(num status) { switch (status) { case 44: return LocaleKeys.resultsPending.tr(context: context); case 45: return LocaleKeys.resultsPending.tr(context: context); case 16: return LocaleKeys.resultsAvailable.tr(context: context); case 17: return LocaleKeys.resultsAvailable.tr(context: context); default: return ""; } } getLabSuggestions(LabViewModel model) { if (model.patientLabOrders.isEmpty) { return []; } return model.patientLabOrders.map((m) => m.testDetails).toList(); } } \ No newline at end of file +import 'dart:async'; import 'dart:ui' as ui; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/utils/date_util.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_via_clinic/LabResultByClinic.dart'; import 'package:hmg_patient_app_new/presentation/lab/search_lab_report.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart'; import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:provider/provider.dart'; import 'alphabeticScroll.dart'; class LabOrdersPage extends StatefulWidget { const LabOrdersPage({super.key}); @override State createState() => _LabOrdersPageState(); } class _LabOrdersPageState extends State { late LabViewModel labProvider; late DateRangeSelectorRangeViewModel rangeViewModel; late AppState _appState; List?> labSuggestions = []; int? expandedIndex; String? selectedFilterText = ''; int activeIndex = 0; @override void initState() { scheduleMicrotask(() { labProvider.initLabProvider(); }); super.initState(); } @override Widget build(BuildContext context) { labProvider = Provider.of(context, listen: false); rangeViewModel = Provider.of(context); _appState = getIt(); return CollapsingListView( title: LocaleKeys.labResults.tr(context: context), search: () async { if (labProvider.isLabOrdersLoading) { return; } else { String? value = await Navigator.of(context).push( CustomPageRoute( page: SearchLabResultsContent(labSuggestionsList: labProvider.labSuggestions), fullScreenDialog: true, direction: AxisDirection.down, ), ); if (value != null) { selectedFilterText = value; labProvider.filterLabReports(value); } } }, child: Consumer( builder: (context, labViewModel, child) { return SingleChildScrollView( physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.all(24.h), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: CustomTabBar( activeTextColor: Color(0xffED1C2B), activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1), tabs: [ CustomTabBarModel(null, LocaleKeys.byVisit.tr()), CustomTabBarModel(null, LocaleKeys.byTest.tr()), // CustomTabBarModel(null, "Completed".needTranslation), ], onTabChange: (index) { activeIndex = index; setState(() {}); }, ), ), ], ), if (activeIndex == 0) Padding( padding: EdgeInsets.symmetric(vertical: 10.h), child: Row( children: [ // CustomButton( // text: LocaleKeys.byClinic.tr(context: context), // onPressed: () { // labViewModel.setIsSortByClinic(true); // }, // backgroundColor: labViewModel.isSortByClinic ? AppColors.bgRedLightColor : AppColors.whiteColor, // borderColor: labViewModel.isSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2), // textColor: labViewModel.isSortByClinic ? AppColors.primaryRedColor : AppColors.blackColor, // fontSize: 12.f, // isBold: true, // borderRadius: 10, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // height: 40.h, // ), // SizedBox(width: 8.h), // CustomButton( // text: LocaleKeys.byHospital.tr(context: context), // onPressed: () { // labViewModel.setIsSortByClinic(false); // }, // backgroundColor: labViewModel.isSortByClinic ? AppColors.whiteColor : AppColors.bgRedLightColor, // borderColor: labViewModel.isSortByClinic ? AppColors.textColor.withValues(alpha: 0.2) : AppColors.primaryRedColor, // textColor: labViewModel.isSortByClinic ? AppColors.blackColor : AppColors.primaryRedColor, // fontSize: 12, // isBold: true, // borderRadius: 10, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // height: 40.h, // ), ], ), ), SizedBox(height: 8.h), selectedFilterText!.isNotEmpty ? Column( children: [ AppCustomChipWidget( labelText: selectedFilterText!, backgroundColor: AppColors.alertColor, textColor: AppColors.whiteColor, deleteIcon: AppAssets.close_bottom_sheet_icon, deleteIconColor: AppColors.whiteColor, deleteIconHasColor: true, isEnglishOnly: true, onDeleteTap: () { selectedFilterText = ""; labProvider.filterLabReports(""); }, ), SizedBox(height: 8.h), ], ) : SizedBox(), activeIndex == 0 ? // By Visit - show grouped view when available labViewModel.isLabOrdersLoading ? ListView.builder( shrinkWrap: true, physics: AlwaysScrollableScrollPhysics(), padding: EdgeInsets.zero, itemCount: 5, itemBuilder: (context, index) => LabResultItemView( onTap: () {}, labOrder: null, index: index, isLoading: true, ), ) : (labViewModel.patientLabOrders.isNotEmpty ? ListView.builder( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, itemCount: labViewModel.filteredLabOrders.length, itemBuilder: (context, index) { final group = labViewModel.filteredLabOrders[index]; final isExpanded = expandedIndex == index; return AnimationConfiguration.staggeredList( position: index, duration: const Duration(milliseconds: 500), child: SlideAnimation( verticalOffset: 100.0, child: FadeInAnimation( child: AnimatedContainer( duration: Duration(milliseconds: 300), curve: Curves.easeInOut, margin: EdgeInsets.symmetric(vertical: 8.h), decoration: RoundedRectangleBorder() .toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: true), child: InkWell( onTap: () { setState(() { expandedIndex = isExpanded ? null : index; }); }, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: EdgeInsets.all(16.h), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Wrap( direction: Axis.horizontal, spacing: 4.h, runSpacing: 4.h, children: [ AppCustomChipWidget( richText: Row( children: [ "${group.testDetails!.length} ".toText10( color: AppColors.successColor, isEnglishOnly: true, weight: FontWeight.w600), LocaleKeys.tests .tr(context: context) .toText10(color: AppColors.successColor, weight: FontWeight.w600), ], ), labelText: "${group.testDetails!.length} ${LocaleKeys.tests.tr(context: context)}", backgroundColor: AppColors.successColor.withOpacity(0.1), textColor: AppColors.successColor, ), AppCustomChipWidget( labelText: "${_appState.isArabic() ? group.isInOutPatientDescriptionN : group.isInOutPatientDescription}", backgroundColor: AppColors.warningColorYellow.withOpacity(0.1), textColor: AppColors.warningColorYellow, ) ], ), Icon(isExpanded ? Icons.expand_less : Icons.expand_more), ], ), SizedBox(height: 8.h), Row( mainAxisSize: MainAxisSize.min, children: [ Image.network( group.doctorImageURL ?? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", width: 24.w, height: 24.h, fit: BoxFit.cover, ).circle(100), SizedBox(width: 8.h), Expanded( child: (group.doctorName ?? group.doctorNameEnglish ?? "") .toString() .toText14(isBold: true)), ], ), SizedBox(height: 8.h), Wrap( direction: Axis.horizontal, spacing: 4.h, runSpacing: 4.h, children: [ Directionality( textDirection: ui.TextDirection.ltr, child: AppCustomChipWidget( labelText: DateUtil.formatDateToDate( DateUtil.convertStringToDate(group.orderDate ?? ""), false), isEnglishOnly: true, )), AppCustomChipWidget( labelText: (group.projectName ?? ""), ), AppCustomChipWidget( labelText: (group.clinicDescription ?? ""), ), ], ), ], ), ), AnimatedSwitcher( duration: Duration(milliseconds: 500), switchInCurve: Curves.easeIn, switchOutCurve: Curves.easeOut, transitionBuilder: (Widget child, Animation animation) { return FadeTransition( opacity: animation, child: SizeTransition( sizeFactor: animation, axisAlignment: 0.0, child: child, ), ); }, child: isExpanded ? Container( key: ValueKey(index), padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 0.h), child: Column( children: [ ListView.separated( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, itemBuilder: (cxt, index) { PatientLabOrdersResponseModel order = group; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ "• ${order.testDetails![index].description!}" .toText14(isBold: true, isEnglishOnly: true), SizedBox(height: 4.h), (_appState.isArabic() ? order.testDetails![index].testDescriptionAr! : order.testDetails![index].testDescriptionEn!).toText12( isBold: true, color: AppColors.textColorLight, isEnglishOnly: true), ], ).paddingOnly(top: 16, bottom: 16); }, separatorBuilder: (cxt, index) => Divider( color: AppColors.borderOnlyColor.withValues(alpha: 0.05), height: 1.h), itemCount: group.testDetails!.length > 3 ? 3 : group.testDetails!.length), SizedBox(height: 16.h), CustomButton( text: "${LocaleKeys.viewResults.tr()} (${group.testDetails!.length})", onPressed: () { labProvider.currentlySelectedPatientOrder = group; labProvider.getPatientLabResultByHospital(group); labProvider.getPatientSpecialResult(group); Navigator.of(context).push( CustomPageRoute( page: LabResultByClinic(labOrder: group), ), ); }, backgroundColor: AppColors.infoColor.withAlpha(20), borderColor: AppColors.infoColor.withAlpha(0), textColor: AppColors.infoColor, fontSize: (isFoldable || isTablet) ? 12.f : 14.f, isBold: true, borderRadius: 12.r, padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0), height: 40.h, iconSize: 14.h, icon: AppAssets.view_report_icon, iconColor: AppColors.infoColor, ), SizedBox(height: 16.h), ], ), ) : SizedBox.shrink(), ), ], ), ), ), ), )); }, ) : Utils.getNoDataWidget(context, noDataText: LocaleKeys.noLabResults.tr(context: context))) : // By Test or other tabs keep existing behavior (labViewModel.isLabOrdersLoading) ? Column( children: List.generate( 5, (index) => LabResultItemView( onTap: () {}, labOrder: null, index: index, isLoading: true, )), ) : AlphabeticScroll( alpahbetsAvailable: labViewModel.filteredIndexedCharacterForUniqueTest, details: labViewModel.filteredUniqueTestsList, labViewModel: labViewModel, rangeViewModel: rangeViewModel, appState: _appState, ) ], )); }, ), ); } Color getLabOrderStatusColor(num status) { switch (status) { case 44: return AppColors.warningColorYellow; case 45: return AppColors.warningColorYellow; case 16: return AppColors.successColor; case 17: return AppColors.successColor; default: return AppColors.greyColor; } } String getLabOrderStatusText(num status) { switch (status) { case 44: return LocaleKeys.resultsPending.tr(context: context); case 45: return LocaleKeys.resultsPending.tr(context: context); case 16: return LocaleKeys.resultsAvailable.tr(context: context); case 17: return LocaleKeys.resultsAvailable.tr(context: context); default: return ""; } } getLabSuggestions(LabViewModel model) { if (model.patientLabOrders.isEmpty) { return []; } return model.patientLabOrders.map((m) => m.testDetails).toList(); } } \ No newline at end of file diff --git a/lib/presentation/lab/lab_result_via_clinic/LabResultList.dart b/lib/presentation/lab/lab_result_via_clinic/LabResultList.dart index 608f70a5..88b9cb6a 100644 --- a/lib/presentation/lab/lab_result_via_clinic/LabResultList.dart +++ b/lib/presentation/lab/lab_result_via_clinic/LabResultList.dart @@ -181,10 +181,11 @@ class _ExpandableDescriptionText extends StatelessWidget { static TextStyle get textStyle => TextStyle( fontSize: 14.f, - color: AppColors.textColor, // or Colors.black87 - fontFamily: 'Poppins', - fontWeight: FontWeight.w400, - fontStyle: FontStyle.normal, + color: AppColors.textColorLight, + // or Colors.black87 + // fontFamily: 'Poppins', + fontWeight: FontWeight.w600, + fontStyle: FontStyle.normal, height: 1.4, letterSpacing: 0.0, decoration: TextDecoration.none, @@ -199,7 +200,7 @@ class _ExpandableDescriptionText extends StatelessWidget { final readMoreStyle = TextStyle( fontSize: 12.f, color: AppColors.primaryRedColor, - fontFamily: 'Poppins', + // fontFamily: 'Poppins', fontWeight: FontWeight.w500, ); diff --git a/lib/presentation/medical_file/medical_file_page.dart b/lib/presentation/medical_file/medical_file_page.dart index a6a335fc..b382bf01 100644 --- a/lib/presentation/medical_file/medical_file_page.dart +++ b/lib/presentation/medical_file/medical_file_page.dart @@ -110,7 +110,7 @@ class _MedicalFilePageState extends State { appState = getIt.get(); scheduleMicrotask(() { if (appState.isAuthenticated) { - myAppointmentsViewModel.getPatientMyDoctors(); + myAppointmentsViewModel.getPatientMyDoctors(isTop8: true); prescriptionsViewModel.initPrescriptionsViewModel(); insuranceViewModel.initInsuranceProvider(); medicalFileViewModel.setIsPatientSickLeaveListLoading(true); @@ -1046,6 +1046,7 @@ class _MedicalFilePageState extends State { ], ).onPress(() { // myAppointmentsViewModel.getPatientMyDoctors(); + myAppointmentsViewModel.setIsMyDoctorsDataToBeLoaded(true); Navigator.of(context).push( CustomPageRoute( page: MyDoctorsPage(), diff --git a/lib/presentation/notifications/notification_details_page.dart b/lib/presentation/notifications/notification_details_page.dart index abe8b521..ba78a298 100644 --- a/lib/presentation/notifications/notification_details_page.dart +++ b/lib/presentation/notifications/notification_details_page.dart @@ -1,5 +1,6 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_screenshot_blocker/flutter_screenshot_blocker.dart'; import 'package:hmg_patient_app_new/core/utils/date_util.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; @@ -20,28 +21,38 @@ class NotificationDetailsPage extends StatelessWidget { @override Widget build(BuildContext context) { - return CollapsingListView( - title: LocaleKeys.notificationDetails.tr(), - // trailing: IconButton( - // icon: Icon( - // Icons.share_outlined, - // size: 24.h, - // color: AppColors.textColor, - // ), - // onPressed: () { - // _shareNotification(); - // }, - // ), - child: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox(height: 24.h), - // Notification content card - _buildNotificationCard(context), - SizedBox(height: 24.h), - ], - ).paddingSymmetrical(24.w, 0.h), + return ScreenshotBlockerWidget( + blockScreenshots: true, // makes this widget appear black in screenshots + detectScreenshots: true, // listen for screenshot attempts + onScreenshotDetected: (event) { + // event.type → 'screenshot_taken' or 'screenshot_blocked' + // ScaffoldMessenger.of(context).showSnackBar( + // SnackBar(content: Text('Screenshots are not allowed!')), + // ); + }, + child: CollapsingListView( + title: LocaleKeys.notificationDetails.tr(), + // trailing: IconButton( + // icon: Icon( + // Icons.share_outlined, + // size: 24.h, + // color: AppColors.textColor, + // ), + // onPressed: () { + // _shareNotification(); + // }, + // ), + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 24.h), + // Notification content card + _buildNotificationCard(context), + SizedBox(height: 24.h), + ], + ).paddingSymmetrical(24.w, 0.h), + ), ), ); } @@ -109,9 +120,9 @@ class NotificationDetailsPage extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ LocaleKeys.messageNotification.tr().toText14( - weight: FontWeight.w600, - color: AppColors.greyTextColor, - ), + weight: FontWeight.w600, + color: AppColors.greyTextColor, + ), SizedBox(height: 8.h), notification.message!.toText16( weight: FontWeight.w400, @@ -123,8 +134,7 @@ class NotificationDetailsPage extends StatelessWidget { ), // Notification image (if MessageType is "image") - if (notification.messageType != null && - notification.messageType!.toLowerCase() == "image") + if (notification.messageType != null && notification.messageType!.toLowerCase() == "image") Builder( builder: (context) { // Try to get image URL from videoURL or messageTypeData @@ -147,9 +157,9 @@ class NotificationDetailsPage extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ LocaleKeys.attachedImage.tr().toText14( - weight: FontWeight.w600, - color: AppColors.greyTextColor, - ), + weight: FontWeight.w600, + color: AppColors.greyTextColor, + ), SizedBox(height: 8.h), ClipRRect( borderRadius: BorderRadius.circular(12.h), @@ -177,8 +187,8 @@ class NotificationDetailsPage extends StatelessWidget { ), SizedBox(height: 8.h), LocaleKeys.failedToLoadImage.tr().toText12( - color: AppColors.greyTextColor, - ), + color: AppColors.greyTextColor, + ), SizedBox(height: 4.h), Text( imageUrl!, @@ -205,10 +215,7 @@ class NotificationDetailsPage extends StatelessWidget { ), child: Center( child: CircularProgressIndicator( - value: loadingProgress.expectedTotalBytes != null - ? loadingProgress.cumulativeBytesLoaded / - loadingProgress.expectedTotalBytes! - : null, + value: loadingProgress.expectedTotalBytes != null ? loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes! : null, ), ), ); @@ -249,7 +256,8 @@ ${notification.message ?? 'Notification'} Time: ${_formatTime(notification.isSentOn)} Date: ${_formatDate(notification.isSentOn)} ${notification.notificationType != null ? '\nType: ${notification.notificationType}' : ''} - '''.trim(); + ''' + .trim(); await Share.share(shareText); } @@ -274,4 +282,3 @@ ${notification.notificationType != null ? '\nType: ${notification.notificationTy } } } - diff --git a/lib/presentation/notifications/notifications_list_page.dart b/lib/presentation/notifications/notifications_list_page.dart index e9a9447e..afea0fba 100644 --- a/lib/presentation/notifications/notifications_list_page.dart +++ b/lib/presentation/notifications/notifications_list_page.dart @@ -61,7 +61,8 @@ class _NotificationsListPageState extends State { child: ListView.builder( itemCount: notificationsVM.isNotificationsLoading ? 4 - : notificationsVM.notificationsList.length + (notificationsVM.hasMoreNotifications ? 1 : 0), + // : notificationsVM.notificationsList.length + (notificationsVM.hasMoreNotifications ? 1 : 0), + : notificationsVM.notificationsList.length, physics: NeverScrollableScrollPhysics(), shrinkWrap: true, padding: EdgeInsetsGeometry.zero, diff --git a/pubspec.yaml b/pubspec.yaml index eafbd513..bdce8130 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,8 +2,8 @@ name: hmg_patient_app_new description: "New HMG Patient App" publish_to: 'none' # Remove this line if you wish to publish to pub.dev -#version: 0.0.38+37 -version: 0.0.7+1 +version: 0.0.39+38 +#version: 0.0.8+1 environment: sdk: ">=3.6.0 <4.0.0" @@ -108,6 +108,8 @@ dependencies: clarity_flutter: 1.9.0 flutter_amazonpaymentservices: ^0.0.13 screen_brightness: ^1.0.1 + flutter_screenshot_blocker: ^1.0.4 + cloudflare_turnstile: ^3.7.2 dev_dependencies: flutter_test: