completed the deviceapi with new architecture

pull/3/head
faizatflutter 2 months ago
parent 89e9323d9e
commit 6bc7af00bc

@ -41,11 +41,20 @@ PODS:
- file_picker (0.0.1):
- DKImagePickerController/PhotoGallery
- Flutter
- Firebase/Analytics (11.15.0):
- Firebase/Core
- Firebase/Core (11.15.0):
- Firebase/CoreOnly
- FirebaseAnalytics (~> 11.15.0)
- Firebase/CoreOnly (11.15.0):
- FirebaseCore (~> 11.15.0)
- Firebase/Messaging (11.15.0):
- Firebase/CoreOnly
- FirebaseMessaging (~> 11.15.0)
- firebase_analytics (11.6.0):
- Firebase/Analytics (= 11.15.0)
- firebase_core
- Flutter
- firebase_core (3.15.2):
- Firebase/CoreOnly (= 11.15.0)
- Flutter
@ -53,6 +62,24 @@ PODS:
- Firebase/Messaging (= 11.15.0)
- firebase_core
- Flutter
- FirebaseAnalytics (11.15.0):
- FirebaseAnalytics/Default (= 11.15.0)
- FirebaseCore (~> 11.15.0)
- FirebaseInstallations (~> 11.0)
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/MethodSwizzler (~> 8.1)
- GoogleUtilities/Network (~> 8.1)
- "GoogleUtilities/NSData+zlib (~> 8.1)"
- nanopb (~> 3.30910.0)
- FirebaseAnalytics/Default (11.15.0):
- FirebaseCore (~> 11.15.0)
- FirebaseInstallations (~> 11.0)
- GoogleAppMeasurement/Default (= 11.15.0)
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/MethodSwizzler (~> 8.1)
- GoogleUtilities/Network (~> 8.1)
- "GoogleUtilities/NSData+zlib (~> 8.1)"
- nanopb (~> 3.30910.0)
- FirebaseCore (11.15.0):
- FirebaseCoreInternal (~> 11.15.0)
- GoogleUtilities/Environment (~> 8.1)
@ -371,14 +398,16 @@ SPEC CHECKSUMS:
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
file_picker: b159e0c068aef54932bb15dc9fd1571818edaf49
Firebase: d99ac19b909cd2c548339c2241ecd0d1599ab02e
firebase_analytics: bf93e20703c95030404d6ddbb1adf05bf5c3885b
firebase_core: 99a37263b3c27536063a7b601d9e2a49400a433c
firebase_messaging: bf6697c61f31c7cc0f654131212ff04c0115c2c7
FirebaseAnalytics: 6433dfd311ba78084fc93bdfc145e8cb75740eae
FirebaseCore: efb3893e5b94f32b86e331e3bd6dadf18b66568e
FirebaseCoreInternal: 9afa45b1159304c963da48addb78275ef701c6b4
FirebaseInstallations: 317270fec08a5d418fdbc8429282238cab3ac843
FirebaseMessaging: 3b26e2cee503815e01c3701236b020aa9b576f09
FLAnimatedImage: bbf914596368867157cc71b38a8ec834b3eeb32b
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
flutter_inappwebview_ios: 6f63631e2c62a7c350263b13fa5427aedefe81d4
flutter_ios_voip_kit_karmm: 7ea37381a8841c92d186edf1f4604df5cc437579
flutter_local_notifications: ff50f8405aaa0ccdc7dcfb9022ca192e8ad9688f
@ -387,6 +416,8 @@ SPEC CHECKSUMS:
geolocator_apple: 66b711889fd333205763b83c9dcf0a57a28c7afd
Google-Maps-iOS-Utils: 66d6de12be1ce6d3742a54661e7a79cb317a9321
google_maps_flutter_ios: e31555a04d1986ab130f2b9f24b6cdc861acc6d3
GoogleAdsOnDeviceConversion: 2be6297a4f048459e0ae17fad9bfd2844e10cf64
GoogleAppMeasurement: 700dce7541804bec33db590a5c496b663fbe2539
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
GoogleMaps: 8939898920281c649150e0af74aa291c60f2e77d
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
@ -423,4 +454,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: 5df9d8aa8f2c105eacd5ad7a310503d93c68c86b
COCOAPODS: 1.15.2
COCOAPODS: 1.16.2

