From 6e05ca15cf438027e70ba93fcda787c04a492f63 Mon Sep 17 00:00:00 2001 From: faizatflutter Date: Thu, 4 Sep 2025 10:01:54 +0300 Subject: [PATCH] adding the login function --- lib/core/api/api_client.dart | 23 +- lib/core/api_consts.dart | 1 + lib/core/app_state.dart | 19 +- lib/core/dependencies.dart | 1 - lib/core/enums.dart | 3 +- lib/core/utils/loading_utils.dart | 87 +++++++ lib/core/utils/request_utils.dart | 3 + lib/core/utils/utils.dart | 115 +++------ lib/core/utils/validation_utils.dart | 19 ++ .../authentication/authentication_repo.dart | 13 +- .../authentication_view_model.dart | 219 +++++++++++------- lib/presentation/authentication/login.dart | 21 +- lib/presentation/home/landing_page.dart | 6 +- lib/services/error_handler_service.dart | 1 + pubspec.lock | 30 ++- pubspec.yaml | 1 + 16 files changed, 347 insertions(+), 215 deletions(-) create mode 100644 lib/core/utils/loading_utils.dart create mode 100644 lib/core/utils/validation_utils.dart diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart index 92a7e6c..a7835f4 100644 --- a/lib/core/api/api_client.dart +++ b/lib/core/api/api_client.dart @@ -134,9 +134,7 @@ class ApiClientImp implements ApiClient { } if (body.containsKey('isDentalAllowedBackend')) { - body['isDentalAllowedBackend'] = body.containsKey('isDentalAllowedBackend') - ? body['isDentalAllowedBackend'] ?? IS_DENTAL_ALLOWED_BACKEND - : IS_DENTAL_ALLOWED_BACKEND; + body['isDentalAllowedBackend'] = body.containsKey('isDentalAllowedBackend') ? body['isDentalAllowedBackend'] ?? IS_DENTAL_ALLOWED_BACKEND : IS_DENTAL_ALLOWED_BACKEND; } //Todo: I have converted it to string @@ -204,9 +202,9 @@ class ApiClientImp implements ApiClient { logApiEndpointError(endPoint, 'Error While Fetching data', statusCode); } else { var parsed = json.decode(utf8.decode(response.bodyBytes)); - log("parsed: ${parsed.toString()}"); + log("parsed: ${jsonEncode(parsed)}"); if (isAllowAny) { - onSuccess(parsed, statusCode); + onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus']); } else { if (parsed['Response_Message'] != null) { onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus']); @@ -225,19 +223,18 @@ class ApiClientImp implements ApiClient { if (parsed['isSMSSent'] == true) { onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus']); } else if (parsed['MessageStatus'] == 1) { - onSuccess(parsed, statusCode); + onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus']); } else if (parsed['Result'] == 'OK') { - onSuccess(parsed, statusCode); + onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus']); } else { - onFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode, - failureType: ServerFailure("Error While Fetching data")); + onFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode, failureType: ServerFailure("Error While Fetching data")); logApiEndpointError(endPoint, parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode); } } else if (parsed['MessageStatus'] == 1 || parsed['SMSLoginRequired'] == true) { - onSuccess(parsed, statusCode); + onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus']); } else if (parsed['MessageStatus'] == 2 && parsed['IsAuthenticated']) { if (parsed['SameClinicApptList'] != null) { - onSuccess(parsed, statusCode); + onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus']); } else { if (parsed['message'] == null && parsed['ErrorEndUserMessage'] == null) { if (parsed['ErrorSearchMsg'] == null) { @@ -266,7 +263,7 @@ class ApiClientImp implements ApiClient { } } else { if (parsed['SameClinicApptList'] != null) { - onSuccess(parsed, statusCode); + onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus']); } else { if (parsed['message'] != null) { onFailure( @@ -292,7 +289,7 @@ class ApiClientImp implements ApiClient { onFailure( 'Please Check The Internet Connection 1', -1, - failureType: ConnectivityFailure("Error While Fetching data"), + failureType: ConnectivityFailure("Please Check The Internet Connection 1"), ); _analytics.errorTracking.log("internet_connectivity", error: "no internet available"); } diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index 6f82e26..1522174 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -747,6 +747,7 @@ class ApiConsts { static final String selectDeviceImei = 'Services/Patients.svc/REST/Patient_SELECTDeviceIMEIbyIMEI'; static final String sendActivationCode = 'Services/Authentication.svc/REST/SendActivationCodebyOTPNotificationType'; + static final String checkPatientAuth = 'Services/Authentication.svc/REST/CheckPatientAuthentication'; diff --git a/lib/core/app_state.dart b/lib/core/app_state.dart index 7a29273..4b31bab 100644 --- a/lib/core/app_state.dart +++ b/lib/core/app_state.dart @@ -1,6 +1,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:hmg_patient_app_new/core/post_params_model.dart'; import 'package:hmg_patient_app_new/features/authentication/models/resp_models/authenticated_user_resp_model.dart'; +import 'package:hmg_patient_app_new/features/authentication/models/resp_models/select_device_by_imei.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'api_consts.dart' as ApiConsts; @@ -8,9 +9,11 @@ import 'api_consts.dart' as ApiConsts; class AppState { NavigationService navigationService; - AppState({required this.navigationService}); + AppState({ + required this.navigationService, + }); - bool isAuthenticated = true; + bool isAuthenticated = false; set setIsAuthenticated(v) => isAuthenticated = v; @@ -70,4 +73,16 @@ class AppState { } AuthenticatedUser? get getAuthenticatedUser => _authenticatedUser; + + SelectDeviceByImeiRespModelElement? _selectDeviceByImeiRespModelElement; + + void setSelectDeviceByImeiRespModelElement(SelectDeviceByImeiRespModelElement value) { + _selectDeviceByImeiRespModelElement = value; + } + + SelectDeviceByImeiRespModelElement? get getSelectDeviceByImeiRespModelElement => _selectDeviceByImeiRespModelElement; + + String appLoginToken = ""; + + set setAppLoginToken(v) => appLoginToken = v; } diff --git a/lib/core/dependencies.dart b/lib/core/dependencies.dart index b878ce5..ee20a60 100644 --- a/lib/core/dependencies.dart +++ b/lib/core/dependencies.dart @@ -43,7 +43,6 @@ class AppDependencies { getIt.registerLazySingleton(() => NavigationService()); getIt.registerLazySingleton(() => GAnalytics()); getIt.registerLazySingleton(() => AppState(navigationService: getIt())); - getIt.registerLazySingleton(() => AppState(navigationService: getIt())); getIt.registerLazySingleton(() => LocationUtils( isShowConfirmDialog: false, navigationService: getIt(), diff --git a/lib/core/enums.dart b/lib/core/enums.dart index 2fd5867..77a17bc 100644 --- a/lib/core/enums.dart +++ b/lib/core/enums.dart @@ -28,7 +28,6 @@ enum LoginTypeEnum { silentWithOTP, } -enum OTPTypeEnum { sms, whatsapp } enum CountryEnum { saudiArabia, unitedArabEmirates } @@ -40,6 +39,8 @@ enum MaritalStatusTypeEnum { single, married, divorced, widowed } enum ChipTypeEnum { success, error, alert, info, warning } +enum OTPTypeEnum { sms, whatsapp } + extension OTPTypeEnumExtension on OTPTypeEnum { /// Convert enum to int int toInt() { diff --git a/lib/core/utils/loading_utils.dart b/lib/core/utils/loading_utils.dart new file mode 100644 index 0000000..1dd0410 --- /dev/null +++ b/lib/core/utils/loading_utils.dart @@ -0,0 +1,87 @@ +import 'dart:developer'; + +import 'package:flutter/material.dart'; +import 'package:gif_view/gif_view.dart'; +import 'package:hmg_patient_app_new/core/dependencies.dart'; +import 'package:hmg_patient_app_new/services/navigation_service.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; + +class LoadingUtils { + static final NavigationService _navigationService = getIt.get(); + + static bool _isLoadingVisible = false; + + static bool get isLoading => _isLoadingVisible; + + static showFullScreenLoading({bool barrierDismissible = true}) { + if (!_isLoadingVisible) { + _isLoadingVisible = true; + final context = _navigationService.navigatorKey.currentContext; + log("context:$context"); + if (context == null) return; + + showDialog( + barrierDismissible: barrierDismissible, + context: context, + barrierColor: AppColors.blackColor.withOpacity(0.5), + useRootNavigator: false, + builder: (BuildContext context) => Center( + child: CircularProgressIndicator( + color: AppColors.primaryRedColor, + )), + ).then((value) { + _isLoadingVisible = false; + }); + } + } + + static hideFullScreenLoader() { + if (!_isLoadingVisible) return; + + final context = _navigationService.navigatorKey.currentContext; + if (context != null) { + try { + Navigator.of(context).pop(); + } catch (_) {} + } + _isLoadingVisible = false; + } +} + +class GifLoaderContainer extends StatefulWidget { + final bool barrierDismissible; + + const GifLoaderContainer({super.key, this.barrierDismissible = true}); + + @override + GifLoaderContainerState createState() => GifLoaderContainerState(); +} + +class GifLoaderContainerState extends State with TickerProviderStateMixin { + late GifController controller; + + @override + void initState() { + super.initState(); + controller = GifController(); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return PopScope( + canPop: widget.barrierDismissible, + child: Center( + child: GifView( + controller: controller, + image: AssetImage("assets/images/progress-loading-red.gif"), + ), + ), + ); + } +} diff --git a/lib/core/utils/request_utils.dart b/lib/core/utils/request_utils.dart index 1869923..7e02ae2 100644 --- a/lib/core/utils/request_utils.dart +++ b/lib/core/utils/request_utils.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/features/authentication/models/request_models/send_activation_request_model.dart'; @@ -17,6 +19,7 @@ class RequestUtils { if (nationIdText.isNotEmpty) { fileNo = nationIdText.length < 10; } + log("phoneNumber: ${phoneNumber}"); var request = SendActivationRequest(); request.patientMobileNumber = int.parse(phoneNumber); request.mobileNo = '0$phoneNumber'; diff --git a/lib/core/utils/utils.dart b/lib/core/utils/utils.dart index ed4770d..b539630 100644 --- a/lib/core/utils/utils.dart +++ b/lib/core/utils/utils.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:developer'; import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:crypto/crypto.dart' as crypto; @@ -13,7 +14,6 @@ import 'package:hmg_patient_app_new/core/dependencies.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/generated/locale_keys.g.dart'; -import 'package:hmg_patient_app_new/services/dialog_service.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/dialogs/confirm_dialog.dart'; @@ -59,14 +59,12 @@ class Utils { // ), // )); return !isAddHours - ? DateFormat('hh:mm a', appState.isArabic() ? "ar_SA" : "en_US") - .format(DateTime.tryParse(startTime.contains("T") ? startTime : convertStringToDateTime(startTime))!.toLocal()) - : DateFormat('hh:mm a', appState.isArabic() ? "ar_SA" : "en_US") - .format(DateTime.tryParse(startTime.contains("T") ? startTime : convertStringToDateTime(startTime))!.add( - Duration( - hours: isAddHours ? 3 : 0, - ), - )); + ? DateFormat('hh:mm a', appState.isArabic() ? "ar_SA" : "en_US").format(DateTime.tryParse(startTime.contains("T") ? startTime : convertStringToDateTime(startTime))!.toLocal()) + : DateFormat('hh:mm a', appState.isArabic() ? "ar_SA" : "en_US").format(DateTime.tryParse(startTime.contains("T") ? startTime : convertStringToDateTime(startTime))!.add( + Duration( + hours: isAddHours ? 3 : 0, + ), + )); } static String convertStringToDateTime(String dateTimeString) { @@ -205,16 +203,16 @@ class Utils { _isLoadingVisible = true; showDialog( context: navigationService.navigatorKey.currentContext!, - barrierColor: Colors.black.withOpacity(0.5), + barrierColor: AppColors.blackColor, builder: (BuildContext context) => LoadingDialog(), ) .then((value) { - _isLoadingVisible = false; - }) + _isLoadingVisible = false; + }) .catchError((e) {}) .onError( (error, stackTrace) {}, - ); + ); } static void hideLoading() { @@ -224,7 +222,9 @@ class Utils { Navigator.of(navigationService.navigatorKey.currentContext!).pop(); } _isLoadingVisible = false; - } catch (e) {} + } catch (e) { + log("errr: ${e.toString()}"); + } } static List uniqueBy(List list, K Function(T) keySelector) { @@ -236,12 +236,11 @@ class Utils { showDialog( barrierDismissible: false, context: context, - builder: (cxt) => - ConfirmDialog( - title: title!, - message: message!, - onTap: onTap, - ), + builder: (cxt) => ConfirmDialog( + title: title!, + message: message!, + onTap: onTap, + ), ); } @@ -268,7 +267,7 @@ class Utils { var x = int.parse(a) * 2; var b = x.toString(); if (b.length == 1) { - b = "0" + b; + b = "0$b"; } sum += int.parse(b[0]) + int.parse(b[1]); } else { @@ -277,7 +276,9 @@ class Utils { } return sum % 10 == 0; } - } catch (err) {} + } catch (err) { + log("errr: ${err.toString()}"); + } return false; } else { return true; @@ -311,10 +312,8 @@ class Utils { } // Replace HTML line breaks with newlines - var withLineBreaks = htmlString - .replaceAll(RegExp(r'', multiLine: true), '\n') - .replaceAll(RegExp(r'<\/p>', multiLine: true), '\n') - .replaceAll(RegExp(r'', multiLine: true), '\n'); + var withLineBreaks = + htmlString.replaceAll(RegExp(r'', multiLine: true), '\n').replaceAll(RegExp(r'<\/p>', multiLine: true), '\n').replaceAll(RegExp(r'', multiLine: true), '\n'); // Remove all other HTML tags var withoutTags = withLineBreaks.replaceAll(RegExp(r'<[^>]*>'), ''); @@ -362,9 +361,8 @@ class Utils { final month = monthNames[dateTime.month - 1]; return '$day $month, $year'; - return '$day $month, $year'; } catch (e) { - print("Error formatting date: $e"); + log("Error formatting date: $e"); return ""; } } @@ -372,9 +370,7 @@ class Utils { static String formatHijriDateToDisplay(String hijriDateString) { try { // Assuming hijriDateString is in the format yyyy-MM-dd - final datePart = hijriDateString - .split("T") - .first; + final datePart = hijriDateString.split("T").first; final parts = datePart.split('-'); if (parts.length != 3) return ""; @@ -382,26 +378,13 @@ class Utils { final year = parts[0]; // Map month number to short month name (Hijri months) - const hijriMonthNames = [ - 'Muharram', - 'Safar', - 'Rabi I', - 'Rabi II', - 'Jumada I', - 'Jumada II', - 'Rajab', - 'Sha\'ban', - 'Ramadan', - 'Shawwal', - 'Dhu al-Qi\'dah', - 'Dhu al-Hijjah' - ]; + const hijriMonthNames = ['Muharram', 'Safar', 'Rabi I', 'Rabi II', 'Jumada I', 'Jumada II', 'Rajab', 'Sha\'ban', 'Ramadan', 'Shawwal', 'Dhu al-Qi\'dah', 'Dhu al-Hijjah']; final monthIndex = int.tryParse(parts[1]) ?? 1; final month = hijriMonthNames[monthIndex - 1]; return '$day $month, $year'; } catch (e) { - print("Error formatting hijri date: $e"); + log("Error formatting hijri date: $e"); return ""; } } @@ -415,7 +398,7 @@ class Utils { return '$day-$month-$year'; } catch (e) { - print("Error formatting date: $e"); + log("Error formatting date: $e"); return ""; } } @@ -432,14 +415,8 @@ class Utils { void Function(LottieComposition)? onLoaded, }) { return Lottie.asset(assetPath, - height: height ?? MediaQuery - .of(context) - .size - .height * 0.26, - width: width ?? MediaQuery - .of(context) - .size - .width, + height: height ?? MediaQuery.of(context).size.height * 0.26, + width: width ?? MediaQuery.of(context).size.width, fit: fit, alignment: alignment, repeat: repeat, @@ -499,10 +476,7 @@ class Utils { static Future isGoogleServicesAvailable() async { GooglePlayServicesAvailability availability = await GoogleApiAvailability.instance.checkGooglePlayServicesAvailability(); - String status = availability - .toString() - .split('.') - .last; + String status = availability.toString().split('.').last; if (status == "success") { return true; } @@ -523,26 +497,3 @@ class Utils { return crypto.md5.convert(utf8.encode(input)).toString(); } } - -class ValidationUtils { - static DialogService dialogService = getIt.get(); - - - static bool isValidatePhoneAndId({ - String? nationalId, - String? phoneNumber - }) { - if (nationalId == null || nationalId.isEmpty) { - dialogService.showErrorDialog(message: "Please enter a valid national ID or file number", onOkPressed: () {}); - return false; - } - - if (phoneNumber == null || phoneNumber.isEmpty) { - dialogService.showErrorDialog(message: "Please enter a valid phone number", onOkPressed: () {}); - return false; - } - return true; - } -} - - diff --git a/lib/core/utils/validation_utils.dart b/lib/core/utils/validation_utils.dart new file mode 100644 index 0000000..5472b66 --- /dev/null +++ b/lib/core/utils/validation_utils.dart @@ -0,0 +1,19 @@ +import 'package:hmg_patient_app_new/core/dependencies.dart'; +import 'package:hmg_patient_app_new/services/dialog_service.dart'; + +class ValidationUtils { + static final DialogService _dialogService = getIt.get(); + + static bool isValidatePhoneAndId({String? nationalId, String? phoneNumber}) { + if (nationalId == null || nationalId.isEmpty) { + _dialogService.showErrorDialog(message: "Please enter a valid national ID or file number", onOkPressed: () {}); + return false; + } + + if (phoneNumber == null || phoneNumber.isEmpty) { + _dialogService.showErrorDialog(message: "Please enter a valid phone number", onOkPressed: () {}); + return false; + } + return true; + } +} diff --git a/lib/features/authentication/authentication_repo.dart b/lib/features/authentication/authentication_repo.dart index 6459eff..6059331 100644 --- a/lib/features/authentication/authentication_repo.dart +++ b/lib/features/authentication/authentication_repo.dart @@ -6,7 +6,6 @@ import 'package:hmg_patient_app_new/core/api/api_client.dart'; import 'package:hmg_patient_app_new/core/api_consts.dart'; import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart'; import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart'; -import 'package:hmg_patient_app_new/features/authentication/models/request_models/check_patient_authentication_request_model.dart'; import 'package:hmg_patient_app_new/features/authentication/models/resp_models/check_activation_code_resp_model.dart'; import 'package:hmg_patient_app_new/features/authentication/models/resp_models/select_device_by_imei.dart'; import 'package:hmg_patient_app_new/services/logger_service.dart'; @@ -16,12 +15,10 @@ abstract class AuthenticationRepo { required String firebaseToken, }); - Future>> checkPatientAuthentication({ - required CheckPatientAuthenticationReq checkPatientAuthenticationReq, - }); + Future>> checkPatientAuthentication({required dynamic checkPatientAuthenticationReq}); Future>> sendActivationCodeRepo({ - required CheckPatientAuthenticationReq checkPatientAuthenticationReq, + required dynamic checkPatientAuthenticationReq, String? languageID, bool isRegister = false, }); @@ -80,7 +77,7 @@ class AuthenticationRepoImp implements AuthenticationRepo { @override Future>> checkPatientAuthentication({ - required CheckPatientAuthenticationReq checkPatientAuthenticationReq, + required dynamic checkPatientAuthenticationReq, String? languageID, }) async { int isOutKsa = (checkPatientAuthenticationReq.zipCode == '966' || checkPatientAuthenticationReq.zipCode == '+966') ? 0 : 1; @@ -96,7 +93,7 @@ class AuthenticationRepoImp implements AuthenticationRepo { GenericApiModel? apiResponse; Failure? failure; await apiClient.post( - ApiConsts.selectDeviceImei, + ApiConsts.checkPatientAuth, body: checkPatientAuthenticationReq.toJson(), onFailure: (error, statusCode, {messageStatus, failureType}) { failure = failureType; @@ -124,7 +121,7 @@ class AuthenticationRepoImp implements AuthenticationRepo { @override Future>> sendActivationCodeRepo({ - required CheckPatientAuthenticationReq checkPatientAuthenticationReq, + required dynamic checkPatientAuthenticationReq, String? languageID, bool isRegister = false, }) async { diff --git a/lib/features/authentication/authentication_view_model.dart b/lib/features/authentication/authentication_view_model.dart index 4c9de27..beb3c74 100644 --- a/lib/features/authentication/authentication_view_model.dart +++ b/lib/features/authentication/authentication_view_model.dart @@ -1,12 +1,13 @@ +import 'dart:developer'; + import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_export.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; -import 'package:hmg_patient_app_new/core/cache_consts.dart'; import 'package:hmg_patient_app_new/core/enums.dart'; +import 'package:hmg_patient_app_new/core/utils/loading_utils.dart'; import 'package:hmg_patient_app_new/core/utils/request_utils.dart'; -import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/core/utils/validation_utils.dart'; import 'package:hmg_patient_app_new/features/authentication/authentication_repo.dart'; -import 'package:hmg_patient_app_new/features/authentication/models/request_models/check_patient_authentication_request_model.dart'; import 'package:hmg_patient_app_new/features/authentication/models/resp_models/check_activation_code_resp_model.dart'; import 'package:hmg_patient_app_new/features/authentication/models/resp_models/select_device_by_imei.dart'; import 'package:hmg_patient_app_new/presentation/authentication/login.dart'; @@ -16,31 +17,28 @@ import 'package:hmg_patient_app_new/services/error_handler_service.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart'; class AuthenticationViewModel extends ChangeNotifier { - AuthenticationRepo authenticationRepo; - AppState appState; - ErrorHandlerService errorHandlerService; - DialogService dialogService; - NavigationService navigationService; - CacheService cacheService; + final AuthenticationRepo _authenticationRepo; + final AppState _appState; + final ErrorHandlerService _errorHandlerService; + final DialogService _dialogService; + final NavigationService _navigationService; AuthenticationViewModel({ - required this.appState, - required this.authenticationRepo, - required this.errorHandlerService, - required this.dialogService, - required this.navigationService, - required this.cacheService, - }); + required AppState appState, + required AuthenticationRepo authenticationRepo, + required ErrorHandlerService errorHandlerService, + required DialogService dialogService, + required NavigationService navigationService, + required CacheService cacheService, + }) : _navigationService = navigationService, + _dialogService = dialogService, + _errorHandlerService = errorHandlerService, + _appState = appState, + _authenticationRepo = authenticationRepo; final TextEditingController nationalIdController = TextEditingController(); final TextEditingController phoneNumberController = TextEditingController(); - void login() { - if (ValidationUtils.isValidatePhoneAndId(nationalId: nationalIdController.text, phoneNumber: phoneNumberController.text)) { - } else {} - } - - //checkUserAuthentication(); bool isDubai = false; bool authenticated = false; late int mobileNumber; @@ -61,106 +59,141 @@ class AuthenticationViewModel extends ChangeNotifier { late int isHijri; var healthId; + Future onLoginPressed() async { + try { + LoadingUtils.showFullScreenLoading(); + //TODO: We will remove this delay + await Future.delayed(Duration(seconds: 3)); + var data = _appState.getSelectDeviceByImeiRespModelElement; + log("Cached IMEI data: ${data?.toJson()}"); + if (data != null) { + await _handleExistingImeiData(data); + } else { + await _handleNewImeiRegistration(); + } + } catch (e) { + log("Error in onLoginPressed: $e"); + LoadingUtils.hideFullScreenLoader(); + _dialogService.showErrorDialog(message: "An unexpected error occurred. Please try again.", onOkPressed: () {}); + } + } + Future selectDeviceImei({required Function(dynamic data) onSuccess, Function(String)? onError}) async { - String firebaseToken = appState.deviceToken == "" + // LoadingUtils.showFullScreenLoading(); + String firebaseToken = _appState.deviceToken == "" ? "dOGRRszQQMGe_9wA5Hx3kO:APA91bFV5IcIJXvcCXXk0tc2ddtZgWwCPq7sGSuPr-YW7iiJpQZKgFGN9GAzCVOWL8MfheaP1slE8MdxB7lczdPBGdONQ7WbMmhgHcsUCUktq-hsapGXXqc" - : appState.deviceToken; - final result = await authenticationRepo.selectDeviceByImei(firebaseToken: firebaseToken); + : _appState.deviceToken; + final result = await _authenticationRepo.selectDeviceByImei(firebaseToken: firebaseToken); result.fold( - (failure) async => await errorHandlerService.handleError(failure: failure), + (failure) async { + // LoadingUtils.hideFullScreenLoader(); + await _errorHandlerService.handleError(failure: failure); + }, (apiResponse) { + // LoadingUtils.hideFullScreenLoader(); + log("apiResponse: ${apiResponse.data.toString()}"); + log("messageStatus: ${apiResponse.messageStatus.toString()}"); if (apiResponse.messageStatus == 1) { onSuccess(apiResponse.data); } else if (apiResponse.messageStatus == 2) { - dialogService.showErrorDialog(message: "Message Status = 2", onOkPressed: () {}); + _dialogService.showErrorDialog(message: "Message Status = 2", onOkPressed: () {}); } }, ); } - Future onLoginPressed() async { - var data = await cacheService.getObject(key: CacheConst.imeiUserData); - //TODO: Why??? - cacheService.remove(key: CacheConst.registerDataForLogin); - if (data != null) { - SelectDeviceByImeiRespModelElement savedData = SelectDeviceByImeiRespModelElement.fromJson(data); - // TODO : SavedLogin Page is not there or renamed. - // navigationService.pushPage(page: SavedLogin(savedData)); - // Navigator.of(context).push( - // MaterialPageRoute( - // builder: (BuildContext context) => SavedLogin(savedData), - // ), - // ); - } else { - // Todo: Show Loader Here - // GifLoaderDialogUtils.showMyDialog(context); - - selectDeviceImei( - onSuccess: (dynamic respData) async { - var data = await cacheService.getObject(key: CacheConst.imeiUserData); - if (respData != null) { - cacheService.saveObject(key: CacheConst.imeiUserData, value: respData); + Future _handleExistingImeiData(dynamic data) async { + try { + SelectDeviceByImeiRespModelElement? savedData = _appState.getSelectDeviceByImeiRespModelElement; + LoadingUtils.hideFullScreenLoader(); - // SelectDeviceByImeiRespModelElement savedData = SelectDeviceByImeiRespModelElement.fromJson(data); - // setUserValues(value); - // TODO : SavedLogin Page is not there or renamed. - // navigationService.pushPage(page: SavedLogin(savedData)); - // Navigator.of(context).push( - // MaterialPageRoute( - // builder: (BuildContext context) => SavedLogin(savedData), - // ), - // ); - } else { - //Todo: Hide Loader Here - // GifLoaderDialogUtils.hideDialog(context); - navigationService.pushPage(page: LoginScreen()); - // Navigator.of(context).push( - // MaterialPageRoute( - // builder: (BuildContext context) => WelcomeLogin(), - // ), - // ); - } - }, - ); + if (savedData != null) { + // TODO: Navigate to SavedLogin when available + _navigationService.pushPage(page: LoginScreen()); + // navigationService.pushPage(page: SavedLogin(savedData)); + } + } catch (e) { + log("Error handling existing IMEI data: $e"); + LoadingUtils.hideFullScreenLoader(); + _navigationService.pushPage(page: LoginScreen()); } } + Future _handleNewImeiRegistration() async { + await selectDeviceImei(onSuccess: (dynamic respData) async { + try { + if (respData != null) { + dynamic data = SelectDeviceByImeiRespModelElement.fromJson(respData.toJson()); + _appState.setSelectDeviceByImeiRespModelElement(data); + LoadingUtils.hideFullScreenLoader(); + + // TODO: Navigate to SavedLogin when available + // SelectDeviceByImeiRespModelElement savedData = + // SelectDeviceByImeiRespModelElement.fromJson(respData); + // navigationService.pushPage(page: SavedLogin(savedData)); + _navigationService.pushPage(page: LoginScreen()); + } else { + LoadingUtils.hideFullScreenLoader(); + _navigationService.pushPage(page: LoginScreen()); + } + } catch (e) { + log("Error processing IMEI registration response: $e"); + LoadingUtils.hideFullScreenLoader(); + _navigationService.pushPage(page: LoginScreen()); + } + }, onError: (String error) { + LoadingUtils.hideFullScreenLoader(); + _dialogService.showErrorDialog(message: error, onOkPressed: () {}); + }); + } + Future checkUserAuthentication({required OTPTypeEnum otpTypeEnum, Function(dynamic)? onSuccess, Function(String)? onError}) async { - CheckPatientAuthenticationReq checkPatientAuthenticationReq = RequestUtils.getCommonRequestWelcome( - phoneNumber: '0567184134', - otpTypeEnum: OTPTypeEnum.sms, + // TODO: THIS SHOULD BE REMOVED LATER ON AND PASSED FROM APP STATE DIRECTLY INTO API CLIENT. BECAUSE THIS API ONLY NEEDS FEW PARAMS FROM USER + + bool isValidated = ValidationUtils.isValidatePhoneAndId( + phoneNumber: phoneNumberController.text, + nationalId: nationalIdController.text, + ); + if (!isValidated) return; + + dynamic checkPatientAuthenticationReq = RequestUtils.getCommonRequestWelcome( + phoneNumber: phoneNumberController.text, + nationIdText: nationalIdController.text, + otpTypeEnum: otpTypeEnum, deviceToken: 'dummyDeviceToken123', patientOutSA: true, loginTokenID: 'dummyLoginToken456', registeredData: null, patientId: 12345, - nationIdText: '1234567890', countryCode: 'SA', ); - final result = await authenticationRepo.checkPatientAuthentication(checkPatientAuthenticationReq: checkPatientAuthenticationReq); + final result = await _authenticationRepo.checkPatientAuthentication(checkPatientAuthenticationReq: checkPatientAuthenticationReq); result.fold( - (failure) async => await errorHandlerService.handleError(failure: failure), + (failure) async => await _errorHandlerService.handleError(failure: failure), (apiResponse) { if (apiResponse.data['isSMSSent']) { // TODO: set this in AppState + _appState.setAppLoginToken = apiResponse.data['LogInTokenID']; + // sharedPref.setString(LOGIN_TOKEN_ID, value['LogInTokenID']); // loginTokenID = value['LogInTokenID'], // sharedPref.setObject(REGISTER_DATA_FOR_LOGIIN, request), sendActivationCode(otpTypeEnum: otpTypeEnum); } else { if (apiResponse.data['IsAuthenticated']) { - checkActivationCode(onWrongActivationCode: (String? message) {}, activationCode: 0000); + checkActivationCode( + onWrongActivationCode: (String? message) {}, + activationCode: 0000, + ); } } }, ); } - Future sendActivationCode({ - required OTPTypeEnum otpTypeEnum, - }) async { + Future sendActivationCode({required OTPTypeEnum otpTypeEnum}) async { var request = RequestUtils.getCommonRequestAuthProvider( otpTypeEnum: otpTypeEnum, registeredData: null, @@ -190,13 +223,13 @@ class AuthenticationViewModel extends ChangeNotifier { request.isHijri = 0; } - final resultEither = await authenticationRepo.sendActivationCodeRepo( + final resultEither = await _authenticationRepo.sendActivationCodeRepo( checkPatientAuthenticationReq: request, isRegister: isForRegister, languageID: 'er', ); resultEither.fold( - (failure) async => await errorHandlerService.handleError(failure: failure), + (failure) async => await _errorHandlerService.handleError(failure: failure), (apiResponse) { if (apiResponse.data != null && apiResponse.data['isSMSSent'] == true) { // startSMSService(otpTypeEnum.toInt()); @@ -229,30 +262,30 @@ class AuthenticationViewModel extends ChangeNotifier { request['HealthId'] = healthId; request['IsHijri'] = isHijri; - final resultEither = await authenticationRepo.checkActivationCodeRepo( + final resultEither = await _authenticationRepo.checkActivationCodeRepo( newRequest: request, activationCode: activationCode.toString(), isRegister: true, ); res = resultEither; - resultEither.fold((failure) async => await errorHandlerService.handleError(failure: failure), (apiResponse) { + resultEither.fold((failure) async => await _errorHandlerService.handleError(failure: failure), (apiResponse) { final activation = CheckActivationCode.fromJson(apiResponse.data as Map); if (registerd_data?.isRegister == true) { - navigationService.popUntilNamed(AppRoutes.registerNewScreen); + _navigationService.popUntilNamed(AppRoutes.registerNewScreen); // Navigator.popUntil(context, (route) => Utils.route(route, equalsTo: RegisterNew)); return; } }); } else { - final resultEither = await authenticationRepo.checkActivationCodeRepo( + final resultEither = await _authenticationRepo.checkActivationCodeRepo( newRequest: request, activationCode: activationCode.toString(), isRegister: false, ); res = resultEither; - resultEither.fold((failure) async => await errorHandlerService.handleError(failure: failure), (apiResponse) { + resultEither.fold((failure) async => await _errorHandlerService.handleError(failure: failure), (apiResponse) { final activation = CheckActivationCode.fromJson(resultEither as Map); if (activation.errorCode == '699') { // Todo: Hide Loader @@ -260,7 +293,7 @@ class AuthenticationViewModel extends ChangeNotifier { onWrongActivationCode(activation.errorEndUserMessage); return; } else if (registerd_data?.isRegister == true) { - navigationService.popUntilNamed(AppRoutes.registerNewScreen); + _navigationService.popUntilNamed(AppRoutes.registerNewScreen); // Navigator.popUntil(context, (route) => Utils.route(route, equalsTo: RegisterNew)); return; } else { @@ -284,7 +317,7 @@ class AuthenticationViewModel extends ChangeNotifier { } Future navigateToOTPScreen({required OTPTypeEnum otpTypeEnum, required String phoneNumber}) async { - navigationService.pushToOtpScreen( + _navigationService.pushToOtpScreen( phoneNumber: phoneNumber, checkActivationCode: (int activationCode) async { await checkActivationCode( @@ -300,4 +333,12 @@ class AuthenticationViewModel extends ChangeNotifier { Future onWrongActivationCode({String? message}) async { // TODO: HANDLE THIS VIA BOTTOM SHEET } + + @override + void dispose() { + nationalIdController.dispose(); + phoneNumberController.dispose(); + myFocusNode.dispose(); + super.dispose(); + } } diff --git a/lib/presentation/authentication/login.dart b/lib/presentation/authentication/login.dart index 2771b36..3c8fa15 100644 --- a/lib/presentation/authentication/login.dart +++ b/lib/presentation/authentication/login.dart @@ -2,6 +2,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/enums.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/context_extensions.dart'; @@ -18,6 +19,8 @@ import 'package:hmg_patient_app_new/widgets/input_widget.dart'; import 'package:provider/provider.dart'; class LoginScreen extends StatefulWidget { + const LoginScreen({super.key}); + @override _LoginScreen createState() => _LoginScreen(); } @@ -83,7 +86,7 @@ class _LoginScreen extends State { icon: AppAssets.login1, iconColor: Colors.white, onPressed: () { - showLoginModel(context: context, textController: authVm.phoneNumberController); + showLoginModelSheet(context: context, textController: authVm.phoneNumberController, authViewModel: authVm); // if (nationIdController.text.isNotEmpty) { // } else { @@ -140,12 +143,16 @@ class _LoginScreen extends State { ); } - void showLoginModel({required BuildContext context, TextEditingController? textController}) { + void showLoginModelSheet({ + required BuildContext context, + required TextEditingController? textController, + required AuthenticationViewModel authViewModel, + }) { context.showBottomSheet( isScrollControlled: true, isDismissible: false, useSafeArea: true, - backgroundColor: Colors.transparent, + backgroundColor: AppColors.transparent, child: StatefulBuilder(builder: (BuildContext context, StateSetter setModalState) { return Padding( padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), @@ -162,7 +169,9 @@ class _LoginScreen extends State { padding: EdgeInsets.only(bottom: 10.h), child: CustomButton( text: LocaleKeys.sendOTPSMS.tr(), - onPressed: () {}, + onPressed: () async { + await authViewModel.checkUserAuthentication(otpTypeEnum: OTPTypeEnum.sms); + }, backgroundColor: AppColors.primaryRedColor, borderColor: AppColors.primaryRedBorderColor, textColor: AppColors.whiteColor, @@ -183,7 +192,9 @@ class _LoginScreen extends State { padding: EdgeInsets.only(bottom: 10.h, top: 10.h), child: CustomButton( text: LocaleKeys.sendOTPWHATSAPP.tr(), - onPressed: () {}, + onPressed: () async { + await authViewModel.checkUserAuthentication(otpTypeEnum: OTPTypeEnum.whatsapp); + }, backgroundColor: Colors.white, borderColor: AppColors.borderOnlyColor, textColor: AppColors.textColor, diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart index 972958b..0710d7c 100644 --- a/lib/presentation/home/landing_page.dart +++ b/lib/presentation/home/landing_page.dart @@ -11,13 +11,13 @@ 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/authentication/authentication_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; -import 'package:hmg_patient_app_new/presentation/authentication/login.dart'; import 'package:hmg_patient_app_new/presentation/home/data/landing_page_data.dart'; import 'package:hmg_patient_app_new/presentation/home/widgets/habib_wallet_card.dart'; import 'package:hmg_patient_app_new/presentation/home/widgets/large_service_card.dart'; import 'package:hmg_patient_app_new/presentation/home/widgets/small_service_card.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/medical_file_page.dart'; import 'package:hmg_patient_app_new/providers/bottom_navigation_provider.dart'; +import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; @@ -34,6 +34,7 @@ class _LandingPageState extends State { @override Widget build(BuildContext context) { AppState appState = getIt.get(); + NavigationService navigationService = getIt.get(); final AuthenticationViewModel authenticationViewModel = context.read(); return Consumer(builder: (context, navigationProvider, child) { return Scaffold( @@ -51,8 +52,7 @@ class _LandingPageState extends State { CustomButton( text: LocaleKeys.loginOrRegister.tr(context: context), onPressed: () async { - // await authenticationViewModel.selectDeviceImei(); - Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) => LoginScreen())); + await authenticationViewModel.onLoginPressed(); }, backgroundColor: Color(0xffFEE9EA), borderColor: Color(0xffFEE9EA), diff --git a/lib/services/error_handler_service.dart b/lib/services/error_handler_service.dart index a26d264..344e59e 100644 --- a/lib/services/error_handler_service.dart +++ b/lib/services/error_handler_service.dart @@ -45,6 +45,7 @@ class ErrorHandlerServiceImp implements ErrorHandlerService { await _showDialog(failure, title: "Unknown Error"); } else { loggerService.errorLogs("Unhandled failure type: $failure"); + await _showDialog(failure, title: "Error"); } } diff --git a/pubspec.lock b/pubspec.lock index 679a57e..a6bf33b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -654,6 +654,14 @@ packages: url: "https://pub.dev" source: hosted version: "8.2.0" + gif_view: + dependency: "direct main" + description: + name: gif_view + sha256: "4c7e17c134719531dabab54af121e4600d63283f56f3aff57c16db54766b67bc" + url: "https://pub.dev" + source: hosted + version: "1.0.3" google_api_availability: dependency: "direct main" description: @@ -874,26 +882,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + sha256: "8dcda04c3fc16c14f48a7bb586d4be1f0d1572731b6d81d51772ef47c02081e0" url: "https://pub.dev" source: hosted - version: "10.0.9" + version: "11.0.1" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.10" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" lints: dependency: transitive description: @@ -1455,10 +1463,10 @@ packages: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.6" time: dependency: transitive description: @@ -1583,10 +1591,10 @@ packages: dependency: transitive description: name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.0" vm_service: dependency: transitive description: @@ -1636,5 +1644,5 @@ packages: source: hosted version: "6.5.0" sdks: - dart: ">=3.7.0 <4.0.0" + dart: ">=3.8.0-0 <4.0.0" flutter: ">=3.29.0" diff --git a/pubspec.yaml b/pubspec.yaml index 4c2b81f..9c78b48 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -64,6 +64,7 @@ dependencies: equatable: ^2.0.7 google_api_availability: ^5.0.1 firebase_analytics: ^11.5.1 + gif_view: ^1.0.3 web: any flutter_staggered_animations: ^1.1.1