updates & fixes

pull/177/head
haroon amjad 2 months ago
parent 63cdced84e
commit cfd0c16738

@ -54,6 +54,8 @@ post_install do |installer|
'PERMISSION_EVENTS_FULL_ACCESS=1', 'PERMISSION_EVENTS_FULL_ACCESS=1',
## dart: PermissionGroup.reminders ## dart: PermissionGroup.reminders
'PERMISSION_REMINDERS=1', 'PERMISSION_REMINDERS=1',
## dart: PermissionGroup.notification
'PERMISSION_NOTIFICATIONS=1',
] ]
build_configuration.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64 i386' build_configuration.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64 i386'
build_configuration.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0' build_configuration.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0'

@ -208,10 +208,10 @@ class ApiClientImp implements ApiClient {
// Handle body encoding based on isBodyPlainText flag // Handle body encoding based on isBodyPlainText flag
final dynamic requestBody = isBodyPlainText ? body : json.encode(body); final dynamic requestBody = isBodyPlainText ? body : json.encode(body);
debugPrint("uri: ${Uri.parse(url.trim())}");
debugPrint("body: ${json.encode(body)}", wrapWidth: 2048);
final response = await http.post(Uri.parse(url.trim()), body: requestBody, headers: headers); final response = await http.post(Uri.parse(url.trim()), body: requestBody, headers: headers);
final int statusCode = response.statusCode; final int statusCode = response.statusCode;
log("uri: ${Uri.parse(url.trim())}");
log("body: ${json.encode(body)}");
// log("response.body: ${response.body}"); // log("response.body: ${response.body}");
if (statusCode < 200 || statusCode >= 400) { if (statusCode < 200 || statusCode >= 400) {
onFailure('Error While Fetching data', statusCode, failureType: StatusCodeFailure("Error While Fetching data")); onFailure('Error While Fetching data', statusCode, failureType: StatusCodeFailure("Error While Fetching data"));

@ -459,7 +459,6 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
await payfortViewModel.checkPaymentStatus( await payfortViewModel.checkPaymentStatus(
transactionID: transID, transactionID: transID,
onSuccess: (apiResponse) async { onSuccess: (apiResponse) async {
print(apiResponse.data);
if (payfortViewModel.payfortCheckPaymentStatusResponseModel!.responseMessage!.toLowerCase() == "success") { if (payfortViewModel.payfortCheckPaymentStatusResponseModel!.responseMessage!.toLowerCase() == "success") {
await myAppointmentsViewModel.createAdvancePayment( await myAppointmentsViewModel.createAdvancePayment(
paymentMethodName: selectedPaymentMethod, paymentMethodName: selectedPaymentMethod,
@ -517,6 +516,7 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
}); });
}); });
} else { } else {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight( showCommonBottomSheetWithoutHeight(
context, context,
child: Utils.getErrorWidget(loadingText: LocaleKeys.paymentFailedPleaseTryAgain.tr(context: context)), child: Utils.getErrorWidget(loadingText: LocaleKeys.paymentFailedPleaseTryAgain.tr(context: context)),
@ -564,14 +564,7 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
} }
startApplePay() async { startApplePay() async {
showCommonBottomSheet(context, LoaderBottomSheet.showLoader(loadingText: LocaleKeys.loadingText.tr(context: context));
child: Utils.getLoadingWidget(),
callBackFunc: (str) {},
title: "",
height: ResponsiveExtension.screenHeight * 0.3,
isCloseButtonVisible: false,
isDismissible: false,
isFullScreen: false);
transID = Utils.getAppointmentTransID( transID = Utils.getAppointmentTransID(
widget.patientAppointmentHistoryResponseModel.projectID, widget.patientAppointmentHistoryResponseModel.projectID,
widget.patientAppointmentHistoryResponseModel.clinicID, widget.patientAppointmentHistoryResponseModel.clinicID,
@ -642,7 +635,7 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
currency: appState.getAuthenticatedUser()!.outSa! == 0 ? "SAR" : "AED", currency: appState.getAuthenticatedUser()!.outSa! == 0 ? "SAR" : "AED",
onFailed: (failureResult) async { onFailed: (failureResult) async {
log("failureResult: ${failureResult.message.toString()}"); log("failureResult: ${failureResult.message.toString()}");
Navigator.of(context).pop(); LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight( showCommonBottomSheetWithoutHeight(
context, context,
child: Utils.getErrorWidget(loadingText: failureResult.message.toString()), child: Utils.getErrorWidget(loadingText: failureResult.message.toString()),
@ -652,6 +645,7 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
); );
}, },
onSucceeded: (successResult) async { onSucceeded: (successResult) async {
LoaderBottomSheet.hideLoader();
log("successResult: ${successResult.responseMessage.toString()}"); log("successResult: ${successResult.responseMessage.toString()}");
selectedPaymentMethod = successResult.paymentOption ?? "VISA"; selectedPaymentMethod = successResult.paymentOption ?? "VISA";
checkPaymentStatus(); checkPaymentStatus();

@ -224,6 +224,32 @@ class AppointmentCard extends StatelessWidget {
} }
Widget _buildActionArea(BuildContext context, AppState appState) { Widget _buildActionArea(BuildContext context, AppState appState) {
if ((patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false) && AppointmentType.isArrived(patientAppointmentHistoryResponseModel)) {
return CustomButton(
text: LocaleKeys.viewDetails.tr(context: context),
onPressed: () {
Navigator.of(context)
.push(
CustomPageRoute(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel),
),
)
.then((_) {
myAppointmentsViewModel.initAppointmentsViewModel();
myAppointmentsViewModel.getPatientAppointments(true, false);
});
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14.f,
fontWeight: FontWeight.w500,
borderRadius: 12.r,
padding: EdgeInsets.symmetric(horizontal: 10.w),
// height: isTablet || isFoldable ? 46.h : 40.h,
height: 40.h,
);
} else {
if (isFromMedicalReport) { if (isFromMedicalReport) {
if (isForEyeMeasurements) { if (isForEyeMeasurements) {
return SizedBox.shrink(); return SizedBox.shrink();
@ -263,10 +289,8 @@ class AppointmentCard extends StatelessWidget {
: CustomButton( : CustomButton(
text: AppointmentType.getNextActionText(patientAppointmentHistoryResponseModel.nextAction), text: AppointmentType.getNextActionText(patientAppointmentHistoryResponseModel.nextAction),
onPressed: () => _goToDetails(context), onPressed: () => _goToDetails(context),
backgroundColor: backgroundColor: AppointmentType.getNextActionButtonColor(patientAppointmentHistoryResponseModel.nextAction).withValues(alpha: 0.15),
AppointmentType.getNextActionButtonColor(patientAppointmentHistoryResponseModel.nextAction).withValues(alpha: 0.15), borderColor: AppointmentType.getNextActionButtonColor(patientAppointmentHistoryResponseModel.nextAction).withValues(alpha: 0.01),
borderColor:
AppointmentType.getNextActionButtonColor(patientAppointmentHistoryResponseModel.nextAction).withValues(alpha: 0.01),
textColor: AppointmentType.getNextActionTextColor(patientAppointmentHistoryResponseModel.nextAction), textColor: AppointmentType.getNextActionTextColor(patientAppointmentHistoryResponseModel.nextAction),
fontSize: 14.f, fontSize: 14.f,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
@ -302,6 +326,7 @@ class AppointmentCard extends StatelessWidget {
], ],
); );
} }
}
Widget _getArrivedButton(BuildContext context) { Widget _getArrivedButton(BuildContext context) {
final isRecent = DateTime.now().difference(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate)).inDays <= 15; final isRecent = DateTime.now().difference(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate)).inDays <= 15;

@ -36,6 +36,7 @@ class AppointmentCheckinBottomSheet extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
locationUtils = getIt.get<LocationUtils>();
appState = getIt.get<AppState>(); appState = getIt.get<AppState>();
FlutterNfcKit.nfcAvailability.then((value) { FlutterNfcKit.nfcAvailability.then((value) {
_supportsNFC = (value == NFCAvailability.available); _supportsNFC = (value == NFCAvailability.available);

@ -89,8 +89,8 @@ class AppointmentDoctorCard extends StatelessWidget {
runSpacing: 6.h, runSpacing: 6.h,
children: [ children: [
AppCustomChipWidget( AppCustomChipWidget(
labelText: (patientAppointmentHistoryResponseModel.clinicName!.length > 15 labelText: (patientAppointmentHistoryResponseModel.clinicName!.length > 20
? '${patientAppointmentHistoryResponseModel.clinicName!.substring(0, 12)}...' ? '${patientAppointmentHistoryResponseModel.clinicName!.substring(0, 20)}...'
: patientAppointmentHistoryResponseModel.clinicName!), : patientAppointmentHistoryResponseModel.clinicName!),
labelPadding: EdgeInsetsDirectional.only(start: 4.w, end: 4.w), labelPadding: EdgeInsetsDirectional.only(start: 4.w, end: 4.w),
), ),

@ -458,8 +458,8 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
), ),
SizedBox(width: 16.w), SizedBox(width: 16.w),
CustomButton( CustomButton(
height: 40.h, height: 42.h,
width: 40.w, width: 42.w,
text: "", text: "",
onPressed: () => context.navigateWithName(AppRoutes.userInfoSelection), onPressed: () => context.navigateWithName(AppRoutes.userInfoSelection),
icon: getIt.get<AppState>().isArabic() ? AppAssets.arrow_back : AppAssets.arrow_forward, icon: getIt.get<AppState>().isArabic() ? AppAssets.arrow_back : AppAssets.arrow_forward,

@ -18,6 +18,7 @@ import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/immed
import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/widgets/select_livecare_call_type.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/widgets/select_livecare_call_type.dart';
import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart'; import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.dart'; import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.dart';
import 'package:hmg_patient_app_new/services/livecare_permission_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
@ -208,7 +209,7 @@ class ImmediateLiveCarePaymentDetails extends StatelessWidget {
? CustomButton( ? CustomButton(
text: LocaleKeys.confirmLiveCare.tr(context: context), text: LocaleKeys.confirmLiveCare.tr(context: context),
onPressed: () async { onPressed: () async {
await askVideoCallPermission().then((val) async { await askVideoCallPermission(context).then((val) async {
if (val) { if (val) {
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.confirmingLiveCareRequest.tr(context: context)); LoaderBottomSheet.showLoader(loadingText: LocaleKeys.confirmingLiveCareRequest.tr(context: context));
@ -271,31 +272,32 @@ class ImmediateLiveCarePaymentDetails extends StatelessWidget {
: CustomButton( : CustomButton(
text: LocaleKeys.payNow.tr(context: context), text: LocaleKeys.payNow.tr(context: context),
onPressed: () async { onPressed: () async {
await askVideoCallPermission().then((val) { await askVideoCallPermission(context).then((val) {
if (val) { if (val) {
Navigator.of(context).push( Navigator.of(context).push(
CustomPageRoute( CustomPageRoute(
page: ImmediateLiveCarePaymentPage(), page: ImmediateLiveCarePaymentPage(),
), ),
); );
} else {
showCommonBottomSheetWithoutHeight(
title: LocaleKeys.notice.tr(context: context),
context,
child: Utils.getWarningWidget(
loadingText: LocaleKeys.liveCarePermissionsMessage.tr(context: context),
isShowActionButtons: true,
onCancelTap: () {
Navigator.pop(context);
},
onConfirmTap: () async {
openAppSettings();
}),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
} }
// else {
// showCommonBottomSheetWithoutHeight(
// title: LocaleKeys.notice.tr(context: context),
// context,
// child: Utils.getWarningWidget(
// loadingText: LocaleKeys.liveCarePermissionsMessage.tr(context: context),
// isShowActionButtons: true,
// onCancelTap: () {
// Navigator.pop(context);
// },
// onConfirmTap: () async {
// openAppSettings();
// }),
// callBackFunc: () {},
// isFullScreen: false,
// isCloseButtonVisible: true,
// );
// }
}); });
}, },
backgroundColor: AppColors.infoColor, backgroundColor: AppColors.infoColor,
@ -318,34 +320,110 @@ class ImmediateLiveCarePaymentDetails extends StatelessWidget {
); );
} }
Future<bool> askVideoCallPermission() async { Future<bool> askVideoCallPermission(BuildContext context) async {
bool hasPermission = false; final statuses = await LiveCarePermissionService.instance.requestCameraMicAndNotification(context);
if (Platform.isIOS) {
Map<Permission, PermissionStatus> statuses = await [Permission.camera, Permission.microphone].request();
if (statuses[Permission.camera] == PermissionStatus.granted && statuses[Permission.microphone] == PermissionStatus.granted) { // If service returned nothing (error), treat as not granted
// Camera permission granted if (statuses.isEmpty) return false;
hasPermission = true;
} else { bool cameraGranted = statuses[Permission.camera]?.isGranted ?? false;
hasPermission = false; bool micGranted = statuses[Permission.microphone]?.isGranted ?? false;
bool notifGranted = statuses[Permission.notification]?.isGranted ?? false;
// If all required permissions are already granted
if (cameraGranted && micGranted && notifGranted) return true;
// Collect only the missing permissions
final missing = <Permission>[];
if (!cameraGranted) missing.add(Permission.camera);
if (!micGranted) missing.add(Permission.microphone);
if (!notifGranted) missing.add(Permission.notification);
// If any of the missing permissions are permanently denied/restricted -> open settings (single dialog)
final permanent = missing.where((p) => (statuses[p]?.isPermanentlyDenied ?? false) || (statuses[p]?.isRestricted ?? false)).toList();
if (permanent.isNotEmpty) {
final names = permanent.map((p) => LiveCarePermissionService.instance.friendlyName(p)).join(' and ');
final message = permanent.length == 1 ? '$names permission is permanently denied. Open app settings to allow it.' : '$names permissions are permanently denied. Open app settings to allow them.';
await LiveCarePermissionService.instance.showOpenSettingsDialog(
context,
title: "Permissions Required",
message: message,
);
return false;
} }
} else {
Map<Permission, PermissionStatus> statuses = await [Permission.camera, Permission.microphone, Permission.notification].request();
if (statuses[Permission.camera] == PermissionStatus.granted && statuses[Permission.microphone] == PermissionStatus.granted && statuses[Permission.notification] == PermissionStatus.granted) { // Show one combined retry dialog for all missing permissions
// Camera permission granted final missingNames = missing.map((p) => LiveCarePermissionService.instance.friendlyName(p)).join(' and ');
hasPermission = true; final retry = await showDialog<bool>(
} else { context: context,
hasPermission = false; builder: (c) => AlertDialog(
title: const Text('Permissions required'),
content: Text('$missingNames permission${missing.length > 1 ? 's are' : ' is'} required to start the video call. Retry now?'),
actions: [
TextButton(onPressed: () => Navigator.of(c).pop(false), child: const Text('Cancel')),
TextButton(onPressed: () => Navigator.of(c).pop(true), child: const Text('Retry')),
],
),
);
if (retry != true) return false;
// Request only the missing permissions
final newStatuses = <Permission, PermissionStatus>{};
for (final p in missing) {
newStatuses[p] = await p.request();
} }
// Update grant state (preserve previously granted ones)
cameraGranted = newStatuses[Permission.camera]?.isGranted ?? cameraGranted;
micGranted = newStatuses[Permission.microphone]?.isGranted ?? micGranted;
notifGranted = newStatuses[Permission.notification]?.isGranted ?? notifGranted;
// If any requested permission is now permanently denied -> open settings
final newlyPermanent = missing.where((p) => (newStatuses[p]?.isPermanentlyDenied ?? false) || (newStatuses[p]?.isRestricted ?? false)).toList();
if (newlyPermanent.isNotEmpty) {
final names = newlyPermanent.map((p) => LiveCarePermissionService.instance.friendlyName(p)).join(' and ');
final message =
newlyPermanent.length == 1 ? '$names permission is permanently denied. Open app settings to allow it.' : '$names permissions are permanently denied. Open app settings to allow them.';
await LiveCarePermissionService.instance.showOpenSettingsDialog(
context,
title: "Permissions Required",
message: message,
);
return false;
} }
return hasPermission; return cameraGranted && micGranted && notifGranted;
}
// if (!(await Permission.camera.request().isGranted) || !(await Permission.microphone.request().isGranted)) { // Future<bool> askVideoCallPermission() async {
// return false; // bool hasPermission = false;
// if (Platform.isIOS) {
// Map<Permission, PermissionStatus> statuses = await [Permission.camera, Permission.microphone].request();
//
// if (statuses[Permission.camera] == PermissionStatus.granted && statuses[Permission.microphone] == PermissionStatus.granted) {
// // Camera permission granted
// hasPermission = true;
// } else {
// hasPermission = false;
// }
// } else {
// Map<Permission, PermissionStatus> statuses = await [Permission.camera, Permission.microphone, Permission.notification].request();
//
// if (statuses[Permission.camera] == PermissionStatus.granted && statuses[Permission.microphone] == PermissionStatus.granted && statuses[Permission.notification] == PermissionStatus.granted) {
// // Camera permission granted
// hasPermission = true;
// } else {
// hasPermission = false;
// }
// }
//
// return hasPermission;
//
// // if (!(await Permission.camera.request().isGranted) || !(await Permission.microphone.request().isGranted)) {
// // return false;
// // }
// } // }
}
String getLiveCareType(BuildContext context, int callType) { String getLiveCareType(BuildContext context, int callType) {
switch (callType) { switch (callType) {

@ -432,6 +432,7 @@ class _ImmediateLiveCarePaymentPageState extends State<ImmediateLiveCarePaymentP
), ),
); );
} else { } else {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight( showCommonBottomSheetWithoutHeight(
context, context,
child: Utils.getErrorWidget(loadingText: "Unknown error occurred..."), child: Utils.getErrorWidget(loadingText: "Unknown error occurred..."),
@ -441,6 +442,7 @@ class _ImmediateLiveCarePaymentPageState extends State<ImmediateLiveCarePaymentP
); );
} }
} else { } else {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight( showCommonBottomSheetWithoutHeight(
context, context,
child: Utils.getErrorWidget(loadingText: LocaleKeys.paymentFailedPleaseTryAgain.tr()), child: Utils.getErrorWidget(loadingText: LocaleKeys.paymentFailedPleaseTryAgain.tr()),

@ -62,25 +62,22 @@ class ErOnlineCheckinPaymentDetailsPage extends StatelessWidget {
SizedBox(height: 12.h), SizedBox(height: 12.h),
Wrap( Wrap(
direction: Axis.horizontal, direction: Axis.horizontal,
spacing: 6.w, spacing: 4.w,
runSpacing: 6.h, runSpacing: 6.h,
children: [ children: [
AppCustomChipWidget( AppCustomChipWidget(
labelText: "File No.: ${appState.getAuthenticatedUser()!.patientId!.toString()}", labelText: "${LocaleKeys.fileno.tr(context: context)}.: ${appState.getAuthenticatedUser()!.patientId!.toString()}",
labelPadding: EdgeInsetsDirectional.only(start: 4.w, end: 4.w),
), ),
AppCustomChipWidget( AppCustomChipWidget(
labelText: LocaleKeys.erClinic.tr(), labelText: LocaleKeys.erClinic.tr(),
labelPadding: EdgeInsetsDirectional.only(start: 4.w, end: 4.w),
), ),
AppCustomChipWidget( AppCustomChipWidget(
labelText: emergencyServicesViewModel.selectedHospital!.name, labelText: emergencyServicesViewModel.selectedHospital!.name,
labelPadding: EdgeInsetsDirectional.only(start: 4.w, end: 4.w),
), ),
AppCustomChipWidget( AppCustomChipWidget(
icon: AppAssets.calendar, icon: AppAssets.calendar,
labelText: DateUtil.formatDateToDate(DateTime.now(), false), labelText: DateUtil.formatDateToDate(DateTime.now(), false),
labelPadding: EdgeInsetsDirectional.only(start: 4.w, end: 4.w), labelPadding: EdgeInsetsDirectional.only(start: -4.w, end: 8.w),
), ),
], ],
), ),

@ -337,8 +337,6 @@ class _ErOnlineCheckinPaymentPageState extends State<ErOnlineCheckinPaymentPage>
} }
startApplePay() async { startApplePay() async {
// showCommonBottomSheet(context,
// child: Utils.getLoadingWidget(), callBackFunc: (str) {}, title: "", height: ResponsiveExtension.screenHeight * 0.3, isCloseButtonVisible: false, isDismissible: false, isFullScreen: false);
LoaderBottomSheet.showLoader(); LoaderBottomSheet.showLoader();
transID = Utils.getAdvancePaymentTransID( transID = Utils.getAdvancePaymentTransID(
emergencyServicesViewModel.selectedHospital!.iD, emergencyServicesViewModel.selectedHospital!.iD,
@ -405,8 +403,8 @@ class _ErOnlineCheckinPaymentPageState extends State<ErOnlineCheckinPaymentPage>
applePayShaRequestPhrase: payfortViewModel.payfortProjectDetailsRespModel!.shaRequest, applePayShaRequestPhrase: payfortViewModel.payfortProjectDetailsRespModel!.shaRequest,
currency: appState.getAuthenticatedUser()!.outSa! == 0 ? "SAR" : "AED", currency: appState.getAuthenticatedUser()!.outSa! == 0 ? "SAR" : "AED",
onFailed: (failureResult) async { onFailed: (failureResult) async {
LoaderBottomSheet.hideLoader();
log("failureResult: ${failureResult.message.toString()}"); log("failureResult: ${failureResult.message.toString()}");
Navigator.of(context).pop();
showCommonBottomSheetWithoutHeight( showCommonBottomSheetWithoutHeight(
context, context,
child: Utils.getErrorWidget(loadingText: failureResult.message.toString()), child: Utils.getErrorWidget(loadingText: failureResult.message.toString()),
@ -416,6 +414,7 @@ class _ErOnlineCheckinPaymentPageState extends State<ErOnlineCheckinPaymentPage>
); );
}, },
onSucceeded: (successResult) async { onSucceeded: (successResult) async {
LoaderBottomSheet.hideLoader();
log("successResult: ${successResult.responseMessage.toString()}"); log("successResult: ${successResult.responseMessage.toString()}");
selectedPaymentMethod = successResult.paymentOption ?? "VISA"; selectedPaymentMethod = successResult.paymentOption ?? "VISA";
checkPaymentStatus(); checkPaymentStatus();

@ -316,6 +316,8 @@ class _WalletPaymentConfirmPageState extends State<WalletPaymentConfirmPage> {
context, context,
child: Utils.getSuccessWidget(loadingText: "Payment Successful!"), child: Utils.getSuccessWidget(loadingText: "Payment Successful!"),
callBackFunc: () { callBackFunc: () {
habibWalletVM.initHabibWalletProvider();
habibWalletVM.getPatientBalanceAmount();
Navigator.of(context).pop(); Navigator.of(context).pop();
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },

@ -652,7 +652,7 @@ class _LandingPageState extends State<LandingPage> {
], ],
).paddingSymmetrical(24.w, 0.h), ).paddingSymmetrical(24.w, 0.h),
SizedBox( SizedBox(
height: 411.h, height: 431.h,
child: ListView.separated( child: ListView.separated(
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
itemCount: LandingPageData.getServiceCardsList.length, itemCount: LandingPageData.getServiceCardsList.length,

@ -65,10 +65,10 @@ class SmallServiceCard extends StatelessWidget {
Utils.buildSvgWithAssets(icon: icon, iconColor: iconColor, width: 32.h, height: 32.h), Utils.buildSvgWithAssets(icon: icon, iconColor: iconColor, width: 32.h, height: 32.h),
SizedBox(height: 6.h), SizedBox(height: 6.h),
if (isFoldable || isTablet) ...[ if (isFoldable || isTablet) ...[
title.tr(context: context).toText11(color: textColor, weight: FontWeight.w500, isCenter: true, letterSpacing: -0.2), title.tr(context: context).toText11(color: textColor, weight: FontWeight.w500, isCenter: true, letterSpacing: -0.2, maxLine: 2),
subtitle.tr(context: context).toText11(color: textColor, weight: FontWeight.w500, isCenter: true, letterSpacing: -0.2), subtitle.tr(context: context).toText11(color: textColor, weight: FontWeight.w500, isCenter: true, letterSpacing: -0.2),
] else ...[ ] else ...[
title.tr(context: context).toText12(color: textColor, fontWeight: FontWeight.w500, isCenter: true, letterSpacing: -0.2), title.tr(context: context).toText12(color: textColor, fontWeight: FontWeight.w500, isCenter: true, letterSpacing: -0.2, maxLine: 2),
subtitle.tr(context: context).toText12(color: textColor, fontWeight: FontWeight.w500, isCenter: true, letterSpacing: -0.2), subtitle.tr(context: context).toText12(color: textColor, fontWeight: FontWeight.w500, isCenter: true, letterSpacing: -0.2),
] ]
], ],

@ -0,0 +1,92 @@
import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:permission_handler/permission_handler.dart';
class LiveCarePermissionService {
LiveCarePermissionService._();
static final LiveCarePermissionService instance = LiveCarePermissionService._();
// Guard to prevent concurrent permission requests
Completer<Map<Permission, PermissionStatus>>? _ongoingRequest;
/// Requests camera, microphone and notification permissions together and
/// returns the final map of permission statuses.
/// This method does NOT show any dialogs UI decisions must be done by caller.
Future<Map<Permission, PermissionStatus>> requestCameraMicAndNotification(BuildContext context) async {
if (_ongoingRequest != null) {
return _ongoingRequest!.future;
}
_ongoingRequest = Completer<Map<Permission, PermissionStatus>>();
final completer = _ongoingRequest!;
final permissions = <Permission>[
Permission.camera,
Permission.microphone,
Permission.notification,
];
try {
final statuses = await permissions.request();
// Ensure all requested keys exist in the returned map (some platforms may omit)
final result = <Permission, PermissionStatus>{};
for (final p in permissions) {
result[p] = statuses[p] ?? await p.status;
}
if (!completer.isCompleted) completer.complete(result);
return completer.future;
} catch (e) {
if (!completer.isCompleted) completer.complete(<Permission, PermissionStatus>{});
return completer.future;
} finally {
_ongoingRequest = null;
}
}
/// Show an "open settings" dialog (kept as helper for the UI layer).
Future<void> showOpenSettingsDialog(BuildContext context, {required String title, required String message}) async {
return showCommonBottomSheetWithoutHeight(
title: title,
context,
child: Utils.getWarningWidget(
loadingText: message,
isShowActionButtons: true,
onCancelTap: () {
Navigator.of(context).pop();
},
onConfirmTap: () async {
openAppSettings();
Navigator.of(context).pop();
}),
isFullScreen: false,
isCloseButtonVisible: true,
callBackFunc: () {},
);
// return showDialog<void>(
// context: context,
// builder: (c) => ConfirmDialog(
// title: '$title Permission',
// message: message,
// onTap: () {
// openAppSettings();
// Navigator.of(c).pop();
// },
// okTitle: LocaleKeys.ok.tr(context: c),
// ),
// );
}
String friendlyName(Permission permission) {
if (permission == Permission.camera) return 'Camera';
if (permission == Permission.microphone) return 'Microphone';
if (permission == Permission.notification) return 'Notifications';
return permission.toString();
}
}
Loading…
Cancel
Save