From aa22ad1e0788b5293c6d7eaf15a8b284009cf98b Mon Sep 17 00:00:00 2001 From: tahaalam Date: Tue, 14 Apr 2026 13:54:01 +0300 Subject: [PATCH 1/2] calender request changes --- lib/core/utils/calender_utils_new.dart | 87 ++++++++++++------- .../appointment_details_page.dart | 6 +- 2 files changed, 61 insertions(+), 32 deletions(-) diff --git a/lib/core/utils/calender_utils_new.dart b/lib/core/utils/calender_utils_new.dart index 347c2ebc..a30de823 100644 --- a/lib/core/utils/calender_utils_new.dart +++ b/lib/core/utils/calender_utils_new.dart @@ -16,33 +16,64 @@ class CalenderUtilsNew { final DeviceCalendar calender = DeviceCalendar.instance; List writableCalender = []; - CalenderUtilsNew._instance() { - getCalenders(); - } + /// Completer to ensure getCalenders() only runs once at a time + Completer? _calendarInitCompleter; + + CalenderUtilsNew._instance(); static final CalenderUtilsNew instance = CalenderUtilsNew._instance(); - Future getCalenders() async { + /// Ensures calendars are loaded exactly once. Safe to call multiple times — + /// subsequent calls await the same future. + Future ensureCalendarsLoaded() async { + if (writableCalender.isNotEmpty) return; + if (_calendarInitCompleter != null) { + // Already in progress — just wait for it + return _calendarInitCompleter!.future; + } + _calendarInitCompleter = Completer(); + try { + await _loadCalendars(); + _calendarInitCompleter!.complete(); + } catch (e) { + _calendarInitCompleter!.completeError(e); + _calendarInitCompleter = null; // Allow retry on failure + rethrow; + } + } + + Future _loadCalendars() async { + print("the get calenders is called"); CalendarPermissionStatus result = await DeviceCalendar.instance.hasPermissions(); if (result != CalendarPermissionStatus.granted) { await DeviceCalendar.instance.requestPermissions(); - showCommonBottomSheetWithoutHeight( - title: LocaleKeys.notice.tr(context: GetIt.instance().navigatorKey.currentContext!), - GetIt.instance().navigatorKey.currentContext!, - child: Utils.getWarningWidget( - loadingText: LocaleKeys.calendarPermissionAlert.tr(), - isShowActionButtons: true, - onCancelTap: () { - GetIt.instance().pop(); - }, - onConfirmTap: () async { - GetIt.instance().pop(); - openAppSettings(); - }), - callBackFunc: () {}, - isFullScreen: false, - isCloseButtonVisible: true, - ); + result = await DeviceCalendar.instance.hasPermissions(); + // if (result != CalendarPermissionStatus.granted) { + // print("the permission is not granted"); + // showCommonBottomSheetWithoutHeight( + // title: LocaleKeys.notice.tr(context: GetIt + // .instance() + // .navigatorKey + // .currentContext!), + // GetIt + // .instance() + // .navigatorKey + // .currentContext!, + // child: Utils.getWarningWidget( + // loadingText: LocaleKeys.calendarPermissionAlert.tr(), + // isShowActionButtons: true, + // onCancelTap: () { + // GetIt.instance().pop(); + // }, + // onConfirmTap: () async { + // GetIt.instance().pop(); + // openAppSettings(); + // }), + // callBackFunc: () {}, + // isFullScreen: false, + // isCloseButtonVisible: true, + // ); + // } } var calenders = await calender.listCalendars(); calenders.forEach((calender) { @@ -53,9 +84,7 @@ class CalenderUtilsNew { } FutureOr createOrUpdateEvent({required String title, required String description, required String location, DateTime? scheduleDateTime, String? eventId, int? reminderMinutes}) async { - if (writableCalender.isEmpty) { - await getCalenders(); - } + await ensureCalendarsLoaded(); var writableCalendars = writableCalender.first; String eventResult = ""; CalendarPermissionStatus result = await DeviceCalendar.instance.hasPermissions(); @@ -146,9 +175,7 @@ class CalenderUtilsNew { } FutureOr checkIfEventExist(String admissionId) async { - if (writableCalender.isEmpty) { - await getCalenders(); - } + await ensureCalendarsLoaded(); List events = await getEvents(); if (events.isEmpty) return false; for (var event in events) { @@ -161,7 +188,7 @@ class CalenderUtilsNew { FutureOr getEventIdIfEventExist(String admissionId) async { if (writableCalender.isEmpty) { - await getCalenders(); + await ensureCalendarsLoaded(); } List events = await getEvents(); if (events.isEmpty) return ""; @@ -174,7 +201,7 @@ class CalenderUtilsNew { FutureOr checkAndRemove({required String id}) async { if (writableCalender.isEmpty) { - await getCalenders(); + await ensureCalendarsLoaded(); } List events = await getEvents(); if (events.isEmpty) return false; @@ -191,7 +218,7 @@ class CalenderUtilsNew { FutureOr checkAndRemoveMultipleItems({required String id}) async { if (writableCalender.isEmpty) { - await getCalenders(); + await ensureCalendarsLoaded(); } List events = await getEvents(); if (events.isEmpty) return false; diff --git a/lib/presentation/appointments/appointment_details_page.dart b/lib/presentation/appointments/appointment_details_page.dart index 40ef4644..545fdb18 100644 --- a/lib/presentation/appointments/appointment_details_page.dart +++ b/lib/presentation/appointments/appointment_details_page.dart @@ -103,14 +103,15 @@ class _AppointmentDetailsPageState extends State { } // // Check if reminder exists in calendar and update the flag - // await _checkAndUpdateReminderStatus(); } }); // Initialize countdown timer for payment after first frame to ensure context is available - WidgetsBinding.instance.addPostFrameCallback((_) { + WidgetsBinding.instance.addPostFrameCallback((_) async { if (mounted) { _startCountdownTimer(); + await _checkAndUpdateReminderStatus(); + } }); } @@ -373,6 +374,7 @@ class _AppointmentDetailsPageState extends State { ? null : Column( children: [ + SizedBox(height: 16.w), Divider(height: 1, color: AppColors.dividerColor), SizedBox(height: 16.w), From 5a4e5e138be1d6abe49912ba6ac297e63501a81b Mon Sep 17 00:00:00 2001 From: tahaalam Date: Tue, 14 Apr 2026 15:42:19 +0300 Subject: [PATCH 2/2] string localization changes --- assets/langs/ar-SA.json | 17 +++++++++-------- assets/langs/en-US.json | 11 ++++++----- lib/generated/locale_keys.g.dart | 1 + lib/presentation/authentication/login.dart | 2 +- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 5a8df673..f339bdd0 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -101,7 +101,7 @@ "onlinePaymentSubtitle": "الدفع", "cancelNocaps": "إلغاء", "setReminder": "تذكير", - "nationalIdNumber": "رقم الهوية الوطنية", + "nationalIdNumber": "رقم الهوية / الإقام", "medicalFileNumber": "رقم الملف الطبي", "loginregister": "تسجيل الدخول / التسجيل", "poweredBy": "مدعوم من", @@ -125,7 +125,7 @@ "profileInfo": "يرجى إدخال معلومات الملف الشخصي", "submit": "تحديث", "forgotDesc": "أدخل رقم الهاتف المحمول لاستلام رقم الملف الطبي عبر الرسائل القصيرة", - "dob": "تاريخ الميلاد:", + "dob": "تاريخ الميلاد", "hijriDate": "الت��ريخ الهجري", "gregorianDate": "التاريخ الميلادي", "verifyLoginWith": "يرجى اختيار واحدة من الخيارات التالية للتحقق", @@ -815,7 +815,7 @@ "enterEmailDesc": "أدخل عنوان بريدك الإلكتروني لإكمال عملية إنشاء ملف طبي", "enterPhoneDesc": "أدخل رقم هاتفك لتلقي رمز التحقق ", "pleaseChooseOption": "الرجاء اختيار من الخيارات أدناه لتلقي رمز التحقق OTP", - "prepareToElevate": "هل أنت مستعد لتحسين صحتك ورفاهيتك؟", + "prepareToElevate": "رعايتك الصحية تبدأ من هنا", "iAcceptTermsConditions": "أوافق على الشروط والأحكام", "alreadyHaveAccount": "هل لديك حساب بالفعل؟", "loginNow": "تسجيل الدخول الآن", @@ -859,8 +859,8 @@ "didntReceiveIt": "لم تستلمه؟", "resendOTP": "إعادة إرسال", "resendIn": "إعادة الإرسال في", - "pleaseEnterAnationalID": "يرجى إدخال رقم الهوية الوطنية", - "pleaseEnterAFileNumber": "يرجى إدخال رقم الملف", + "pleaseEnterAnationalID": "تسجيل الدخول برقم الهوية او الاقامة", + "pleaseEnterAFileNumber": "يرجى إدخال / رقم الملف", "pleaseEnterAValidEmail": "يرجى إدخال بريد إلكتروني صالح", "pleaseEnterFullName": "يرجى إدخال الاسم الكامل", "pleaseAcceptTermsConditions": "يرجى قبول الشروط والأحكام", @@ -1681,10 +1681,10 @@ "favouriteList": "قائمة المفضلة", "later": "لاحقاً", "cancelAppointmentConfirmMessage": "هل أنت متأكد من رغبتك في إلغاء هذا الموعد؟", - "acknowledged": "حسنا", + "acknowledged": "إغلاق", "searchLabResults": "بحث نتائج المختبر", "callForAssistance": "اتصل للحصول على المساعدة الفورية", - "oneWaySubtitle": "نقل من الموقع إلى المستشفى", + "oneWaySubtitle": "نقل م ن الموقع إلى المستشفى", "twoWaySubtitle": "نقل من الموقع إلى المستشفى والعودة مرة أخرى", "toHospitalSubtitle": "نقل من موقعك الحالي إلى المستشفى", "fromHospitalSubtitle": "نقل من المستشفى إلى منزلك", @@ -1748,5 +1748,6 @@ "m": "متر", "inch": "انش", "updateEmailAddressTitle": "تحديث البريد الالكتروني: ", - "enterNewNewContactNumber": "الرجاء اضافة رقم اتصال في حالة الطواريء: " + "enterNewNewContactNumber": "الرجاء اضافة رقم اتصال في حالة الطواريء: ", + "nationalIdFileNumber": "رقم الهوية / الإقامة / الملف" } diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 5a7f9e66..ddecd702 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -99,7 +99,7 @@ "onlinePaymentSubtitle": "Payment", "cancelNocaps": "Cancel", "setReminder": "Set Reminder", - "nationalIdNumber": "National ID Number", + "nationalIdNumber": "National ID / Iqama", "medicalFileNumber": "Medical File Number", "loginregister": "Login / Register", "poweredBy": "Powered by", @@ -123,7 +123,7 @@ "profileInfo": "Please enter profile information", "submit": "Submit", "forgotDesc": "Enter the mobile number to receive the Medical File Number via SMS", - "dob": "Birth Date:", + "dob": "Birth Date", "hijriDate": "Hijri Date", "gregorianDate": "Gregorian Date", "verifyLoginWith": "Please choose one of the following options to verify", @@ -806,7 +806,7 @@ "readInstructions": "Read Instructions", "searchLabReport": "Search Lab Report", "prescriptionDeliveryError": "This clinic doesn't support refill", - "prepareToElevate": "Prepared to elevate your health and well-being?", + "prepareToElevate": "Your wellness journey starts here.", "iAcceptTermsConditions": "I Accept the Terms and Conditions", "alreadyHaveAccount": "Already have an account?", "loginNow": "Login Now", @@ -997,7 +997,7 @@ "processingCheckIn": "Processing Check-In...", "bookingWaitingAppointment": "Booking Waiting Appointment, Please wait...", "enterValidIDorIqama": "Please enter a valid national ID or file number", - "selectAppointment": "Select Appointment", + "selectAppointment": "Select Axppointment", "rebookSameDoctor": "Rebook with same doctor", "queueing": "Queueing", "inQueue": "In Queue", @@ -1737,5 +1737,6 @@ "m": "m", "inch": "in", "updateEmailAddressTitle": "Enter the new email address to be updated: ", - "enterNewNewContactNumber": "Enter the new emergency contact number to be updated: " + "enterNewNewContactNumber": "Enter the new emergency contact number to be updated: ", + "nationalIdFileNumber": "National ID/File Number" } diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index f71483b9..51d64b07 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -1739,5 +1739,6 @@ abstract class LocaleKeys { static const inch = 'inch'; static const updateEmailAddressTitle = 'updateEmailAddressTitle'; static const enterNewNewContactNumber = 'enterNewNewContactNumber'; + static const nationalIdFileNumber = 'nationalIdFileNumber'; } diff --git a/lib/presentation/authentication/login.dart b/lib/presentation/authentication/login.dart index b2979235..bbb6b4eb 100644 --- a/lib/presentation/authentication/login.dart +++ b/lib/presentation/authentication/login.dart @@ -75,7 +75,7 @@ class LoginScreenState extends State { SizedBox(height: 32.h), Localizations.override(context: context, locale: Locale('en', 'US'), child: Container()), // Force English locale for this widget TextInputWidget( - labelText: "${LocaleKeys.nationalId.tr(context: context)} / ${LocaleKeys.fileNo.tr(context: context)}", + labelText: "${LocaleKeys.nationalIdFileNumber.tr(context: context)}", hintText: "xxxxxxxxx", controller: authVm.nationalIdController, focusNode: _nationalIdFocusNode,