|
|
|
|
@ -1,7 +1,10 @@
|
|
|
|
|
import 'dart:convert';
|
|
|
|
|
import 'dart:developer';
|
|
|
|
|
import 'dart:typed_data';
|
|
|
|
|
|
|
|
|
|
import 'package:easy_localization/easy_localization.dart';
|
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
import 'package:get_it/get_it.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';
|
|
|
|
|
@ -14,6 +17,7 @@ import 'package:hmg_patient_app_new/features/authentication/authentication_view_
|
|
|
|
|
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
|
|
|
|
|
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart';
|
|
|
|
|
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
|
|
|
|
|
import 'package:hmg_patient_app_new/features/profile_settings/profile_settings_view_model.dart';
|
|
|
|
|
import 'package:hmg_patient_app_new/features/symptoms_checker/symptoms_checker_view_model.dart';
|
|
|
|
|
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
|
|
|
|
|
import 'package:hmg_patient_app_new/presentation/book_appointment/waiting_appointment/waiting_appointment_payment_page.dart';
|
|
|
|
|
@ -41,6 +45,9 @@ class _ReviewAppointmentPageState extends State<ReviewAppointmentPage> {
|
|
|
|
|
late MyAppointmentsViewModel myAppointmentsViewModel;
|
|
|
|
|
late SymptomsCheckerViewModel symptomsCheckerViewModel;
|
|
|
|
|
|
|
|
|
|
Uint8List? _cachedImageBytes;
|
|
|
|
|
String? _cachedImageDataHash;
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
|
bookAppointmentsViewModel = Provider.of<BookAppointmentsViewModel>(context, listen: false);
|
|
|
|
|
@ -156,11 +163,14 @@ class _ReviewAppointmentPageState extends State<ReviewAppointmentPage> {
|
|
|
|
|
padding: EdgeInsets.all(16.h),
|
|
|
|
|
child: Row(
|
|
|
|
|
children: [
|
|
|
|
|
Image.asset(
|
|
|
|
|
appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.maleImg : AppAssets.femaleImg,
|
|
|
|
|
width: 52.h,
|
|
|
|
|
height: 52.h,
|
|
|
|
|
),
|
|
|
|
|
// Image.asset(
|
|
|
|
|
// appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.maleImg : AppAssets.femaleImg,
|
|
|
|
|
// width: 52.h,
|
|
|
|
|
// height: 52.h,
|
|
|
|
|
// ),
|
|
|
|
|
Consumer<ProfileSettingsViewModel>(builder: (context, profileVm, _) {
|
|
|
|
|
return _buildProfileImage(profileVm);
|
|
|
|
|
}),
|
|
|
|
|
SizedBox(width: 8.h),
|
|
|
|
|
Column(
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
|
@ -260,6 +270,79 @@ class _ReviewAppointmentPageState extends State<ReviewAppointmentPage> {
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Widget _buildProfileImage(ProfileSettingsViewModel profileVm) {
|
|
|
|
|
// Always get fresh user data
|
|
|
|
|
final currentUser = appState.getAuthenticatedUser();
|
|
|
|
|
final currentPatientId = currentUser?.patientId;
|
|
|
|
|
final gender = currentUser?.gender ?? 1;
|
|
|
|
|
final age = currentUser?.age ?? 0;
|
|
|
|
|
|
|
|
|
|
// Determine the default image based on gender and age
|
|
|
|
|
final String defaultImage;
|
|
|
|
|
if (gender == 1) {
|
|
|
|
|
// Male
|
|
|
|
|
defaultImage = age < 7 ? AppAssets.babyBoyImg : AppAssets.maleImg;
|
|
|
|
|
} else {
|
|
|
|
|
// Female
|
|
|
|
|
defaultImage = age < 7 ? AppAssets.babyGirlImg : AppAssets.femaleImg;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Show selected image if available (only during upload)
|
|
|
|
|
// if (_selectedImage != null) {
|
|
|
|
|
// return ClipOval(
|
|
|
|
|
// child: Image.file(
|
|
|
|
|
// _selectedImage!,
|
|
|
|
|
// width: 136.w,
|
|
|
|
|
// height: 136.h,
|
|
|
|
|
// fit: BoxFit.cover,
|
|
|
|
|
// ),
|
|
|
|
|
// );
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// Use cached decoded bytes — update cache if source data changed
|
|
|
|
|
final String? imageData = GetIt.instance<AppState>().getProfileImageData;
|
|
|
|
|
final String? currentHash = (imageData != null && imageData.isNotEmpty) ? '${imageData.length}_${imageData.hashCode}' : null;
|
|
|
|
|
|
|
|
|
|
// Re-decode only if the underlying data actually changed
|
|
|
|
|
if (currentHash != null && currentHash != _cachedImageDataHash) {
|
|
|
|
|
try {
|
|
|
|
|
_cachedImageBytes = base64Decode(imageData!);
|
|
|
|
|
_cachedImageDataHash = currentHash;
|
|
|
|
|
} catch (e) {
|
|
|
|
|
print('❌ Error decoding profile image: $e');
|
|
|
|
|
_cachedImageBytes = null;
|
|
|
|
|
_cachedImageDataHash = null;
|
|
|
|
|
}
|
|
|
|
|
} else if (currentHash == null) {
|
|
|
|
|
_cachedImageBytes = null;
|
|
|
|
|
_cachedImageDataHash = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Show cached decoded image if available
|
|
|
|
|
if (_cachedImageBytes != null) {
|
|
|
|
|
return ClipOval(
|
|
|
|
|
child: Image.memory(
|
|
|
|
|
_cachedImageBytes!,
|
|
|
|
|
key: ValueKey('profile_$currentPatientId'),
|
|
|
|
|
width: 52.w,
|
|
|
|
|
height: 52.h,
|
|
|
|
|
fit: BoxFit.cover,
|
|
|
|
|
gaplessPlayback: true, // Prevents blink during image rebuild
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Show default image (no image data or user has no uploaded image)
|
|
|
|
|
print('📷 Showing default avatar for user $currentPatientId');
|
|
|
|
|
return Image.asset(
|
|
|
|
|
defaultImage,
|
|
|
|
|
key: ValueKey('default_$currentPatientId'),
|
|
|
|
|
width: 52.w,
|
|
|
|
|
height: 52.h,
|
|
|
|
|
gaplessPlayback: true,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void getWalkInAppointmentPatientShare() async {
|
|
|
|
|
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.fetchingAppointmentShare.tr(context: context));
|
|
|
|
|
await bookAppointmentsViewModel.getWalkInPatientShareAppointment(onSuccess: (val) {
|
|
|
|
|
|