diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index f531524..432ad2a 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -1,10 +1,9 @@ plugins { id("com.android.application") id("kotlin-android") - // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. id("dev.flutter.flutter-gradle-plugin") id("com.google.gms.google-services") - id("com.huawei.agconnect") +// id("com.huawei.agconnect") } android { diff --git a/android/build.gradle.kts b/android/build.gradle.kts index 89176ef..71a2e60 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -1,7 +1,21 @@ +buildscript { + repositories { + google() + mavenCentral() +// maven { url = uri("https://developer.huawei.com/repo/") } + } + dependencies { + classpath("com.android.tools.build:gradle:7.4.2") + classpath("com.google.gms:google-services:4.4.1") +// classpath("com.huawei.agconnect:agcp:1.9.3.302") + } +} + allprojects { repositories { google() mavenCentral() +// maven { url = uri("https://developer.huawei.com/repo/") } } } diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist index 7c56964..1dc6cf7 100644 --- a/ios/Flutter/AppFrameworkInfo.plist +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 12.0 + 13.0 diff --git a/ios/Podfile b/ios/Podfile index 6010472..9249e54 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -99,4 +99,4 @@ post_integrate do |installer| end end project.save() -end \ No newline at end of file +end diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 3f36518..dae81f2 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -279,10 +279,14 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", ); + inputPaths = ( + ); name = "[CP] Copy Pods Resources"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", ); + outputPaths = ( + ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; @@ -296,10 +300,14 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); + inputPaths = ( + ); name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); + outputPaths = ( + ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; @@ -435,7 +443,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -451,7 +459,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = 2; - DEVELOPMENT_TEAM = 3A359E86ZF; + DEVELOPMENT_TEAM = ZB3P5B74MA; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.6; @@ -564,7 +572,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -615,7 +623,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -633,7 +641,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = 2; - DEVELOPMENT_TEAM = 3A359E86ZF; + DEVELOPMENT_TEAM = ZB3P5B74MA; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.6; @@ -658,7 +666,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = 2; - DEVELOPMENT_TEAM = 3A359E86ZF; + DEVELOPMENT_TEAM = ZB3P5B74MA; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.6; diff --git a/lib/services/api_client.dart b/lib/core/api/api_client.dart similarity index 86% rename from lib/services/api_client.dart rename to lib/core/api/api_client.dart index 42ffc58..ef15c95 100644 --- a/lib/services/api_client.dart +++ b/lib/core/api/api_client.dart @@ -3,14 +3,15 @@ import 'dart:convert'; import 'dart:io'; import 'package:flutter/foundation.dart'; -import 'package:hmg_patient_app_new/services/api_exception.dart'; +import 'package:hmg_patient_app_new/core/app_state.dart'; +import 'package:hmg_patient_app_new/core/consts.dart'; +import 'package:hmg_patient_app_new/core/dependencies.dart'; +import 'package:hmg_patient_app_new/core/exceptions/api_exception.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/services/logger_service.dart'; import 'package:http/http.dart'; import 'package:http/io_client.dart'; -import '../core/app_state.dart'; -import '../core/consts.dart'; -import '../core/utils/utils.dart'; -import '../main.dart'; // ignore_for_file: avoid_annotating_with_dynamic @@ -31,12 +32,16 @@ class APIError { } APIException _throwAPIException(Response response, Function retryCallBack) { + + +LoggerService loggerService = getIt.get(); + switch (response.statusCode) { case 200: APIError? apiError; if (response.body != null && response.body.isNotEmpty) { var jsonError = jsonDecode(response.body); - print(jsonError); + loggerService.logInfo(jsonError.toString()); apiError = APIError(jsonError['ErrorCode'], jsonError['ErrorMessage']); } return APIException(APIException.BAD_REQUEST, error: apiError); @@ -75,11 +80,9 @@ abstract class IApiClient { } class ApiClient implements IApiClient { - // static final ApiClient _instance = ApiClient._internal(); - - // ApiClient._internal(); + LoggerService loggerService; - // factory ApiClient() => _instance; + ApiClient({required this.loggerService}); @override Future postJsonForObject(FactoryConstructor factoryConstructor, String url, T jsonObject, @@ -89,18 +92,18 @@ class ApiClient implements IApiClient { _headers.addAll(headers); } if (!kReleaseMode) { - print("Url:$url"); + loggerService.logInfo("Url:$url"); var bodyJson = json.encode(jsonObject); - print("body:$bodyJson"); + loggerService.logInfo("body:$bodyJson"); } var response = await postJsonForResponse(url, jsonObject, token: token, queryParameters: queryParameters, headers: _headers, retryTimes: retryTimes); // try { if (!kReleaseMode) { - logger.i("res: " + response.body); + loggerService.logInfo("res: ${response.body}"); } var jsonData = jsonDecode(response.body); if (jsonData["IsAuthenticated"] != null) { - AppState().setIsAuthenticated = jsonData["IsAuthenticated"]; + getIt.get().setIsAuthenticated = jsonData["IsAuthenticated"]; } if (jsonData["ErrorMessage"] == null) { return factoryConstructor(jsonData); @@ -146,16 +149,16 @@ class ApiClient implements IApiClient { } if (queryParameters != null) { // var queryString = new Uri(queryParameters: queryParameters).query; - var queryString = Uri(queryParameters: queryParameters.map((key, value) => MapEntry(key, value == null ? null : value.toString()))).query; - url = url + '?' + queryString; + var queryString = Uri(queryParameters: queryParameters.map((key, value) => MapEntry(key, value?.toString()))).query; + url = '$url?$queryString'; } // if (!kReleaseMode && url.contains("saned")) { if (!kReleaseMode) { - print("Url: $url"); - print("Headers: $_headers"); + loggerService.logInfo("Url: $url"); + loggerService.logInfo("Headers: $_headers"); // var bodyJson = json.encode(requestBody); - print("body: $requestBody"); + loggerService.logInfo("body: $requestBody"); } var response = await _post(Uri.parse(url), body: requestBody, headers: _headers).timeout(Duration(seconds: 120)); @@ -170,7 +173,7 @@ class ApiClient implements IApiClient { } } on SocketException catch (e) { if (retryTimes > 0) { - print('will retry after 3 seconds...'); + loggerService.logInfo('will retry after 3 seconds...'); await Future.delayed(Duration(seconds: 3)); return await _postForResponse(url, requestBody, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes - 1); } else { @@ -178,7 +181,7 @@ class ApiClient implements IApiClient { } } on HttpException catch (e) { if (retryTimes > 0) { - print('will retry after 3 seconds...'); + loggerService.logInfo('will retry after 3 seconds...'); await Future.delayed(Duration(seconds: 3)); return await _postForResponse(url, requestBody, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes - 1); } else { @@ -188,7 +191,7 @@ class ApiClient implements IApiClient { throw APIException(APIException.TIMEOUT, arguments: e); } on ClientException catch (e) { if (retryTimes > 0) { - print('will retry after 3 seconds...'); + loggerService.logInfo('will retry after 3 seconds...'); await Future.delayed(Duration(seconds: 3)); return await _postForResponse(url, requestBody, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes - 1); } else { @@ -199,7 +202,7 @@ class ApiClient implements IApiClient { @override Future getJsonForResponse(String url, {String? token, Map? queryParameters, Map? headers, int retryTimes = 0, bool isAuthAPI = false}) async { - logger.i("Url:$url"); + loggerService.logInfo("Url:$url"); if (headers == null) { headers = {'Content-Type': 'application/json'}; } else { @@ -225,8 +228,8 @@ class ApiClient implements IApiClient { } if (queryParameters != null) { - var queryString = new Uri(queryParameters: queryParameters).query; - url = url + '?' + queryString; + var queryString = Uri(queryParameters: queryParameters).query; + url = '$url?$queryString'; } var response = await _get(Uri.parse(url), headers: _headers).timeout(Duration(seconds: 60)); @@ -239,7 +242,7 @@ class ApiClient implements IApiClient { } } on SocketException catch (e) { if (retryTimes > 0) { - print('will retry after 3 seconds...'); + loggerService.logInfo('will retry after 3 seconds...'); await Future.delayed(Duration(seconds: 3)); return await _getForResponse(url, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes - 1); } else { @@ -247,7 +250,7 @@ class ApiClient implements IApiClient { } } on HttpException catch (e) { if (retryTimes > 0) { - print('will retry after 3 seconds...'); + loggerService.logInfo('will retry after 3 seconds...'); await Future.delayed(Duration(seconds: 3)); return await _getForResponse(url, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes - 1); } else { @@ -257,7 +260,7 @@ class ApiClient implements IApiClient { throw APIException(APIException.TIMEOUT, arguments: e); } on ClientException catch (e) { if (retryTimes > 0) { - print('will retry after 3 seconds...'); + loggerService.logInfo('will retry after 3 seconds...'); await Future.delayed(Duration(seconds: 3)); return await _getForResponse(url, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes - 1); } else { @@ -282,7 +285,6 @@ class ApiClient implements IApiClient { Future _post(url, {Map? headers, body, Encoding? encoding}) => _withClient((client) => client.post(url, headers: headers, body: body, encoding: encoding)); - Future _put(url, {Map? headers, body, Encoding? encoding}) => _withClient((client) => client.put(url, headers: headers, body: body, encoding: encoding)); @override void setHomeUrl(String url) { diff --git a/lib/core/app_state.dart b/lib/core/app_state.dart index 4bed676..684c600 100644 --- a/lib/core/app_state.dart +++ b/lib/core/app_state.dart @@ -5,14 +5,10 @@ import 'package:hmg_patient_app_new/main.dart'; import 'consts.dart'; class AppState { - static final AppState _instance = AppState._internal(); - - AppState._internal(); - - factory AppState() => _instance; + // Simple constructor - let get_it handle the singleton behavior + AppState(); //Tokens - bool isAuthenticated = false; set setIsAuthenticated(v) => isAuthenticated = v; diff --git a/lib/core/dependencies.dart b/lib/core/dependencies.dart index 4b3b0d6..82e3fe7 100644 --- a/lib/core/dependencies.dart +++ b/lib/core/dependencies.dart @@ -1,9 +1,31 @@ +import 'package:get_it/get_it.dart'; +import 'package:hmg_patient_app_new/core/api/api_client.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; -import 'package:injector/injector.dart'; +import 'package:hmg_patient_app_new/features/authentication/authentication_repo.dart'; +import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_repo.dart'; +import 'package:hmg_patient_app_new/features/common/common_repo.dart'; +import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_repo.dart'; +import 'package:hmg_patient_app_new/services/cache_service.dart'; +import 'package:hmg_patient_app_new/services/logger_service.dart'; +import 'package:logger/web.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +GetIt getIt = GetIt.instance; class AppDependencies { - static void addDependencies() { - Injector injector = Injector.appInstance; - injector.registerSingleton(() => AppState()); + static Future addDependencies() async { + // Services + getIt.registerLazySingleton(() => LoggerServiceImp(logger: Logger(printer: PrettyPrinter(lineLength: 0)))); + final sharedPreferences = await SharedPreferences.getInstance(); + getIt.registerLazySingleton(() => CacheServiceImp(sharedPreferences: sharedPreferences)); + getIt.registerSingleton(AppState()); + getIt.registerLazySingleton(() => ApiClient(loggerService: getIt())); + + // Repositories + getIt.registerLazySingleton(() => CommonRepoImp(loggerService: getIt())); + getIt.registerLazySingleton(() => AuthenticationRepoImp(loggerService: getIt(), apiClient: getIt())); + getIt.registerLazySingleton(() => BookAppointmentsRepoImp(loggerService: getIt(), apiClient: getIt())); + getIt.registerLazySingleton(() => MyAppointmentsRepoImp(loggerService: getIt(), apiClient: getIt())); + } } diff --git a/lib/core/enums.dart b/lib/core/enums.dart index 95efb6c..db758f3 100644 --- a/lib/core/enums.dart +++ b/lib/core/enums.dart @@ -23,9 +23,9 @@ enum ViewState { } enum LoginType { - FROM_LOGIN, - SILENT_LOGIN, - SILENT_WITH_OTP, + fromLogin, + silentLogin, + silentWithOTP, } enum OTPType { sms, whatsapp } diff --git a/lib/services/api_exception.dart b/lib/core/exceptions/api_exception.dart similarity index 90% rename from lib/services/api_exception.dart rename to lib/core/exceptions/api_exception.dart index f0f1ff9..5076cc3 100644 --- a/lib/services/api_exception.dart +++ b/lib/core/exceptions/api_exception.dart @@ -1,6 +1,7 @@ import 'dart:convert'; -import 'package:hmg_patient_app_new/services/api_client.dart'; +import 'package:equatable/equatable.dart'; +import 'package:hmg_patient_app_new/core/api/api_client.dart'; class APIException implements Exception { static const String BAD_REQUEST = 'api_common_bad_request'; @@ -27,3 +28,4 @@ class APIException implements Exception { return jsonEncode(this); } } + diff --git a/lib/core/exceptions/api_failure.dart b/lib/core/exceptions/api_failure.dart new file mode 100644 index 0000000..eaa434e --- /dev/null +++ b/lib/core/exceptions/api_failure.dart @@ -0,0 +1,42 @@ + +import 'package:equatable/equatable.dart'; + +abstract class Failure extends Equatable implements Exception { + final String message; + const Failure(this.message); +} + +class ServerFailure extends Failure { + const ServerFailure(super.message); + + @override + List get props => [message]; +} + +class ConnectivityFailure extends Failure { + const ConnectivityFailure(super.message); + + @override + List get props => [message]; +} + +class LocalStorageFailure extends Failure { + const LocalStorageFailure(super.message); + + @override + List get props => [message]; +} + +class DuplicateUsername extends Failure { + const DuplicateUsername({String? message}) : super(message ?? ''); + + @override + List get props => [message]; +} + +class InvalidCredentials extends Failure { + const InvalidCredentials({String? message}) : super(message ?? ''); + + @override + List get props => [message]; +} diff --git a/lib/core/post_params_model.dart b/lib/core/post_params_model.dart index 5eed9bb..218f29f 100644 --- a/lib/core/post_params_model.dart +++ b/lib/core/post_params_model.dart @@ -25,32 +25,32 @@ class PostParamsModel { } Map toJson() { - Map data = new Map(); - data['versionID'] = this.versionID; - data['channel'] = this.channel; - data['languageID'] = this.languageID; - data['logInTokenID'] = this.logInTokenID ?? ""; - data['tokenID'] = this.tokenID ?? ""; + Map data = {}; + data['versionID'] = versionID; + data['channel'] = channel; + data['languageID'] = languageID; + data['logInTokenID'] = logInTokenID ?? ""; + data['tokenID'] = tokenID ?? ""; return data; } Map toJsonAfterLogin() { - Map data = new Map(); - data['versionID'] = this.versionID; - data['channel'] = this.channel; - data['languageID'] = this.languageID; - data['logInTokenID'] = this.logInTokenID; - data['tokenID'] = this.tokenID; + Map data = {}; + data['versionID'] = versionID; + data['channel'] = channel; + data['languageID'] = languageID; + data['logInTokenID'] = logInTokenID; + data['tokenID'] = tokenID; return data; } - String? _LogInTokenID; + String? _logInTokenID; - String? get getLogInTokenID => _LogInTokenID ?? logInTokenID; + String? get getLogInTokenID => _logInTokenID ?? logInTokenID; set setLogInTokenID(String? value) { logInTokenID = value; - _LogInTokenID = value; + _logInTokenID = value; } set setTokenID(String? token) => tokenID = token; diff --git a/lib/extensions/route_extensions.dart b/lib/extensions/route_extensions.dart new file mode 100644 index 0000000..71f5d2d --- /dev/null +++ b/lib/extensions/route_extensions.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; + +extension NavigationExtensions on BuildContext { + void navigateWithName(String routeName, {Object? arguments}) { + Navigator.pushNamed(this, routeName, arguments: arguments); + } + + Future navigateReplaceWithName(String routeName, {Object? arguments}) async { + await Navigator.pushReplacementNamed(this, routeName, arguments: arguments); + } + + void navigateReplaceWithNameUntilRoute(String routeName, {Object? arguments}) { + Navigator.pushNamedAndRemoveUntil(this, routeName, (route) => false); + } + + void pop() { + Navigator.of(this).pop(); + } + + void navigateTo(Widget page) { + Navigator.push(this, MaterialPageRoute(builder: (context) => page)); + } +} diff --git a/lib/features/authentication/authentication_repo.dart b/lib/features/authentication/authentication_repo.dart new file mode 100644 index 0000000..a5e9ae3 --- /dev/null +++ b/lib/features/authentication/authentication_repo.dart @@ -0,0 +1,44 @@ +import 'package:dartz/dartz.dart'; +import 'package:hmg_patient_app_new/core/api/api_client.dart'; +import 'package:hmg_patient_app_new/core/exceptions/api_exception.dart'; +import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart'; +import 'package:hmg_patient_app_new/features/authentication/models/select_device_by_imei.dart'; +import 'package:hmg_patient_app_new/services/logger_service.dart'; +import 'package:http/http.dart'; + +abstract class AuthenticationRepo { + Future> selectDeviceByImei({required String deviceIMEI}); +} + +class AuthenticationRepoImp implements AuthenticationRepo { + final ApiClient apiClient; + final LoggerService loggerService; + + AuthenticationRepoImp({required this.loggerService, required this.apiClient}); + + @override + Future> selectDeviceByImei({required String deviceIMEI}) async { + final mapDevice = {"": deviceIMEI}; + + final String apiUrl = "https://hmgwebservices.com/Services/Patients.svc/REST/Patient_SELECTDeviceIMEIbyIMEI" ; + try { + final Response result = await apiClient.postJsonForResponse(apiUrl, mapDevice); + if (result !=null) { + return Right(null); + } else { + loggerService.errorLogs(result.toString()); + return Left(ServerFailure(result.toString())); + } + } on APIException catch (e) { + APIError? apiError; + if (e.error is APIError) { + apiError = e.error as APIError; + } + loggerService.errorLogs(e.toString()); + return Left(ServerFailure(apiError?.errorMessage ?? e.message)); + } catch (e) { + loggerService.errorLogs(e.toString()); + return Left(ServerFailure(e.toString())); + } + } +} diff --git a/lib/features/authentication/authentication_view_model.dart b/lib/features/authentication/authentication_view_model.dart new file mode 100644 index 0000000..5b45790 --- /dev/null +++ b/lib/features/authentication/authentication_view_model.dart @@ -0,0 +1,47 @@ +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/features/authentication/authentication_repo.dart'; + +class AuthenticationViewModel extends ChangeNotifier { + AuthenticationRepo authenticationRepo; + + AuthenticationViewModel({required this.authenticationRepo}); + + + + Future signUp({ + required String phone, + required String password, + Function(dynamic)? onSuccess, + Function(String)? onError, + }) async { + Utils.showLoading(); + final String deviceIMEI = + "cIkkB7h7Q7uoFkC4Qv82xG:APA91bEb53Z9XzqymCIctaLxCoMX6bm9fuKlWILQ59uUqfwhCoD42AOP1-jWGB1WYd9BVN5PT2pUUFxrT07vcNg1KH9OH39mrPgCl0m21XVIgWrzNnCkufg"; + + final resultEither = await authenticationRepo.selectDeviceByImei(deviceIMEI: deviceIMEI); + + if (resultEither.isLeft()) { + + } + + if (resultEither.isRight()) { + + + } + + + resultEither.fold( + (failure) { + Utils.hideLoading(); + notifyListeners(); + if (onError != null) onError(failure.message); + }, + (data) { + + notifyListeners(); + if (onSuccess != null) onSuccess(data); + }, + ); + } +} diff --git a/lib/services/authentication/models/response_models/get_patient_last_login_details_response_model.dart b/lib/features/authentication/models/get_patient_last_login_details_response_model.dart similarity index 67% rename from lib/services/authentication/models/response_models/get_patient_last_login_details_response_model.dart rename to lib/features/authentication/models/get_patient_last_login_details_response_model.dart index a14da5e..ce48dd4 100644 --- a/lib/services/authentication/models/response_models/get_patient_last_login_details_response_model.dart +++ b/lib/features/authentication/models/get_patient_last_login_details_response_model.dart @@ -48,21 +48,21 @@ class GetPatientLastLoginDetailsResponseModel { } Map toJson() { - final Map data = new Map(); - data['ID'] = this.iD; - data['IMEI'] = this.iMEI; - data['LogInType'] = this.logInType; - data['PatientID'] = this.patientID; - data['OutSA'] = this.outSA; - data['Mobile'] = this.mobile; - data['IdentificationNo'] = this.identificationNo; - data['Name'] = this.name; - data['NameN'] = this.nameN; - data['CreatedOn'] = this.createdOn; - data['EditedOn'] = this.editedOn; - data['BiometricEnabled'] = this.biometricEnabled; - data['PatientType'] = this.patientType; - data['PreferredLanguage'] = this.preferredLanguage; + final Map data = {}; + data['ID'] = iD; + data['IMEI'] = iMEI; + data['LogInType'] = logInType; + data['PatientID'] = patientID; + data['OutSA'] = outSA; + data['Mobile'] = mobile; + data['IdentificationNo'] = identificationNo; + data['Name'] = name; + data['NameN'] = nameN; + data['CreatedOn'] = createdOn; + data['EditedOn'] = editedOn; + data['BiometricEnabled'] = biometricEnabled; + data['PatientType'] = patientType; + data['PreferredLanguage'] = preferredLanguage; return data; } } diff --git a/lib/features/authentication/models/select_device_by_imei.dart b/lib/features/authentication/models/select_device_by_imei.dart new file mode 100644 index 0000000..2ab669e --- /dev/null +++ b/lib/features/authentication/models/select_device_by_imei.dart @@ -0,0 +1,77 @@ +// To parse this JSON data, do +// +// final selectDeviceByImeiRespModel = selectDeviceByImeiRespModelFromJson(jsonString); + +import 'dart:convert'; + +Map selectDeviceByImeiRespModelFromJson(String str) => Map.from(json.decode(str)).map((k, v) => MapEntry(k, v)); + +String selectDeviceByImeiRespModelToJson(Map data) => json.encode(Map.from(data).map((k, v) => MapEntry(k, v))); + +class SelectDeviceByImeiRespModelElement { + int id; + String imei; + int logInType; + int patientId; + bool outSa; + String mobile; + String identificationNo; + String name; + String nameN; + String createdOn; + String editedOn; + bool biometricEnabled; + int patientType; + int preferredLanguage; + + SelectDeviceByImeiRespModelElement({ + required this.id, + required this.imei, + required this.logInType, + required this.patientId, + required this.outSa, + required this.mobile, + required this.identificationNo, + required this.name, + required this.nameN, + required this.createdOn, + required this.editedOn, + required this.biometricEnabled, + required this.patientType, + required this.preferredLanguage, + }); + + factory SelectDeviceByImeiRespModelElement.fromJson(Map json) => SelectDeviceByImeiRespModelElement( + id: json["ID"], + imei: json["IMEI"], + logInType: json["LogInType"], + patientId: json["PatientID"], + outSa: json["OutSA"], + mobile: json["Mobile"], + identificationNo: json["IdentificationNo"], + name: json["Name"], + nameN: json["NameN"], + createdOn: json["CreatedOn"], + editedOn: json["EditedOn"], + biometricEnabled: json["BiometricEnabled"], + patientType: json["PatientType"], + preferredLanguage: json["PreferredLanguage"], + ); + + Map toJson() => { + "ID": id, + "IMEI": imei, + "LogInType": logInType, + "PatientID": patientId, + "OutSA": outSa, + "Mobile": mobile, + "IdentificationNo": identificationNo, + "Name": name, + "NameN": nameN, + "CreatedOn": createdOn, + "EditedOn": editedOn, + "BiometricEnabled": biometricEnabled, + "PatientType": patientType, + "PreferredLanguage": preferredLanguage, + }; +} diff --git a/lib/features/book_appointments/book_appointments_repo.dart b/lib/features/book_appointments/book_appointments_repo.dart new file mode 100644 index 0000000..b994b35 --- /dev/null +++ b/lib/features/book_appointments/book_appointments_repo.dart @@ -0,0 +1,56 @@ +import 'package:dartz/dartz.dart'; +import 'package:hmg_patient_app_new/core/api/api_client.dart'; +import 'package:hmg_patient_app_new/core/exceptions/api_exception.dart'; +import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart'; +import 'package:hmg_patient_app_new/services/logger_service.dart'; + +abstract class BookAppointmentsRepo { + Future> getDoctors(); +} + +class BookAppointmentsRepoImp implements BookAppointmentsRepo { + final ApiClient apiClient; + final LoggerService loggerService; + + BookAppointmentsRepoImp({required this.loggerService, required this.apiClient}); + + @override + Future> getDoctors() async { + try { + // Mock API call with delayed response + final result = await Future.delayed( + const Duration(seconds: 2), + () => { + 'success': true, + 'data': [ + { + 'id': '1', + 'name': 'Dr. Ahmed Hassan', + 'specialty': 'Cardiology', + 'experience': '10 years', + 'rating': 4.8, + 'image': 'https://example.com/doctor1.jpg' + }, + ] + } + ); + + if (result != null && result is Map && result['success'] != null && result['success'] != false) { + return Right(result); + } else { + loggerService.errorLogs(result.toString()); + return Left(ServerFailure(result.toString())); + } + } on APIException catch (e) { + APIError? apiError; + if (e.error is APIError) { + apiError = e.error as APIError; + } + loggerService.errorLogs(e.toString()); + return Left(ServerFailure(apiError?.errorMessage ?? e.message)); + } catch (e) { + loggerService.errorLogs(e.toString()); + return Left(ServerFailure(e.toString())); + } + } +} diff --git a/lib/features/book_appointments/book_appointments_view_model.dart b/lib/features/book_appointments/book_appointments_view_model.dart new file mode 100644 index 0000000..e69de29 diff --git a/lib/features/common/common_repo.dart b/lib/features/common/common_repo.dart new file mode 100644 index 0000000..1d7301c --- /dev/null +++ b/lib/features/common/common_repo.dart @@ -0,0 +1,13 @@ +import 'package:hmg_patient_app_new/services/logger_service.dart'; + +abstract class CommonRepo { + +} + + +class CommonRepoImp implements CommonRepo { + LoggerService loggerService; + + CommonRepoImp({required this.loggerService}); + +} \ No newline at end of file diff --git a/lib/features/common/common_view_model.dart b/lib/features/common/common_view_model.dart new file mode 100644 index 0000000..e69de29 diff --git a/lib/features/my_appointments/my_appointments_repo.dart b/lib/features/my_appointments/my_appointments_repo.dart new file mode 100644 index 0000000..7ac0c47 --- /dev/null +++ b/lib/features/my_appointments/my_appointments_repo.dart @@ -0,0 +1,78 @@ +import 'package:dartz/dartz.dart'; +import 'package:hmg_patient_app_new/core/api/api_client.dart'; +import 'package:hmg_patient_app_new/core/exceptions/api_exception.dart'; +import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart'; +import 'package:hmg_patient_app_new/services/logger_service.dart'; + +abstract class MyAppointmentsRepo { + Future> getMyAppointments(); +} + +class MyAppointmentsRepoImp implements MyAppointmentsRepo { + final ApiClient apiClient; + final LoggerService loggerService; + + MyAppointmentsRepoImp({required this.loggerService, required this.apiClient}); + + @override + Future> getMyAppointments() async { + try { + // Mock API call with delayed response + final result = await Future.delayed( + const Duration(seconds: 2), + () => { + 'success': true, + 'data': [ + { + 'id': '1', + 'doctorName': 'Dr. Ahmed Hassan', + 'specialty': 'Cardiology', + 'appointmentDate': '2025-09-05', + 'appointmentTime': '10:00 AM', + 'status': 'confirmed', + 'clinicName': 'HMG Hospital', + 'patientName': 'John Doe' + }, + { + 'id': '2', + 'doctorName': 'Dr. Sarah Johnson', + 'specialty': 'Dermatology', + 'appointmentDate': '2025-09-08', + 'appointmentTime': '2:30 PM', + 'status': 'pending', + 'clinicName': 'HMG Medical Center', + 'patientName': 'John Doe' + }, + { + 'id': '3', + 'doctorName': 'Dr. Mohamed Ali', + 'specialty': 'Pediatrics', + 'appointmentDate': '2025-08-25', + 'appointmentTime': '11:15 AM', + 'status': 'completed', + 'clinicName': 'HMG Children\'s Clinic', + 'patientName': 'John Doe' + } + ] + } + ); + + if (result != null && result is Map && result['success'] != null && result['success'] != false) { + return Right(result); + } else { + loggerService.errorLogs(result.toString()); + return Left(ServerFailure(result.toString())); + } + } on APIException catch (e) { + APIError? apiError; + if (e.error is APIError) { + apiError = e.error as APIError; + } + loggerService.errorLogs(e.toString()); + return Left(ServerFailure(apiError?.errorMessage ?? e.message)); + } catch (e) { + loggerService.errorLogs(e.toString()); + return Left(ServerFailure(e.toString())); + } + } +} diff --git a/lib/features/my_appointments/my_appointments_view_model.dart b/lib/features/my_appointments/my_appointments_view_model.dart new file mode 100644 index 0000000..e69de29 diff --git a/lib/main.dart b/lib/main.dart index 1996653..8d0f7fa 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -6,8 +6,10 @@ import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; -import 'package:hmg_patient_app_new/providers/authentication_view_model.dart'; +import 'package:hmg_patient_app_new/core/dependencies.dart'; +import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; import 'package:hmg_patient_app_new/routes/app_routes.dart'; +import 'package:hmg_patient_app_new/services/logger_service.dart'; import 'package:hmg_patient_app_new/theme/app_theme.dart'; import 'package:logger/logger.dart'; import 'package:provider/provider.dart'; @@ -19,23 +21,11 @@ import 'firebase_options.dart'; var globalMessengerKey = GlobalKey(); final navigatorKey = GlobalKey(); -Logger logger = Logger( - printer: PrettyPrinter( - lineLength: 0, - ), -); - -late AppState appState; @pragma('vm:entry-point') Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); - print("Firebase backgroundMessageHandler Main!!!"); - // debugPrint('backgroundMessage: message => ${message.notification!.title.toString()}'); - // messagesOpended = message.notification!.title.toString(); - var payload = message.data; - // showCallkitIncoming(payload); - // await backgroundCallHandler(payload); + getIt.get().logInfo("Firebase backgroundMessageHandler Main!!!"); } class MyHttpOverrides extends HttpOverrides { @@ -45,14 +35,17 @@ class MyHttpOverrides extends HttpOverrides { } } -//pub run easy_localization:generate -O ./lib/generated -f keys -o locale_keys.g.dart --source-dir ./assets/langs - -void main() async { +Future callInitializations() async { WidgetsFlutterBinding.ensureInitialized(); await EasyLocalization.ensureInitialized(); await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); + AppDependencies.addDependencies(); SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); HttpOverrides.global = MyHttpOverrides(); +} + +void main() async { + callInitializations(); runApp( EasyLocalization( supportedLocales: const [ @@ -63,7 +56,7 @@ void main() async { fallbackLocale: Locale('en', 'US'), child: MultiProvider(providers: [ ChangeNotifierProvider( - create: (_) => AuthenticationViewModel(), + create: (_) => AuthenticationViewModel(authenticationRepo: getIt()), ), ], child: MyApp()), ), @@ -73,7 +66,6 @@ void main() async { class MyApp extends StatelessWidget { const MyApp({super.key}); - // This widget is the root of your application. @override Widget build(BuildContext context) { return SafeArea( diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart index a80c25f..9163a7e 100644 --- a/lib/presentation/home/landing_page.dart +++ b/lib/presentation/home/landing_page.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; -import 'package:hmg_patient_app_new/providers/authentication_view_model.dart'; +import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; class LandingPage extends StatefulWidget { const LandingPage({super.key}); diff --git a/lib/providers/authentication_view_model.dart b/lib/providers/authentication_view_model.dart deleted file mode 100644 index af542ac..0000000 --- a/lib/providers/authentication_view_model.dart +++ /dev/null @@ -1,5 +0,0 @@ -import 'package:flutter/material.dart'; - -class AuthenticationViewModel extends ChangeNotifier { - // Add properties and methods related to authentication here -} \ No newline at end of file diff --git a/lib/routes/app_routes.dart b/lib/routes/app_routes.dart index 244b57e..8aafc82 100644 --- a/lib/routes/app_routes.dart +++ b/lib/routes/app_routes.dart @@ -10,4 +10,5 @@ class AppRoutes { initialRoute: (context) => SplashPage(), login: (context) => LoginScreen(), }; + static Map get routes => {initialRoute: (context) => SplashPage()}; } diff --git a/lib/services/authentication/authentication_repo.dart b/lib/services/authentication/authentication_repo.dart deleted file mode 100644 index 353ad59..0000000 --- a/lib/services/authentication/authentication_repo.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'dart:convert'; - -import 'package:hmg_patient_app_new/core/app_state.dart'; -import 'package:http/http.dart'; - -import '../../core/consts.dart'; -import '../api_client.dart'; -import 'models/response_models/get_patient_last_login_details_response_model.dart'; - -class AuthenticationApiClient { - static final AuthenticationApiClient _instance = AuthenticationApiClient._internal(); - - AuthenticationApiClient._internal(); - - factory AuthenticationApiClient() => _instance; - - Future getMultipleLoginUserData(String deviceIMEI) async { - GetPatientLastLoginDetailsResponseModel getPatientLastLoginDetailsResponseModel; - - Map request = {"IMEI": deviceIMEI}; - request.addAll(AppState().postParamsJson); - String url = ApiConsts.baseUrl + ApiConsts.SELECT_DEVICE_IMEI; - Response response = await ApiClient().postJsonForResponse(url, request); - - var json = jsonDecode(response.body); - getPatientLastLoginDetailsResponseModel = GetPatientLastLoginDetailsResponseModel.fromJson(json); - - return getPatientLastLoginDetailsResponseModel; - } -} diff --git a/lib/services/cache_service.dart b/lib/services/cache_service.dart new file mode 100644 index 0000000..f25409e --- /dev/null +++ b/lib/services/cache_service.dart @@ -0,0 +1,14 @@ + +import 'package:shared_preferences/shared_preferences.dart'; + +abstract class CacheService { + +} + +class CacheServiceImp implements CacheService { + SharedPreferences sharedPreferences; + + CacheServiceImp({required this.sharedPreferences}); + + +} diff --git a/lib/services/logger_service.dart b/lib/services/logger_service.dart new file mode 100644 index 0000000..0b33c52 --- /dev/null +++ b/lib/services/logger_service.dart @@ -0,0 +1,27 @@ + +import 'package:logger/logger.dart'; + +abstract class LoggerService { + + void errorLogs(String message); + + void logInfo(String message); +} + +class LoggerServiceImp implements LoggerService { + Logger logger; + + LoggerServiceImp({required this.logger}); + + + + @override + void errorLogs(String message) { + logger.e(message); + } + + @override + void logInfo(String message) { + logger.i(message); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index e7dd29a..a6e3e49 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -30,8 +30,8 @@ dependencies: # firebase_core: ^3.13.1 permission_handler: ^12.0.1 flutter_local_notifications: ^19.4.1 - injector: ^4.0.0 provider: ^6.1.5+1 + get_it: ^8.2.0 just_audio: ^0.10.4 flutter_callkit_incoming: git: @@ -60,6 +60,8 @@ dependencies: dropdown_search: ^6.0.2 google_maps_flutter: ^2.12.3 flutter_zoom_videosdk: ^2.1.10 + dartz: ^0.10.1 + equatable: ^2.0.7 web: any smooth_corner: ^1.1.1