Compare commits

..

2 Commits

@ -115,6 +115,9 @@ class BaseAppClient {
// } // }
body['DeviceTypeID'] = Platform.isAndroid ? '1' : '2'; body['DeviceTypeID'] = Platform.isAndroid ? '1' : '2';
if( body['SetupID'] != null) {
body['SetupID'] = body['SetupID'].trim();
}
// body['ClinicID'] = 501; // body['ClinicID'] = 501;
// body['ProjectID'] = 12; // body['ProjectID'] = 12;

@ -18,7 +18,7 @@ const BASE_URL = 'https://hmgwebservices.com/';
// const BASE_URL = 'https://uat.hmgwebservices.com/'; // const BASE_URL = 'https://uat.hmgwebservices.com/';
// const BASE_URL = 'https://webservices.hmg.com/'; // const BASE_URL = 'https://webservices.hmg.com/';
//
// const BASE_URL = 'https://vidauat.cloudsolutions.com.sa/'; //Vida Plus URL // const BASE_URL = 'https://vidauat.cloudsolutions.com.sa/'; //Vida Plus URL
// const BASE_URL = 'https://vidamergeuat.cloudsolutions.com.sa/'; //Vida Plus URL // const BASE_URL = 'https://vidamergeuat.cloudsolutions.com.sa/'; //Vida Plus URL

@ -33,7 +33,7 @@ class InsertIMEIDetailsModel {
String? vidaRefreshTokenID; String? vidaRefreshTokenID;
dynamic? password; dynamic? password;
int? loginDoctorID; int? loginDoctorID;
String? zipCode;
InsertIMEIDetailsModel( InsertIMEIDetailsModel(
{this.iMEI, {this.iMEI,
this.logInTypeID, this.logInTypeID,
@ -68,7 +68,9 @@ class InsertIMEIDetailsModel {
this.vidaAuthTokenID, this.vidaAuthTokenID,
this.vidaRefreshTokenID, this.vidaRefreshTokenID,
this.password, this.password,
this.loginDoctorID}); this.loginDoctorID,
this.zipCode
});
InsertIMEIDetailsModel.fromJson(Map<String, dynamic> json) { InsertIMEIDetailsModel.fromJson(Map<String, dynamic> json) {
iMEI = json['IMEI']; iMEI = json['IMEI'];
@ -105,6 +107,7 @@ class InsertIMEIDetailsModel {
vidaRefreshTokenID = json['VidaRefreshTokenID']; vidaRefreshTokenID = json['VidaRefreshTokenID'];
password = json['Password']; password = json['Password'];
loginDoctorID = json['LoginDoctorID']; loginDoctorID = json['LoginDoctorID'];
zipCode = json['ZipCode'];
} }
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
@ -143,7 +146,7 @@ class InsertIMEIDetailsModel {
data['VidaRefreshTokenID'] = this.vidaRefreshTokenID; data['VidaRefreshTokenID'] = this.vidaRefreshTokenID;
data['Password'] = this.password; data['Password'] = this.password;
data['LoginDoctorID'] = this.loginDoctorID; data['LoginDoctorID'] = this.loginDoctorID;
data['ZipCode'] = this.zipCode;
return data; return data;
} }
} }

@ -61,7 +61,7 @@ class AuthenticationViewModel extends BaseViewModel {
UserModel userInfo = UserModel(); UserModel userInfo = UserModel();
final LocalAuthentication auth = LocalAuthentication(); final LocalAuthentication auth = LocalAuthentication();
List<BiometricType>? _availableBiometrics; List<BiometricType>? _availableBiometrics;
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance; late FirebaseMessaging _firebaseMessaging;
bool isLogin = false; bool isLogin = false;
bool unverified = false; bool unverified = false;
@ -71,8 +71,30 @@ class AuthenticationViewModel extends BaseViewModel {
bool isHuawei = false; bool isHuawei = false;
AuthenticationViewModel({bool checkDeviceInfo = false}) { AuthenticationViewModel({bool checkDeviceInfo = false}) {
getDeviceInfoFromFirebase(); FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance;
getDoctorProfile(); // getDeviceInfoFromFirebase();
// getDoctorProfile();
}
Future<void> initializeApp({String? token}) async {
setState(ViewState.Busy);
try {
localToken = token;
await getDeviceInfoFromFirebase();
if (imeiDetails.isNotEmpty) {
user = imeiDetails.first;
sharedPref.setObj(LAST_LOGIN_USER, user!);
unverified = true;
appStatus = APP_STATUS.UNVERIFIED;
} else {
appStatus = APP_STATUS.UNAUTHENTICATED;
}
setState(ViewState.Idle);
} catch (e) {
error = e.toString();
appStatus = APP_STATUS.UNAUTHENTICATED;
setState(ViewState.ErrorLocal);
}
} }
/// Insert Device IMEI /// Insert Device IMEI
@ -97,12 +119,12 @@ class AuthenticationViewModel extends BaseViewModel {
insertIMEIDetailsModel.titleDescriptionN = profileInfo['Title_DescriptionN']; insertIMEIDetailsModel.titleDescriptionN = profileInfo['Title_DescriptionN'];
insertIMEIDetailsModel.projectID = await sharedPref.getInt(PROJECT_ID); insertIMEIDetailsModel.projectID = await sharedPref.getInt(PROJECT_ID);
insertIMEIDetailsModel.doctorID = loggedIn != null ? loggedIn['List_MemberInformation'][0]['MemberID'] : user!.doctorID; insertIMEIDetailsModel.doctorID = loggedIn != null ? loggedIn['List_MemberInformation'][0]['MemberID'] : user!.doctorID;
insertIMEIDetailsModel.outSA = loggedIn != null ? loggedIn['PatientOutSA'] : user!.outSA; insertIMEIDetailsModel.outSA = loggedIn != null ? loggedIn['ZipCode'].toString() =='966' ? false: true : user!.outSA;
insertIMEIDetailsModel.vidaAuthTokenID = await sharedPref.getString(VIDA_AUTH_TOKEN_ID); insertIMEIDetailsModel.vidaAuthTokenID = await sharedPref.getString(VIDA_AUTH_TOKEN_ID);
insertIMEIDetailsModel.vidaRefreshTokenID = await sharedPref.getString(VIDA_REFRESH_TOKEN_ID); insertIMEIDetailsModel.vidaRefreshTokenID = await sharedPref.getString(VIDA_REFRESH_TOKEN_ID);
insertIMEIDetailsModel.password = userInfo.password; insertIMEIDetailsModel.password = userInfo.password;
insertIMEIDetailsModel.loginDoctorID = loggedUser != null ? loggedUser!.listMemberInformation![0].employeeID : user!.editedBy; insertIMEIDetailsModel.loginDoctorID = loggedUser != null ? loggedUser!.listMemberInformation![0].employeeID : user!.editedBy;
insertIMEIDetailsModel.zipCode = loggedIn != null ? loggedIn['ZipCode'] : user!.zipCode;
await _authService.insertDeviceImei(insertIMEIDetailsModel); await _authService.insertDeviceImei(insertIMEIDetailsModel);
if (_authService.hasError) { if (_authService.hasError) {
error = _authService.error; error = _authService.error;
@ -332,48 +354,60 @@ class AuthenticationViewModel extends BaseViewModel {
// } // }
/// call firebase service to check if the user already login in before or not /// call firebase service to check if the user already login in before or not
getDeviceInfoFromFirebase() async { // getDeviceInfoFromFirebase() async {
// await checkIsHuawei(); // // await checkIsHuawei();
var token; // var token;
var APNSToken; // var APNSToken;
_firebaseMessaging.setAutoInitEnabled(true); // _firebaseMessaging.setAutoInitEnabled(true);
if (Platform.isIOS) { // if (Platform.isIOS) {
_firebaseMessaging.requestPermission(); // _firebaseMessaging.requestPermission();
} // }
setState(ViewState.Busy); // setState(ViewState.Busy);
// try {
// if (Platform.isIOS) {
// APNSToken = await _firebaseMessaging.getAPNSToken();
// print("APNS TOKEN: $APNSToken");
// }
// token = await _firebaseMessaging.getToken();
// print("Device TOKEN: $token");
// } catch (ex) {
// print(ex);
// }
// if (localToken == "") {
// localToken = token;
// await _authService.selectDeviceImei(localToken);
// if (_authService.hasError) {
// error = _authService.error;
// setState(ViewState.ErrorLocal);
// } else {
// if (_authService.dashboardItemsList.length > 0) {
// user = _authService.dashboardItemsList[0];
// sharedPref.setObj(LAST_LOGIN_USER, _authService.dashboardItemsList[0]);
// // await sharedPref.setString(VIDA_REFRESH_TOKEN_ID, user!.vidaRefreshTokenID!);
// // await sharedPref.setString(VIDA_AUTH_TOKEN_ID, user!.vidaAuthTokenID!);
// this.unverified = true;
// }
// setState(ViewState.Idle);
// }
// } else {
// setState(ViewState.Idle);
// }
// }
Future<void> getDeviceInfoFromFirebase() async {
try { try {
if (Platform.isIOS) { // Only call API if we have a token
APNSToken = await _firebaseMessaging.getAPNSToken(); if (localToken != null && localToken!.isNotEmpty) {
print("APNS TOKEN: $APNSToken"); await _authService.selectDeviceImei(localToken!);
}
token = await _firebaseMessaging.getToken();
print("Device TOKEN: $token");
} catch (ex) {
print(ex);
}
if (localToken == "") {
localToken = token;
await _authService.selectDeviceImei(localToken); if (_authService.hasError) {
if (_authService.hasError) { throw Exception(_authService.error);
error = _authService.error;
setState(ViewState.ErrorLocal);
} else {
if (_authService.dashboardItemsList.length > 0) {
user = _authService.dashboardItemsList[0];
sharedPref.setObj(LAST_LOGIN_USER, _authService.dashboardItemsList[0]);
// await sharedPref.setString(VIDA_REFRESH_TOKEN_ID, user!.vidaRefreshTokenID!);
// await sharedPref.setString(VIDA_AUTH_TOKEN_ID, user!.vidaAuthTokenID!);
this.unverified = true;
} }
setState(ViewState.Idle);
} }
} else { } catch (e) {
setState(ViewState.Idle); // debugPrint("Error getting device info: $e");
rethrow;
} }
} }
/// determine the status of the app /// determine the status of the app
APP_STATUS get status { APP_STATUS get status {
if (state == ViewState.Busy) { if (state == ViewState.Busy) {
@ -418,8 +452,11 @@ class AuthenticationViewModel extends BaseViewModel {
setState(ViewState.Idle); setState(ViewState.Idle);
} }
deleteUser() { deleteUser() async{
user = GetIMEIDetailsModel(); await Utils.clearSharedPref();
user = null;
doctorProfile = null;
appStatus = APP_STATUS.UNAUTHENTICATED;
unverified = false; unverified = false;
isLogin = false; isLogin = false;
} }

@ -32,6 +32,7 @@ import 'package:doctor_app_flutter/screens/patients/profile/vte_assessment/vte_a
import 'package:doctor_app_flutter/screens/prescription/new_prescriptions_page.dart'; import 'package:doctor_app_flutter/screens/prescription/new_prescriptions_page.dart';
import 'package:doctor_app_flutter/screens/prescription/old_prescriptions_page.dart'; import 'package:doctor_app_flutter/screens/prescription/old_prescriptions_page.dart';
import 'package:doctor_app_flutter/screens/procedures/procedure_screen.dart'; import 'package:doctor_app_flutter/screens/procedures/procedure_screen.dart';
import 'package:doctor_app_flutter/screens/splash/splash_screen.dart';
import 'package:doctor_app_flutter/screens/video_call_zoom/zoom_video_call.dart'; import 'package:doctor_app_flutter/screens/video_call_zoom/zoom_video_call.dart';
import './screens/auth/login_screen.dart'; import './screens/auth/login_screen.dart';
import './screens/patients/profile/vital_sign/vital_sign_details_screen.dart'; import './screens/patients/profile/vital_sign/vital_sign_details_screen.dart';
@ -95,7 +96,8 @@ const String ZOOM_CALL_PAGE ='zoom-call';
//todo: change the routing way. //todo: change the routing way.
var routes = { var routes = {
ROOT: (_) => RootPage(), // ROOT: (_) => RootPage(),
ROOT: (_) => SplashScreen(),
HOME: (_) => LandingPage(), HOME: (_) => LandingPage(),
LOGIN: (_) => LoginScreen(), LOGIN: (_) => LoginScreen(),
VERIFICATION_METHODS: (_) => VerificationMethodsScreen(), VERIFICATION_METHODS: (_) => VerificationMethodsScreen(),

@ -4,6 +4,7 @@ import 'package:doctor_app_flutter/core/enum/view_state.dart';
import 'package:doctor_app_flutter/core/model/hospitals/get_hospitals_response_model.dart'; import 'package:doctor_app_flutter/core/model/hospitals/get_hospitals_response_model.dart';
import 'package:doctor_app_flutter/core/viewModel/authentication_view_model.dart'; import 'package:doctor_app_flutter/core/viewModel/authentication_view_model.dart';
import 'package:doctor_app_flutter/core/viewModel/project_view_model.dart'; import 'package:doctor_app_flutter/core/viewModel/project_view_model.dart';
import 'package:doctor_app_flutter/screens/auth/verification_methods_screen.dart';
import 'package:doctor_app_flutter/utils/translations_delegate_base_utils.dart'; import 'package:doctor_app_flutter/utils/translations_delegate_base_utils.dart';
import 'package:doctor_app_flutter/utils/utils.dart'; import 'package:doctor_app_flutter/utils/utils.dart';
import 'package:doctor_app_flutter/widgets/shared/buttons/app_buttons_widget.dart'; import 'package:doctor_app_flutter/widgets/shared/buttons/app_buttons_widget.dart';
@ -201,16 +202,17 @@ class _LoginScreenState extends State<LoginScreen> {
Utils.showErrorToast(authenticationViewModel.error); Utils.showErrorToast(authenticationViewModel.error);
} else { } else {
GifLoaderDialogUtils.hideDialog(context); GifLoaderDialogUtils.hideDialog(context);
authenticationViewModel.setUnverified(true, isFromLogin: true); // authenticationViewModel.setUnverified(true, isFromLogin: true);
// Navigator.of(context).pushReplacement( Navigator.of(context).pushReplacement(
// MaterialPageRoute( MaterialPageRoute(
// builder: (BuildContext context) => builder: (BuildContext context) =>
// VerificationMethodsScreen( VerificationMethodsScreen(
// password: authenticationViewModel.userInfo.password, password: authenticationViewModel.userInfo.password,
// isFromLogin: true, isMoreOption: true,
// ), // isFromLogin: true,
// ), ),
// ); ),
);
} }
} }
} }
@ -235,8 +237,10 @@ class _LoginScreenState extends State<LoginScreen> {
projectsList = authenticationViewModel.hospitals; projectsList = authenticationViewModel.hospitals;
setState(() { setState(() {
projectViewModel.setVidaPlusProjectList(authenticationViewModel.vidaPlusProjectList); projectViewModel.setVidaPlusProjectList(authenticationViewModel.vidaPlusProjectList);
authenticationViewModel.userInfo.projectID = projectsList[0].facilityId; if(projectsList.isNotEmpty) {
projectIdController.text = projectsList[0].facilityName!; authenticationViewModel.userInfo.projectID = projectsList.first.facilityId;
projectIdController.text = projectsList.first.facilityName!;
}
}); });
} }
} }

@ -7,6 +7,7 @@ import 'package:doctor_app_flutter/core/enum/view_state.dart';
import 'package:doctor_app_flutter/core/service/authentication_service.dart'; import 'package:doctor_app_flutter/core/service/authentication_service.dart';
import 'package:doctor_app_flutter/core/viewModel/authentication_view_model.dart'; import 'package:doctor_app_flutter/core/viewModel/authentication_view_model.dart';
import 'package:doctor_app_flutter/core/viewModel/project_view_model.dart'; import 'package:doctor_app_flutter/core/viewModel/project_view_model.dart';
import 'package:doctor_app_flutter/landing_page.dart';
import 'package:doctor_app_flutter/utils/date-utils.dart'; import 'package:doctor_app_flutter/utils/date-utils.dart';
import 'package:doctor_app_flutter/utils/translations_delegate_base_utils.dart'; import 'package:doctor_app_flutter/utils/translations_delegate_base_utils.dart';
import 'package:doctor_app_flutter/widgets/auth/sms-popup.dart'; import 'package:doctor_app_flutter/widgets/auth/sms-popup.dart';
@ -23,6 +24,7 @@ import '../../config/size_config.dart';
import '../../utils/dr_app_shared_pref.dart'; import '../../utils/dr_app_shared_pref.dart';
import '../../utils/utils.dart'; import '../../utils/utils.dart';
import '../../widgets/auth/verification_methods_list.dart'; import '../../widgets/auth/verification_methods_list.dart';
import 'login_screen.dart';
DrAppSharedPreferances sharedPref = new DrAppSharedPreferances(); DrAppSharedPreferances sharedPref = new DrAppSharedPreferances();
Utils helpers = Utils(); Utils helpers = Utils();
@ -30,9 +32,10 @@ Utils helpers = Utils();
///TODO Elham* check if this still in user or not ///TODO Elham* check if this still in user or not
class VerificationMethodsScreen extends StatefulWidget { class VerificationMethodsScreen extends StatefulWidget {
final password; final password;
final isMoreOption;
VerificationMethodsScreen({ VerificationMethodsScreen({
this.password, this.password,
this.isMoreOption = false,
}); });
@override @override
@ -47,6 +50,12 @@ class _VerificationMethodsScreenState extends State<VerificationMethodsScreen> {
AuthMethodTypes? selectedOption; AuthMethodTypes? selectedOption;
late AuthenticationViewModel authenticationViewModel; late AuthenticationViewModel authenticationViewModel;
@override
void initState() {
this.isMoreOption = widget.isMoreOption;
super.initState();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
projectsProvider = Provider.of<ProjectViewModel>(context); projectsProvider = Provider.of<ProjectViewModel>(context);
@ -339,7 +348,11 @@ class _VerificationMethodsScreenState extends State<VerificationMethodsScreen> {
//fontWeight: FontWeight.w700, //fontWeight: FontWeight.w700,
onPressed: () { onPressed: () {
authenticationViewModel.deleteUser(); authenticationViewModel.deleteUser();
authenticationViewModel.setAppStatus(APP_STATUS.UNAUTHENTICATED); // authenticationViewModel.setAppStatus(APP_STATUS.UNAUTHENTICATED);
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (_) => LoginScreen()),
);
}, },
), ),
SizedBox( SizedBox(
@ -486,7 +499,17 @@ class _VerificationMethodsScreenState extends State<VerificationMethodsScreen> {
if (authenticationViewModel.state == ViewState.ErrorLocal) { if (authenticationViewModel.state == ViewState.ErrorLocal) {
Utils.showErrorToast(authenticationViewModel.error); Utils.showErrorToast(authenticationViewModel.error);
} else { } else {
authenticationViewModel.setAppStatus(APP_STATUS.AUTHENTICATED); Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (BuildContext context) =>
LandingPage(
// password: authenticationViewModel.userInfo.password,
// isMoreOption: true,
// isFromLogin: true,
),
),
);
// authenticationViewModel.setAppStatus(APP_STATUS.AUTHENTICATED);
} }
} }
} }

@ -0,0 +1,121 @@
import 'dart:async';
import 'dart:io';
import 'package:doctor_app_flutter/config/config.dart';
import 'package:doctor_app_flutter/core/service/authentication_service.dart';
import 'package:doctor_app_flutter/core/viewModel/authentication_view_model.dart';
import 'package:doctor_app_flutter/locator.dart';
import 'package:doctor_app_flutter/screens/auth/login_screen.dart';
import 'package:doctor_app_flutter/screens/auth/verification_methods_screen.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class SplashScreen extends StatefulWidget {
@override
_SplashScreenState createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance;
late AuthenticationViewModel _authViewModel;
@override
void initState() {
super.initState();
AppGlobal.CONTEX = context;
_initializeApp();
}
Future<void> _initializeApp() async {
try {
final String? token = await _getFirebaseToken();
_authViewModel = Provider.of<AuthenticationViewModel>(context, listen: false);
await _authViewModel.initializeApp(token: token);
_navigateBasedOnAuthState();
} catch (e) {
// Handle error and fallback to login
_navigateToLogin();
}
}
Future<String?> _getFirebaseToken() async {
try {
await Firebase.initializeApp();
Future.delayed(Duration(seconds: 1), () {
});
if (Platform.isIOS) {
// Request permissions first
final settings = await _firebaseMessaging.requestPermission(
alert: true,
announcement: false,
badge: true,
carPlay: false,
criticalAlert: false,
provisional: false,
sound: true,
);
debugPrint('Notification permissions: ${settings.authorizationStatus}');
// Get both FCM and APNS tokens
final fcmToken = await _firebaseMessaging.getToken();
debugPrint("FCM Token: $fcmToken");
// APNS token might not be immediately available
String? apnsToken;
int retries = 0;
while (apnsToken == null && retries < 3) {
apnsToken = await _firebaseMessaging.getAPNSToken();
if (apnsToken == null) {
await Future.delayed(Duration(milliseconds: 500));
retries++;
}
}
debugPrint("APNS Token: $apnsToken");
return apnsToken ?? fcmToken; // Fallback to FCM token if APNS is null
} else {
return await _firebaseMessaging.getToken();
}
} catch (e) {
debugPrint("Failed to get FCM token: $e");
return null;
}
}
void _navigateBasedOnAuthState() {
if (_authViewModel.status == APP_STATUS.UNVERIFIED) {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (_) => VerificationMethodsScreen()),
);
} else if (_authViewModel.status == APP_STATUS.UNAUTHENTICATED) {
_navigateToLogin();
} else {
_navigateToLogin();
}
}
void _navigateToLogin() {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (_) => LoginScreen()),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xffF8F8F8),
body: Center(
child: Image.asset('assets/images/dr_app_logo.png'),
),
);
}
}