@ -2,21 +2,25 @@ import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'dart:io' show Platform;
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/api_consts.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/services/analytics/analytics_service.dart';
import 'package:hmg_patient_app_new/services/dialog_service.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
import 'package:http/http.dart' as http;
import '../exceptions/api_failure.dart';
abstract class ApiClient {
Future<void> post(
String endPoint, {
required Map<String, dynamic> body,
required Function(dynamic response, int statusCode) onSuccess,
required Function(String error, int statusCode) onFailure,
required Function(dynamic response, int statusCode, {int? messageStatus}) onSuccess,
required Function(String error, int statusCode, {int? messageStatus, Failure? failureType}) onFailure,
bool isAllowAny,
bool isExternal,
bool isRCService,
@ -32,61 +36,69 @@ abstract class ApiClient {
bool isRCService,
});
Future<void> simplePost(
String fullUrl, {
required Map<dynamic, dynamic> body,
required Map<String, String> headers,
required Function(dynamic response, int statusCode) onSuccess,
required Function(String error, int statusCode) onFailure,
});
Future<void> simpleGet(
String fullUrl, {
Function(dynamic response, int statusCode)? onSuccess,
Function(String error, int statusCode)? onFailure,
Map<String, dynamic>? queryParams,
Map<String, String>? headers,
});
Future<void> simplePut(
String fullUrl, {
Map<String, dynamic>? body,
Map<String, String>? headers,
Function(dynamic response, int statusCode)? onSuccess,
Function(String error, int statusCode)? onFailure,
});
Future<void> simpleDelete(
String fullUrl, {
Function(dynamic response, int statusCode)? onSuccess,
Function(String error, int statusCode)? onFailure,
Map<String, String>? queryParams,
Map<String, String>? headers,
});
Future<bool> handleUnauthorized(int statusCode, {required String forUrl});
String getSessionId(String id);
Future<String> generatePackagesToken();
// Future<void> simplePost(
// String fullUrl, {
// required Map<dynamic, dynamic> body,
// required Map<String, String> headers,
// required Function(dynamic response, int statusCode) onSuccess,
// required Function(String error, int statusCode) onFailure,
// });
//
// Future<void> simpleGet(
// String fullUrl, {
// Function(dynamic response, int statusCode)? onSuccess,
// Function(String error, int statusCode)? onFailure,
// Map<String, dynamic>? queryParams,
// Map<String, String>? headers,
// });
//
// Future<void> simplePut(
// String fullUrl, {
// Map<String, dynamic>? body,
// Map<String, String>? headers,
// Function(dynamic response, int statusCode)? onSuccess,
// Function(String error, int statusCode)? onFailure,
// });
//
// Future<void> simpleDelete(
// String fullUrl, {
// Function(dynamic response, int statusCode)? onSuccess,
// Function(String error, int statusCode)? onFailure,
// Map<String, String>? queryParams,
// Map<String, String>? headers,
// });
// Future<bool> handleUnauthorized(int statusCode, {required String forUrl});
// Future<String> generatePackagesToken();
}
class ApiClientImp implements ApiClient {
final _analytics = getIt<GAnalytics>();
final LoggerService loggerService;
final AppState appState;
final DialogService dialogService;
ApiClientImp({required this.loggerService});
ApiClientImp({
required this.loggerService,
required this.dialogService,
required this.appState,
});
@override
post(String endPoint,
{required Map<String, dynamic> body,
required Function(dynamic response, int statusCode) onSuccess,
required Function(String error, int statusCode) onFailure,
bool isAllowAny = false,
bool isExternal = false,
bool isRCService = false,
bool bypassConnectionCheck = false}) async {
post(
String endPoint, {
required Map<String, dynamic> body,
required Function(dynamic response, int statusCode, {int? messageStatus}) onSuccess,
required Function(String error, int statusCode, {int? messageStatus, Failure? failureType}) onFailure,
bool isAllowAny = false,
bool isExternal = false,
bool isRCService = false,
bool bypassConnectionCheck = false,
}) async {
AppState appState = getIt.get<AppState>();
String url;
if (isExternal) {
@ -103,7 +115,7 @@ class ApiClientImp implements ApiClient {
Map<String, String> headers = {'Content-Type': 'application/json', 'Accept': 'application/json'};
if (!isExternal) {
String? token = appState.appAuthToken;
String? languageID = (appState.postParamsObject?.languageID == 1 ? 'ar' : 'en') ?? 'ar';
String? languageID = (appState.postParamsObject?.languageID == 1 ? 'ar' : 'en');
if (endPoint == ApiConsts.sendActivationCode) {
languageID = 'en';
}
@ -113,7 +125,6 @@ class ApiClientImp implements ApiClient {
if (body.containsKey('LanguageID')) {
if (body['LanguageID'] != null) {
//change this line because language issue happened on dental
body['LanguageID'] = body['LanguageID'] == 'ar'
? 1
: body['LanguageID'] == 'en'
@ -128,7 +139,6 @@ class ApiClientImp implements ApiClient {
: IS_DENTAL_ALLOWED_BACKEND;
}
//Todo: I have converted it to string
body['DeviceTypeID'] = Platform.isIOS
? "1"
@ -160,40 +170,38 @@ class ApiClientImp implements ApiClient {
}
}
body['LanguageID'] = body['LanguageID'] ?? "2";
body['VersionID'] = body['VersionID'] ?? "18.7";
body['Channel'] = body['Channel'] ?? "3";
body['IPAdress'] = body['IPAdress'] ?? "10.20.10.20";
body['generalid'] = body['generalid'] ?? "Cs2020@2016\$2958";
body['Latitude'] = body['Latitude'] ?? "0.0";
body['Longitude'] = body['Longitude'] ?? "0.0";
body['LanguageID'] = body['LanguageID'] ?? "2";
body['VersionID'] = body['VersionID'] ?? "18.7";
body['Channel'] = body['Channel'] ?? "3";
body['IPAdress'] = body['IPAdress'] ?? "10.20.10.20";
body['generalid'] = body['generalid'] ?? "Cs2020@2016\$2958";
body['Latitude'] = body['Latitude'] ?? "0.0";
body['Longitude'] = body['Longitude'] ?? "0.0";
body['DeviceTypeID'] = body['DeviceTypeID'] ??
(Platform.isIOS ? "1" : await Utils.isGoogleServicesAvailable() ? "2" : "3");
//"LanguageID":1,"VersionID":18.7,"Channel":3,"IPAdress":"10.20.10.20","generalid":"Cs2020@2016$2958","Latitude":0.0,"Longitude":0.0,"DeviceTypeID":2,"PatientType":1}
(Platform.isIOS
? "1"
: await Utils.isGoogleServicesAvailable()
? "2"
: "3");
body.removeWhere((key, value) => value == null);
log("bodi: ${json.encode(body)}");
log("bodi: ${Uri.parse(url.trim())}");
log("body: ${json.encode(body)}");
log("uri: ${Uri.parse(url.trim())}");
if (await Utils.checkConnection(bypassConnectionCheck: bypassConnectionCheck)) {
final response = await http.post(Uri.parse(url.trim()), body: json.encode(body), headers: headers);
final int statusCode = response.statusCode;
if (statusCode < 200 || statusCode >= 400) {
onFailure('Error While Fetching data', statusCode);
onFailure('Error While Fetching data', statusCode, failureType: ServerFailure("Error While Fetching data"));
logApiEndpointError(endPoint, 'Error While Fetching data', statusCode);
} else {
var parsed = json.decode(utf8.decode(response.bodyBytes));
var parsed = json.decode(utf8.decode(response.bodyBytes));
log("parsed: ${parsed.toString()}");
if (isAllowAny) {
onSuccess(parsed, statusCode);
} else {
if (parsed['Response_Message'] != null) {
onSuccess(parsed, statusCode);
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus']);
} else {
if (parsed['ErrorType'] == 4) {
//TODO : handle app update
@ -204,19 +212,18 @@ class ApiClientImp implements ApiClient {
logApiEndpointError(endPoint, "session logged out", statusCode);
}
if (isAllowAny) {
onSuccess(parsed, statusCode);
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus']);
} else if (parsed['IsAuthenticated'] == null) {
if (parsed['isSMSSent'] == true) {
onSuccess(parsed, statusCode);
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus']);
} else if (parsed['MessageStatus'] == 1) {
onSuccess(parsed, statusCode);
} else if (parsed['Result'] == 'OK') {
onSuccess(parsed, statusCode);
} else {
onFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode);
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);
@ -226,28 +233,46 @@ class ApiClientImp implements ApiClient {
} else {
if (parsed['message'] == null && parsed['ErrorEndUserMessage'] == null) {
if (parsed['ErrorSearchMsg'] == null) {
onFailure("Server Error found with no available message", statusCode);
onFailure(
"Server Error found with no available message",
statusCode,
failureType: ServerFailure("Error While Fetching data"),
);
logApiEndpointError(endPoint, "Server Error found with no available message", statusCode);
} else {
onFailure(parsed['ErrorSearchMsg'], statusCode);
onFailure(
parsed['ErrorSearchMsg'],
statusCode,
failureType: ServerFailure("Error While Fetching data"),
);
logApiEndpointError(endPoint, parsed['ErrorSearchMsg'], statusCode);
}
} else {
onFailure(parsed['message'] ?? parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode);
onFailure(
parsed['message'] ?? parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'],
statusCode,
failureType: ServerFailure("Error While Fetching data"),
);
logApiEndpointError(endPoint, parsed['message'] ?? parsed['message'], statusCode);
}
}
}
else {
} else {
if (parsed['SameClinicApptList'] != null) {
onSuccess(parsed, statusCode);
} else {
if (parsed['message'] != null) {
onFailure(parsed['message'] ?? parsed['message'], statusCode);
onFailure(
parsed['message'] ?? parsed['message'],
statusCode,
failureType: ServerFailure("Error While Fetching data"),
);
logApiEndpointError(endPoint, parsed['message'] ?? parsed['message'], statusCode);
} else {
onFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode);
onFailure(
parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'],
statusCode,
failureType: ServerFailure("Error While Fetching data"),
);
logApiEndpointError(endPoint, parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode);
}
}
@ -256,13 +281,17 @@ class ApiClientImp implements ApiClient {
}
}
} else {
onFailure('Please Check The Internet Connection 1', -1);
onFailure(
'Please Check The Internet Connection 1',
-1,
failureType: ConnectivityFailure("Error While Fetching data"),
);
_analytics.errorTracking.log("internet_connectivity", error: "no internet available");
}
} catch (e) {
loggerService.errorLogs(e.toString());
if (e.toString().contains("ClientException")) {
onFailure('Something went wrong, plase try again', -1);
onFailure('Something went wrong, plase try again', -1, failureType: InvalidCredentials('Something went wrong, plase try again'));
_analytics.errorTracking.log("internet_connectivity", error: "no internet available");
} else {
onFailure(e.toString(), -1);
@ -271,6 +300,7 @@ class ApiClientImp implements ApiClient {
}
}
@override
get(String endPoint,
{required Function(dynamic response, int statusCode) onSuccess,
required Function(String error, int statusCode) onFailure,
@ -304,203 +334,173 @@ class ApiClientImp implements ApiClient {
// print("statusCode :$statusCode");
if (statusCode < 200 || statusCode >= 400) {
onFailure!('Error While Fetching data', statusCode);
onFailure('Error While Fetching data', statusCode);
logApiEndpointError(endPoint, 'Error While Fetching data', statusCode);
} else {
var parsed = json.decode(utf8.decode(response.bodyBytes));
onSuccess!(parsed, statusCode);
}
} else {
onFailure!('Please Check The Internet Connection', -1);
_analytics.errorTracking.log("internet_connectivity", error: "no internet available");
}
}
simplePost(
String fullUrl, {
required Map<dynamic, dynamic> body,
required Map<String, String> headers,
required Function(dynamic response, int statusCode) onSuccess,
required Function(String error, int statusCode) onFailure,
}) async {
String url = fullUrl;
// print("URL Query String: $url");
// print("body: $body");
if (await Utils.checkConnection()) {
headers!.addAll({'Content-Type': 'application/json', 'Accept': 'application/json'});
final response = await http.post(
Uri.parse(url.trim()),
body: json.encode(body),
headers: headers,
);
final int statusCode = response.statusCode;
// print("statusCode :$statusCode");
if (await handleUnauthorized(statusCode, forUrl: fullUrl))
simplePost(fullUrl, onFailure: onFailure, onSuccess: onSuccess, body: body, headers: headers);
// print(response.body.toString());
if (statusCode < 200 || statusCode >= 400) {
onFailure!('Error While Fetching data', statusCode);
logApiFullUrlError(fullUrl, 'Error While Fetching data', statusCode);
} else {
onSuccess!(response.body.toString(), statusCode);
onSuccess(parsed, statusCode);
}
} else {
onFailure!('Please Check The Internet Connection', -1);
onFailure('Please Check The Internet Connection', -1);
_analytics.errorTracking.log("internet_connectivity", error: "no internet available");
}
}
simpleGet(String fullUrl,
{Function(dynamic response, int statusCode)? onSuccess,
Function(String error, int statusCode)? onFailure,
Map<String, dynamic>? queryParams,
Map<String, String>? headers}) async {
headers = headers ?? {};
String url = fullUrl;
var haveParams = (queryParams != null);
if (haveParams) {
String queryString = Uri(queryParameters: queryParams).query;
url += '?$queryString';
// print("URL Query String: $url");
}
if (await Utils.checkConnection()) {
headers.addAll({'Content-Type': 'application/json', 'Accept': 'application/json'});
final response = await http.get(
Uri.parse(url.trim()),
headers: headers,
);
final int statusCode = response.statusCode;
// print("statusCode :$statusCode");
if (await handleUnauthorized(statusCode, forUrl: fullUrl))
simpleGet(fullUrl, onFailure: onFailure, onSuccess: onSuccess, headers: headers, queryParams: queryParams);
if (statusCode < 200 || statusCode >= 400) {
onFailure!('Error While Fetching data', statusCode);
logApiFullUrlError(fullUrl, 'Error While Fetching data', statusCode);
} else {
onSuccess!(response.body.toString(), statusCode);
}
} else {
onFailure!('Please Check The Internet Connection', -1);
_analytics.errorTracking.log("internet_connectivity", error: "no internet available");
}
}
simplePut(String fullUrl,
{Map<String, dynamic>? body,
Map<String, String>? headers,
Function(dynamic response, int statusCode)? onSuccess,
Function(String error, int statusCode)? onFailure}) async {
String url = fullUrl;
// print("URL Query String: $url");
if (await Utils.checkConnection()) {
headers!.addAll({'Content-Type': 'application/json', 'Accept': 'application/json'});
final response = await http.put(
Uri.parse(url.trim()),
body: json.encode(body),
headers: headers,
);
final int statusCode = response.statusCode;
// print("statusCode :$statusCode");
if (await handleUnauthorized(statusCode, forUrl: fullUrl))
simplePut(fullUrl, onFailure: onFailure, onSuccess: onSuccess, headers: headers, body: body);
if (statusCode < 200 || statusCode >= 400) {
onFailure!('Error While Fetching data', statusCode);
logApiFullUrlError(fullUrl, 'Error While Fetching data', statusCode);
} else {
onSuccess!(response.body.toString(), statusCode);
}
} else {
onFailure!('Please Check The Internet Connection', -1);
_analytics.errorTracking.log("internet_connectivity", error: "no internet available");
}
}
simpleDelete(String fullUrl,
{Function(dynamic response, int statusCode)? onSuccess,
Function(String error, int statusCode)? onFailure,
Map<String, String>? queryParams,
Map<String, String>? headers}) async {
String url = fullUrl;
// print("URL Query String: $url");
var haveParams = (queryParams != null);
if (haveParams) {
String queryString = Uri(queryParameters: queryParams).query;
url += '?$queryString';
// print("URL Query String: $url");
}
if (await Utils.checkConnection()) {
headers!.addAll({'Content-Type': 'application/json', 'Accept': 'application/json'});
final response = await http.delete(
Uri.parse(url.trim()),
headers: headers,
);
final int statusCode = response.statusCode;
// print("statusCode :$statusCode");
if (await handleUnauthorized(statusCode, forUrl: fullUrl))
simpleDelete(fullUrl, onFailure: onFailure, onSuccess: onSuccess, queryParams: queryParams, headers: headers);
if (statusCode < 200 || statusCode >= 400) {
onFailure!('Error While Fetching data', statusCode);
logApiFullUrlError(fullUrl, 'Error While Fetching data', statusCode);
} else {
onSuccess!(response.body.toString(), statusCode);
}
} else {
onFailure!('Please Check The Internet Connection', -1);
_analytics.errorTracking.log("internet_connectivity", error: "no internet available");
}
}
Future<bool> handleUnauthorized(int statusCode, {required String forUrl}) async {
if (forUrl.startsWith(EXA_CART_API_BASE_URL) && statusCode == 401) {
final token = await generatePackagesToken();
ApiConsts.packagesAuthHeader['Authorization'] = 'Bearer $token';
return (token is String);
}
return false;
}
// @override
// simplePost(
// String fullUrl, {
// required Map<dynamic, dynamic> body,
// required Map<String, String> headers,
// required Function(dynamic response, int statusCode) onSuccess,
// required Function(String error, int statusCode) onFailure,
// }) async {
// String url = fullUrl;
// // print("URL Query String: $url");
// // print("body: $body");
//
// if (await Utils.checkConnection()) {
// headers!.addAll({'Content-Type': 'application/json', 'Accept': 'application/json'});
// final response = await http.post(
// Uri.parse(url.trim()),
// body: json.encode(body),
// headers: headers,
// );
// final int statusCode = response.statusCode;
// // print("statusCode :$statusCode");
// if (await handleUnauthorized(statusCode, forUrl: fullUrl)) {
// simplePost(fullUrl, onFailure: onFailure, onSuccess: onSuccess, body: body, headers: headers);
// }
//
// // print(response.body.toString());
//
// if (statusCode < 200 || statusCode >= 400) {
// onFailure!('Error While Fetching data', statusCode);
// logApiFullUrlError(fullUrl, 'Error While Fetching data', statusCode);
// } else {
// onSuccess!(response.body.toString(), statusCode);
// }
// } else {
// onFailure!('Please Check The Internet Connection', -1);
// _analytics.errorTracking.log("internet_connectivity", error: "no internet available");
// }
// }
// simpleGet(String fullUrl,
// {Function(dynamic response, int statusCode)? onSuccess,
// Function(String error, int statusCode)? onFailure,
// Map<String, dynamic>? queryParams,
// Map<String, String>? headers}) async {
// headers = headers ?? {};
// String url = fullUrl;
//
// var haveParams = (queryParams != null);
// if (haveParams) {
// String queryString = Uri(queryParameters: queryParams).query;
// url += '?$queryString';
// // print("URL Query String: $url");
// }
//
// if (await Utils.checkConnection()) {
// headers.addAll({'Content-Type': 'application/json', 'Accept': 'application/json'});
// final response = await http.get(
// Uri.parse(url.trim()),
// headers: headers,
// );
//
// final int statusCode = response.statusCode;
// // print("statusCode :$statusCode");
// if (await handleUnauthorized(statusCode, forUrl: fullUrl))
// simpleGet(fullUrl, onFailure: onFailure, onSuccess: onSuccess, headers: headers, queryParams: queryParams);
//
// if (statusCode < 200 || statusCode >= 400) {
// onFailure!('Error While Fetching data', statusCode);
// logApiFullUrlError(fullUrl, 'Error While Fetching data', statusCode);
// } else {
// onSuccess!(response.body.toString(), statusCode);
// }
// } else {
// onFailure!('Please Check The Internet Connection', -1);
// _analytics.errorTracking.log("internet_connectivity", error: "no internet available");
// }
// }
// simplePut(String fullUrl,
// {Map<String, dynamic>? body,
// Map<String, String>? headers,
// Function(dynamic response, int statusCode)? onSuccess,
// Function(String error, int statusCode)? onFailure}) async {
// String url = fullUrl;
// // print("URL Query String: $url");
//
// if (await Utils.checkConnection()) {
// headers!.addAll({'Content-Type': 'application/json', 'Accept': 'application/json'});
// final response = await http.put(
// Uri.parse(url.trim()),
// body: json.encode(body),
// headers: headers,
// );
//
// final int statusCode = response.statusCode;
// // print("statusCode :$statusCode");
// if (await handleUnauthorized(statusCode, forUrl: fullUrl))
// simplePut(fullUrl, onFailure: onFailure, onSuccess: onSuccess, headers: headers, body: body);
//
// if (statusCode < 200 || statusCode >= 400) {
// onFailure!('Error While Fetching data', statusCode);
// logApiFullUrlError(fullUrl, 'Error While Fetching data', statusCode);
// } else {
// onSuccess!(response.body.toString(), statusCode);
// }
// } else {
// onFailure!('Please Check The Internet Connection', -1);
// _analytics.errorTracking.log("internet_connectivity", error: "no internet available");
// }
// }
//
// simpleDelete(String fullUrl,
// {Function(dynamic response, int statusCode)? onSuccess,
// Function(String error, int statusCode)? onFailure,
// Map<String, String>? queryParams,
// Map<String, String>? headers}) async {
// String url = fullUrl;
// // print("URL Query String: $url");
//
// var haveParams = (queryParams != null);
// if (haveParams) {
// String queryString = Uri(queryParameters: queryParams).query;
// url += '?$queryString';
// // print("URL Query String: $url");
// }
//
// if (await Utils.checkConnection()) {
// headers!.addAll({'Content-Type': 'application/json', 'Accept': 'application/json'});
// final response = await http.delete(
// Uri.parse(url.trim()),
// headers: headers,
// );
//
// final int statusCode = response.statusCode;
// // print("statusCode :$statusCode");
// if (await handleUnauthorized(statusCode, forUrl: fullUrl))
// simpleDelete(fullUrl, onFailure: onFailure, onSuccess: onSuccess, queryParams: queryParams, headers: headers);
//
// if (statusCode < 200 || statusCode >= 400) {
// onFailure!('Error While Fetching data', statusCode);
// logApiFullUrlError(fullUrl, 'Error While Fetching data', statusCode);
// } else {
// onSuccess!(response.body.toString(), statusCode);
// }
// } else {
// onFailure!('Please Check The Internet Connection', -1);
// _analytics.errorTracking.log("internet_connectivity", error: "no internet available");
// }
// }
@override
String getSessionId(String id) {
return id.replaceAll(RegExp('/[^a-zA-Z]'), '');
}
Future<String> generatePackagesToken() async {
var url = EXA_CART_API_BASE_URL + PACKAGES_TOKEN;
var body = {
"api_client": {
"client_id": "a4ab6be4-424f-4836-b032-46caed88e184",
"client_secret": "3c1a3e07-4a40-4510-9fb0-ee5f0a72752c"
}
};
String? token;
final completer = Completer();
simplePost(url, body: body, headers: {}, onSuccess: (dynamic stringResponse, int statusCode) {
if (statusCode == 200) {
var jsonResponse = json.decode(stringResponse);
token = jsonResponse['auth_token'];
completer.complete();
}
}, onFailure: (String error, int statusCode) {
completer.complete();
logApiFullUrlError(url, error, statusCode);
});
await completer.future;
return token!;
}
logApiFullUrlError(String fullUrl, error, code) {
final endpoint = Uri.parse(fullUrl).pathSegments.last;
logApiEndpointError(endpoint, error, code);

@ -1,15 +1,14 @@
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/authenticated_user_model.dart';
import 'package:hmg_patient_app_new/main.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'api_consts.dart' as ApiConsts;
class AppState {
// Simple constructor - let get_it handle the singleton behavior
AppState();
NavigationService navigationService;
AppState({required this.navigationService});
bool isAuthenticated = true;
@ -40,9 +39,7 @@ class AppState {
PostParamsModel? get postParamsObject => _postParams;
Map<String, dynamic> get postParamsJson => isAuthenticated
? (_postParams?.toJsonAfterLogin() ?? {})
: (_postParams?.toJson() ?? {});
Map<String, dynamic> get postParamsJson => isAuthenticated ? (_postParams?.toJsonAfterLogin() ?? {}) : (_postParams?.toJson() ?? {});
void setPostParamsModel(PostParamsModel _postParams) {
this._postParams = _postParams;
@ -51,12 +48,9 @@ class AppState {
double userLat = 0.0;
double userLong = 0.0;
bool isArabic() =>
EasyLocalization.of(navigatorKey.currentContext!)?.locale.languageCode ==
"ar";
bool isArabic() => EasyLocalization.of(navigationService.navigatorKey.currentContext!)?.locale.languageCode == "ar";
int getLanguageID(context) =>
EasyLocalization.of(context)?.locale.languageCode == "ar" ? 1 : 2;
int getLanguageID(context) => EasyLocalization.of(context)?.locale.languageCode == "ar" ? 1 : 2;
AuthenticatedUser? _authenticatedUser;

@ -0,0 +1,34 @@
class GenericApiModel<T> {
final int? messageStatus;
final String? errorMessage;
final int? statusCode;
final T? data;
GenericApiModel({
this.messageStatus,
this.errorMessage,
this.statusCode,
this.data,
});
factory GenericApiModel.fromJson(
Map<String, dynamic> json,
T Function(Object? json)? fromJsonT,
) {
return GenericApiModel<T>(
messageStatus: json['messageStatus'] as int?,
errorMessage: json['errorMessage'] as String?,
statusCode: json['statusCode'] as int?,
data: fromJsonT != null ? fromJsonT(json['data']) : json['data'] as T?,
);
}
Map<String, dynamic> toJson(Object Function(T value)? toJsonT) {
return {
'messageStatus': messageStatus,
'errorMessage': errorMessage,
'statusCode': statusCode,
'data': toJsonT != null && data != null ? toJsonT(data as T) : data,
};
}
}

@ -1,13 +1,18 @@
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:hmg_patient_app_new/core/location_util.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_repo.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.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/analytics/analytics_service.dart';
import 'package:hmg_patient_app_new/services/cache_service.dart';
import 'package:hmg_patient_app_new/services/dialog_service.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:logger/web.dart';
import 'package:shared_preferences/shared_preferences.dart';
@ -15,25 +20,58 @@ GetIt getIt = GetIt.instance;
class AppDependencies {
static Future<void> addDependencies() async {
// Services
getIt.registerLazySingleton<LoggerService>(() => LoggerServiceImp(logger: Logger(printer: PrettyPrinter(
methodCount: 2, // number of stack trace lines
errorMethodCount: 5, // number of stack trace lines for errors
lineLength: 100, // wrap width
colors: true, // colorful logs
printEmojis: true, // include emojis
),)));
Logger logger = Logger(
printer: PrettyPrinter(
methodCount: 2,
errorMethodCount: 5,
lineLength: 100,
colors: true,
printEmojis: true,
),
);
// Core Services
getIt.registerLazySingleton<LoggerService>(() => LoggerServiceImp(logger: logger));
getIt.registerLazySingleton<NavigationService>(() => NavigationService());
getIt.registerLazySingleton<GAnalytics>(() => GAnalytics());
getIt.registerLazySingleton<AppState>(() => AppState(navigationService: getIt()));
getIt.registerLazySingleton<LocationUtils>(() => LocationUtils(isShowConfirmDialog: false, navigationService: getIt()));
getIt.registerLazySingleton<DialogService>(() => DialogServiceImp(navigationService: getIt<NavigationService>()));
getIt.registerLazySingleton<ErrorHandlerService>(() => ErrorHandlerServiceImp(
dialogService: getIt(),
loggerService: getIt(),
navigationService: getIt(),
));
final sharedPreferences = await SharedPreferences.getInstance();
getIt.registerLazySingleton<CacheService>(() => CacheServiceImp(sharedPreferences: sharedPreferences));
getIt.registerSingleton(AppState());
getIt.registerSingleton(GAnalytics());
getIt.registerLazySingleton<ApiClient>(() => ApiClientImp(loggerService: getIt()));
getIt.registerLazySingleton<ApiClient>(() => ApiClientImp(loggerService: getIt(), dialogService: getIt(), appState: getIt()));
// Repositories
getIt.registerLazySingleton<CommonRepo>(() => CommonRepoImp(loggerService: getIt()));
getIt.registerLazySingleton<AuthenticationRepo>(() => AuthenticationRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<BookAppointmentsRepo>(() => BookAppointmentsRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<BookAppointmentsRepo>(
() => BookAppointmentsRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<MyAppointmentsRepo>(() => MyAppointmentsRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
// ViewModels
// Global/shared VMs LazySingleton
getIt.registerLazySingleton<AuthenticationViewModel>(
() => AuthenticationViewModel(
authenticationRepo: getIt(),
dialogService: getIt(),
appState: getIt(),
errorHandlerService: getIt(),
),
);
// Screen-specific VMs Factory
// getIt.registerFactory<BookAppointmentsViewModel>(
// () => BookAppointmentsViewModel(
// bookAppointmentsRepo: getIt(),
// dialogService: getIt(),
// errorHandlerService: getIt(),
// ),
// );
}
}

@ -1,8 +1,8 @@
import 'package:equatable/equatable.dart';
abstract class Failure extends Equatable implements Exception {
final String message;
const Failure(this.message);
}
@ -27,15 +27,30 @@ class LocalStorageFailure extends Failure {
List<Object?> get props => [message];
}
class DataParsingFailure extends Failure {
const DataParsingFailure(super.message);
@override
List<Object?> get props => [message];
}
class UnknownFailure extends Failure {
const UnknownFailure(super.message);
@override
List<Object?> get props => [message];
}
class DuplicateUsername extends Failure {
const DuplicateUsername({String? message}) : super(message ?? '');
const DuplicateUsername(String? message) : super(message ?? '');
@override
List<Object?> get props => [message];
}
class InvalidCredentials extends Failure {
const InvalidCredentials({String? message}) : super(message ?? '');
const InvalidCredentials(String? message) : super(message ?? '');
@override
List<Object?> get props => [message];

@ -1,41 +1,40 @@
import 'dart:io';
import 'package:geolocator/geolocator.dart';
import 'package:google_maps_flutter/google_maps_flutter.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/utils/utils.dart';
import 'package:flutter/cupertino.dart';
import 'package:geolocator/geolocator.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
// import 'package:huawei_location/huawei_location.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:provider/provider.dart';
class LocationUtils {
NavigationService navigationService;
bool isShowConfirmDialog;
bool isShowLocationTimeoutDialog;
BuildContext context;
bool isHuawei;
final GeolocatorPlatform _geolocatorPlatform = GeolocatorPlatform.instance;
LocationUtils({required this.isShowConfirmDialog, required this.context, this.isHuawei = false, this.isShowLocationTimeoutDialog = true});
LocationUtils({
required this.isShowConfirmDialog,
required this.navigationService,
this.isHuawei = false,
this.isShowLocationTimeoutDialog = true,
});
AppState appState = getIt.get<AppState>();
void getCurrentLocation({Function(LatLng)? callBack}) async {
Geolocator.isLocationServiceEnabled().then((value) async {
if (value) {
await Geolocator.checkPermission().then((permission) async {
if (permission == LocationPermission.always || permission == LocationPermission.whileInUse) {
// Geolocator.getCurrentPosition(locationSettings: LocationSettings(accuracy: LocationAccuracy.medium, timeLimit: Duration(seconds: 5))).then((value) {
Geolocator.getLastKnownPosition().then((value) {
setLocation(value);
if (callBack != null) callBack(LatLng(value?.latitude ?? 24.7101433, value?.longitude ?? 46.6757709));
}).catchError((err) {
print(err);
if (isShowConfirmDialog && isShowLocationTimeoutDialog) {
// showLocationTimeOutDialog(failureCallBack: () {
// Geolocator.openAppSettings();
// });
}
if (isShowConfirmDialog && isShowLocationTimeoutDialog) {}
});
}
@ -62,15 +61,11 @@ class LocationUtils {
}
}
}
}).catchError((err) {
print(err);
});
}).catchError((err) {});
} else {
if (isShowConfirmDialog) showErrorLocationDialog(false, failureCallBack: () {});
}
}).catchError((err) {
print(err);
});
}).catchError((err) {});
}
Future<bool> checkIfGPSIsEnabled() async {
@ -151,8 +146,8 @@ class LocationUtils {
Utils.saveNumFromPrefs(SharedPrefsConsts.user_lat, position?.latitude ?? 0.0);
Utils.saveNumFromPrefs(SharedPrefsConsts.user_lat, position?.longitude ?? 0.0);
AppState().setUserLat = position?.latitude ?? 0.0;
AppState().setUserLong = position?.longitude ?? 0.0;
appState.setUserLat = position?.latitude ?? 0.0;
appState.setUserLong = position?.longitude ?? 0.0;
// projectViewModel.setLatitudeLongitude(position?.latitude ?? 0.0, position?.longitude ?? 0.0);
}
@ -161,8 +156,8 @@ class LocationUtils {
Utils.saveNumFromPrefs(SharedPrefsConsts.user_lat, 0.0);
Utils.saveNumFromPrefs(SharedPrefsConsts.user_lat, 0.0);
AppState().setUserLat = 0.0;
AppState().setUserLong = 0.0;
appState.setUserLat = 0.0;
appState.setUserLong = 0.0;
}
Future<bool> requestPermissions() async {

@ -1,24 +1,28 @@
import 'dart:convert';
import 'package:crypto/crypto.dart' as crypto;
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:crypto/crypto.dart' as crypto;
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:google_api_availability/google_api_availability.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/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/main.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';
import 'package:hmg_patient_app_new/widgets/loading_dialog.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:lottie/lottie.dart';
import 'package:shared_preferences/shared_preferences.dart';
class Utils {
static AppState appState = getIt.get<AppState>();
static NavigationService navigationService = getIt.get<NavigationService>();
static bool _isLoadingVisible = false;
static bool get isLoading => _isLoadingVisible;
@ -47,14 +51,16 @@ class Utils {
}
static String getFreeSlotsTimeText(String startTime, {bool isAddHours = false}) {
// return DateFormat('hh:mm a', AppState().isArabic() ? "ar_SA" : "en_US").format(DateTime.tryParse(startTime)!.add(
// return DateFormat('hh:mm a', appState.isArabic() ? "ar_SA" : "en_US").format(DateTime.tryParse(startTime)!.add(
// Duration(
// hours: isAddHours ? 3 : 0,
// ),
// ));
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(
? 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,
),
@ -82,12 +88,9 @@ class Utils {
}
static String getMonthDayYearDateFormatted(DateTime dateTime) {
if (dateTime != null)
return AppState().isArabic()
? getMonthArabic(dateTime.month) + " " + dateTime.day.toString() + ", " + dateTime.year.toString()
: getMonth(dateTime.month) + " " + dateTime.day.toString() + ", " + dateTime.year.toString();
else
return "";
return appState.isArabic()
? getMonthArabic(dateTime.month) + " " + dateTime.day.toString() + ", " + dateTime.year.toString()
: getMonth(dateTime.month) + " " + dateTime.day.toString() + ", " + dateTime.year.toString();
}
/// get month by
@ -200,7 +203,7 @@ class Utils {
static void showLoadingDialog() {
_isLoadingVisible = true;
showDialog(
context: navigatorKey.currentContext!,
context: navigationService.navigatorKey.currentContext!,
barrierColor: Colors.black.withOpacity(0.5),
builder: (BuildContext context) => LoadingDialog(),
)
@ -217,7 +220,7 @@ class Utils {
try {
if (_isLoadingVisible) {
_isLoadingVisible = false;
Navigator.of(navigatorKey.currentContext!).pop();
Navigator.of(navigationService.navigatorKey.currentContext!).pop();
}
_isLoadingVisible = false;
} catch (e) {}
@ -242,9 +245,6 @@ class Utils {
static bool isSAUDIIDValid(String id, type) {
if (type == 1) {
if (id == null) {
return false;
}
try {
id = id.toString();
id = id.trim();
@ -304,13 +304,15 @@ class Utils {
}
static String removeHtmlTags(String htmlString) {
if (htmlString == null || htmlString.isEmpty) {
if (htmlString.isEmpty) {
return '';
}
// Replace HTML line breaks with newlines
var withLineBreaks =
htmlString.replaceAll(RegExp(r'<br\s*\/?>', multiLine: true), '\n').replaceAll(RegExp(r'<\/p>', multiLine: true), '\n').replaceAll(RegExp(r'<divider>', multiLine: true), '\n');
var withLineBreaks = htmlString
.replaceAll(RegExp(r'<br\s*\/?>', multiLine: true), '\n')
.replaceAll(RegExp(r'<\/p>', multiLine: true), '\n')
.replaceAll(RegExp(r'<divider>', multiLine: true), '\n');
// Remove all other HTML tags
var withoutTags = withLineBreaks.replaceAll(RegExp(r'<[^>]*>'), '');
@ -376,7 +378,20 @@ 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];
@ -458,13 +473,8 @@ class Utils {
);
}
static Future<bool> isGoogleServicesAvailable() async {
GooglePlayServicesAvailability availability = await GoogleApiAvailability
.instance
.checkGooglePlayServicesAvailability();
GooglePlayServicesAvailability availability = await GoogleApiAvailability.instance.checkGooglePlayServicesAvailability();
String status = availability.toString().split('.').last;
if (status == "success") {
return true;
@ -472,26 +482,17 @@ class Utils {
return false;
}
static Future<bool> checkConnection(
{bool bypassConnectionCheck = false}) async {
static Future<bool> checkConnection({bool bypassConnectionCheck = false}) async {
if (bypassConnectionCheck) return true;
List<ConnectivityResult> connectivityResult =
await (Connectivity().checkConnectivity());
if (connectivityResult.contains(ConnectivityResult.mobile) ||
connectivityResult.contains(ConnectivityResult.wifi)) {
List<ConnectivityResult> connectivityResult = await (Connectivity().checkConnectivity());
if (connectivityResult.contains(ConnectivityResult.mobile) || connectivityResult.contains(ConnectivityResult.wifi)) {
return true;
} else {
return false;
}
}
static String generateMd5Hash(String input) {
return crypto.md5.convert(utf8.encode(input)).toString();
}
}

@ -1,16 +1,15 @@
import 'dart:async';
import 'dart:developer';
import 'package:dartz/dartz.dart';
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/exceptions/api_exception.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/select_device_by_imei.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
abstract class AuthenticationRepo {
Future<Either<Failure, SelectDeviceByImeiRespModelElement?>> selectDeviceByImei({required String firebaseToken});
Future<Either<Failure, GenericApiModel<SelectDeviceByImeiRespModelElement>>> selectDeviceByImei({required String firebaseToken});
}
class AuthenticationRepoImp implements AuthenticationRepo {
@ -20,38 +19,43 @@ class AuthenticationRepoImp implements AuthenticationRepo {
AuthenticationRepoImp({required this.loggerService, required this.apiClient});
@override
Future<Either<Failure, SelectDeviceByImeiRespModelElement?>> selectDeviceByImei({
Future<Either<Failure, GenericApiModel<SelectDeviceByImeiRespModelElement>>> selectDeviceByImei({
required String firebaseToken,
}) async {
final mapDevice = {"IMEI": firebaseToken};
try {
GenericApiModel<SelectDeviceByImeiRespModelElement>? apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.selectDeviceImei,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus}) {
try {
final list = response['Patient_SELECTDeviceIMEIbyIMEIList'] as List<dynamic>?;
if (list == null || list.isEmpty) {
throw Exception("Device list is empty");
}
final completer = Completer<Either<Failure, SelectDeviceByImeiRespModelElement?>>();
await apiClient.post(
ApiConsts.selectDeviceImei,
body: mapDevice,
onSuccess: (response, statusCode) {
try {
final SelectDeviceByImeiRespModelElement model =
SelectDeviceByImeiRespModelElement.fromJson(response['Patient_SELECTDeviceIMEIbyIMEIList'][0]);
completer.complete(Right(model));
} catch (e) {
completer.complete(Left(ServerFailure(e.toString())));
}
},
onFailure: (error, statusCode) {
loggerService.logInfo(("$error - $statusCode").toString());
completer.complete(Left(ServerFailure(error)));
},
);
return await completer.future;
} on APIException catch (e) {
loggerService.errorLogs(e.toString());
return Left(ServerFailure(e.message));
final model = SelectDeviceByImeiRespModelElement.fromJson(list[0] as Map<String, dynamic>);
apiResponse = GenericApiModel<SelectDeviceByImeiRespModelElement>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: model,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
loggerService.errorLogs(e.toString());
return Left(ServerFailure(e.toString()));
return Left(UnknownFailure(e.toString()));
}
}
}

@ -1,46 +1,37 @@
import 'dart:developer';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/api_consts.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_repo.dart';
import 'package:hmg_patient_app_new/features/authentication/models/check_activation_code_request_register.dart';
import 'package:hmg_patient_app_new/services/dialog_service.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
class AuthenticationViewModel extends ChangeNotifier {
AuthenticationRepo authenticationRepo;
AppState appState;
ErrorHandlerService errorHandlerService;
DialogService dialogService;
AuthenticationViewModel({
required this.appState,
required this.authenticationRepo,
required this.errorHandlerService,
required this.dialogService,
});
final TextEditingController nationalIdController = TextEditingController();
final TextEditingController phoneNumberController = TextEditingController();
Future<void> selectDeviceImei({
Function(dynamic)? onSuccess,
Function(String)? onError
}) async {
final String firebaseToken =
"cIkkB7h7Q7uoFkC4Qv82xG:APA91bEb53Z9XzqymCIctaLxCoMX6bm9fuKlWILQ59uUqfwhCoD42AOP1-jWGB1WYd9BVN5PT2pUUFxrT07vcNg1KH9OH39mrPgCl0m21XVIgWrzNnCkufg";
final resultEither =
await authenticationRepo.selectDeviceByImei(firebaseToken: firebaseToken);
resultEither.fold(
(failure) {
notifyListeners();
if (onError != null) onError(failure.message);
},
(data) {
log("resultEither: ${data.toString()} ");
notifyListeners();
if (onSuccess != null) onSuccess(data);
Future<void> selectDeviceImei({Function(dynamic)? onSuccess, Function(String)? onError}) async {
String firebaseToken =
"dOGRRszQQMGe_9wA5Hx3kO:APA91bFV5IcIJXvcCXXk0tc2ddtZgWwCPq7sGSuPr-YW7iiJpQZKgFGN9GAzCVOWL8MfheaP1slE8MdxB7lczdPBGdONQ7WbMmhgHcsUCUktq-hsapGXXqc";
final result = await authenticationRepo.selectDeviceByImei(firebaseToken: firebaseToken);
result.fold(
(failure) async => await errorHandlerService.handleError(failure),
(apiResponse) {
if (apiResponse.messageStatus == 2) {
dialogService.showErrorDialog(apiResponse.errorMessage!);
} else if (apiResponse.messageStatus == 1) {
// move to next api call
}
},
);
}

@ -1,77 +1,73 @@
// To parse this JSON data, do
//
// final selectDeviceByImeiRespModel = selectDeviceByImeiRespModelFromJson(jsonString);
import 'dart:convert';
Map<String, dynamic> selectDeviceByImeiRespModelFromJson(String str) => Map.from(json.decode(str)).map((k, v) => MapEntry<String, dynamic>(k, v));
Map<String, dynamic> selectDeviceByImeiRespModelFromJson(String str) => Map<String, dynamic>.from(json.decode(str));
String selectDeviceByImeiRespModelToJson(Map<String, dynamic> data) => json.encode(Map.from(data).map((k, v) => MapEntry<String, dynamic>(k, v)));
String selectDeviceByImeiRespModelToJson(Map<String, dynamic> data) => json.encode(Map<String, dynamic>.from(data));
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;
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,
this.id,
this.imei,
this.logInType,
this.patientId,
this.outSa,
this.mobile,
this.identificationNo,
this.name,
this.nameN,
this.createdOn,
this.editedOn,
this.biometricEnabled,
this.patientType,
this.preferredLanguage,
});
factory SelectDeviceByImeiRespModelElement.fromJson(Map<String, dynamic> 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"],
);
id: json["ID"] as int?,
imei: json["IMEI"] as String?,
logInType: json["LogInType"] as int?,
patientId: json["PatientID"] as int?,
outSa: json["OutSA"] as bool?,
mobile: json["Mobile"] as String?,
identificationNo: json["IdentificationNo"] as String?,
name: json["Name"] as String?,
nameN: json["NameN"] as String?,
createdOn: json["CreatedOn"] as String?,
editedOn: json["EditedOn"] as String?,
biometricEnabled: json["BiometricEnabled"] as bool?,
patientType: json["PatientType"] as int?,
preferredLanguage: json["PreferredLanguage"] as int?,
);
Map<String, dynamic> 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,
};
"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,
};
}

@ -10,16 +10,14 @@ import 'package:hmg_patient_app_new/features/authentication/authentication_view_
import 'package:hmg_patient_app_new/providers/bottom_navigation_provider.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/services/navigation_service.dart';
import 'package:hmg_patient_app_new/theme/app_theme.dart';
import 'package:provider/provider.dart';
import 'package:provider/single_child_widget.dart';
import 'core/utils/size_utils.dart';
import 'firebase_options.dart';
var globalMessengerKey = GlobalKey<ScaffoldMessengerState>();
final navigatorKey = GlobalKey<NavigatorState>();
@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
@ -57,7 +55,12 @@ void main() async {
create: (_) => BottomNavigationProvider(),
),
ChangeNotifierProvider<AuthenticationViewModel>(
create: (_) => AuthenticationViewModel(authenticationRepo: getIt(), appState: getIt()),
create: (_) => AuthenticationViewModel(
authenticationRepo: getIt(),
appState: getIt(),
dialogService: getIt(),
errorHandlerService: getIt(),
),
),
], child: MyApp()),
),
@ -94,7 +97,7 @@ class MyApp extends StatelessWidget {
initialRoute: AppRoutes.initialRoute,
routes: AppRoutes.routes,
theme: AppTheme.getTheme(EasyLocalization.of(context)?.locale.languageCode == "ar"),
navigatorKey: navigatorKey,
navigatorKey: getIt.get<NavigationService>().navigatorKey,
);
},
);

@ -3,11 +3,13 @@ import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/int_extensions.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/home/data/landing_page_data.dart';
import 'package:hmg_patient_app_new/presentation/home/widgets/large_service_card.dart';
@ -25,9 +27,10 @@ class LandingPage extends StatefulWidget {
}
class _LandingPageState extends State<LandingPage> {
@override
Widget build(BuildContext context) {
AppState appState = getIt.get<AppState>();
final AuthenticationViewModel authenticationViewModel = context.read<AuthenticationViewModel>();
return Consumer<BottomNavigationProvider>(builder: (context, navigationProvider, child) {
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
@ -43,10 +46,8 @@ class _LandingPageState extends State<LandingPage> {
children: [
CustomButton(
text: LocaleKeys.loginOrRegister.tr(context: context),
onPressed: () {
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (BuildContext context) => LandingPage()),
);
onPressed: () async {
await authenticationViewModel.selectDeviceImei();
},
backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA),
@ -66,7 +67,7 @@ class _LandingPageState extends State<LandingPage> {
),
),
SizedBox(height: 16.h),
AppState().isAuthenticated
appState.isAuthenticated
? Column(
children: [
Container(
@ -257,7 +258,7 @@ class _LandingPageState extends State<LandingPage> {
),
),
SizedBox(height: 16.h),
AppState().isAuthenticated
appState.isAuthenticated
? Column(
children: [
Row(

@ -0,0 +1,71 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
abstract class DialogService {
Future<void> showErrorDialog(String message);
}
class DialogServiceImp implements DialogService {
final NavigationService navigationService;
DialogServiceImp({required this.navigationService});
@override
Future<void> showErrorDialog(String message) async {
final context = navigationService.navigatorKey.currentContext;
if (context == null) return;
await showModalBottomSheet(
context: context,
isScrollControlled: false,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
),
builder: (_) => _ErrorBottomSheet(message: message),
);
}
}
class _ErrorBottomSheet extends StatelessWidget {
final String message;
const _ErrorBottomSheet({required this.message});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.error_outline, color: Colors.red, size: 40),
const SizedBox(height: 12),
Text(
"Error",
style: Theme.of(context).textTheme.titleLarge?.copyWith(
color: Colors.red,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Text(
message,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodyMedium,
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () => Navigator.of(context).pop(),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: const Text("OK"),
),
],
),
);
}
}

@ -0,0 +1,52 @@
import 'dart:io';
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/dialog_service.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
abstract class ErrorHandlerService {
Future<void> handleError(Failure failure);
}
class ErrorHandlerServiceImp implements ErrorHandlerService {
final DialogService dialogService;
final LoggerService loggerService;
final NavigationService navigationService;
ErrorHandlerServiceImp({
required this.dialogService,
required this.loggerService,
required this.navigationService,
});
@override
Future<void> handleError(Failure failure) async {
if (failure is APIException) {
loggerService.errorLogs("API Exception: ${failure.message}");
} else if (failure is ServerFailure) {
loggerService.errorLogs("Server Failure: ${failure.message}");
await _showDialog(failure);
} else if (failure is DataParsingFailure) {
loggerService.errorLogs("Data Parsing Failure: ${failure.message}");
await _showDialog(failure, title: "Data Error");
} else if (failure is HttpException) {
loggerService.errorLogs("Http Exception: ${failure.message}");
await _showDialog(failure, title: "Network Error");
} else if (failure is UnknownFailure) {
loggerService.errorLogs("Unknown Failure: ${failure.message}");
await _showDialog(failure, title: "Unknown Error");
} else if (failure is InvalidCredentials) {
loggerService.errorLogs("Invalid Credentials : ${failure.message}");
await _showDialog(failure, title: "Unknown Error");
} else {
loggerService.errorLogs("Unhandled failure type: $failure");
await _showDialog(failure, title: "Error");
}
}
Future<void> _showDialog(Failure failure, {String title = "Error"}) async {
await dialogService.showErrorDialog(failure.message);
}
}

@ -0,0 +1,15 @@
import 'package:flutter/material.dart';
class NavigationService {
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
BuildContext? get context => navigatorKey.currentContext;
Future<T?> push<T>(Route<T> route) {
return navigatorKey.currentState!.push(route);
}
void pop<T extends Object?>([T? result]) {
navigatorKey.currentState!.pop(result);
}
}
Loading…
Cancel
Save