@ -1,6 +1,9 @@
import 'package:doctor_app_flutter/core/service/NavigationService.dart';
import 'package:doctor_app_flutter/core/viewModel/authentication_view_model.dart'; import 'package:doctor_app_flutter/core/viewModel/authentication_view_model.dart';
import 'package:doctor_app_flutter/core/viewModel/project_view_model.dart'; import 'package:doctor_app_flutter/core/viewModel/project_view_model.dart';
import 'package:doctor_app_flutter/icons_app/doctor_app_icons.dart'; import 'package:doctor_app_flutter/icons_app/doctor_app_icons.dart';
import 'package:doctor_app_flutter/locator.dart';
import 'package:doctor_app_flutter/routes.dart';
import 'package:doctor_app_flutter/screens/doctor_schedule/doctor_schedule.dart'; import 'package:doctor_app_flutter/screens/doctor_schedule/doctor_schedule.dart';
import 'package:doctor_app_flutter/screens/reschedule_leaves/add_reschedule_leave.dart'; import 'package:doctor_app_flutter/screens/reschedule_leaves/add_reschedule_leave.dart';
import 'package:doctor_app_flutter/utils/translations_delegate_base_utils.dart'; import 'package:doctor_app_flutter/utils/translations_delegate_base_utils.dart';
@ -189,6 +192,7 @@ class _AppDrawerState extends State<AppDrawer> {
onTap: () async { onTap: () async {
Navigator.pop(context); Navigator.pop(context);
await authenticationViewModel.logout(isFromLogin: false); await authenticationViewModel.logout(isFromLogin: false);
locator<NavigationService>().pushNamedAndRemoveUntil(ROOT);
}, },
), ),
], ],

Loading…
Cancel
Save