You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
HMG_QLine/lib/view_models/screen_config_view_model.dart

547 lines
19 KiB
Dart

import 'dart:developer';
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:hmg_qline/config/dependency_injection.dart';
import 'package:hmg_qline/constants/app_constants.dart';
import 'package:hmg_qline/main.dart';
import 'package:hmg_qline/models/generic_response_model.dart';
import 'package:hmg_qline/models/global_config_model.dart';
import 'package:hmg_qline/models/kiosk_language_config_model.dart';
import 'package:hmg_qline/models/kiosk_queue_model.dart';
import 'package:hmg_qline/models/kiosk_ticket_model.dart';
import 'package:hmg_qline/models/prayers_widget_model.dart';
import 'package:hmg_qline/models/rss_feed_model.dart';
import 'package:hmg_qline/models/weathers_widget_model.dart';
import 'package:hmg_qline/repositories/screen_details_repo.dart';
import 'package:hmg_qline/repositories/signalR_repo.dart';
import 'package:hmg_qline/services/cache_service.dart';
import 'package:hmg_qline/services/connectivity_service.dart';
import 'package:hmg_qline/services/logger_service.dart';
import 'package:hmg_qline/utilities/enums.dart';
import 'package:hmg_qline/utilities/extensions.dart';
import 'package:hmg_qline/utilities/native_method_handler.dart';
import 'package:hmg_qline/view_models/queuing_view_model.dart';
import 'package:hmg_qline/views/view_helpers/info_components.dart';
import 'package:qr_code_scanner_plus/qr_code_scanner_plus.dart';
// import 'package:timezone/browser.dart' as tz;
class ScreenConfigViewModel extends ChangeNotifier {
final ScreenDetailsRepo screenDetailsRepo;
final CacheService cacheService;
final ConnectivityService connectivityService;
final LoggerService loggerService;
final NativeMethodChannelService nativeMethodChannelService;
ScreenConfigViewModel({
required this.screenDetailsRepo,
required this.cacheService,
required this.connectivityService,
required this.loggerService,
required this.nativeMethodChannelService,
});
Future<void> initializeScreenConfigVM() async {
await getGlobalConfigurationsByIP();
await getLastTimeUpdatedFromCache();
await getLastTimeLogsClearedFromCache();
listenNetworkConnectivity();
getTheWidgetsConfigurationsEveryMidnight();
}
Future<void> onAppResumed() async {
loggerService.logToFile(message: "[didChangeAppLifecycleState] : [onAppResumed]", source: "onAppResumed -> screen_config_view_model.dart", type: LogTypeEnum.data);
}
Future<void> onAppPaused() async {
loggerService.logToFile(message: "[didChangeAppLifecycleState] : [onAppPaused]", source: "onAppPaused -> screen_config_view_model.dart", type: LogTypeEnum.data);
// nativeMethodChannelService.reopenApp();
nativeMethodChannelService.restartApp();
// runApp(const MyApp());
}
Future<void> waitForIPAndInitializeConfigVM() async {
while (currentScreenIP == "") {
await getCurrentScreenIP();
if (currentScreenIP != "") {
initializeScreenConfigVM();
} else {
await Future.delayed(const Duration(seconds: 2));
}
}
}
bool isInternetConnected = true;
updateIsInternetConnected(bool value) {
isInternetConnected = value;
notifyListeners();
}
bool isHubConnected = true;
updateIsHubConnected(bool value) {
isHubConnected = value;
notifyListeners();
}
ViewState state = ViewState.idle;
void updateViewState(ViewState viewState) {
state = viewState;
notifyListeners();
}
updateCurrentScreenRotation(ScreenOrientationEnum value) {
globalConfigurationsModel.orientationTypeEnum = value;
notifyListeners();
}
ScreenTypeEnum currentScreenTypeEnum = ScreenTypeEnum.waitingAreaScreen;
updateCurrentScreenTypeEnum(ScreenTypeEnum value) {
currentScreenTypeEnum = value;
}
QTypeEnum currentQTypeEnum = QTypeEnum.lab;
updateCurrentQTypeEnum(QTypeEnum value) {
currentQTypeEnum = value;
}
String currentScreenIP = "";
Future<void> getCurrentScreenIP() async {
if (useTestIP) {
currentScreenIP = AppConstants.testIP;
} else {
currentScreenIP = await connectivityService.getCurrentScreenIP();
log("currentScreenIP: $currentScreenIP");
}
}
void listenNetworkConnectivity() {
return connectivityService.subscribeToConnectivityChange(
onInternetDisConnected: () {
updateIsInternetConnected(false);
updateIsHubConnected(false);
},
onInternetConnected: () {
updateIsInternetConnected(true);
QueuingViewModel queuingViewModel = getIt.get<QueuingViewModel>();
queuingViewModel.startHubConnection();
},
);
}
GlobalConfigurationsModel globalConfigurationsModel = GlobalConfigurationsModel();
Future<void> getGlobalConfigurationsByIP() async {
GlobalConfigurationsModel? response = await screenDetailsRepo.getGlobalScreenConfigurations(ipAddress: currentScreenIP);
if (response == null) {
loggerService.logError("response was: $response");
updateViewState(ViewState.error);
return;
}
updateGlobalConfigurationsModel(value: response);
updateCurrentScreenTypeEnum(globalConfigurationsModel.screenTypeEnum);
updateCurrentQTypeEnum(globalConfigurationsModel.qTypeEnum);
notifyListeners();
}
void updateGlobalConfigurationsModel({required var value, bool needNotify = false, bool shouldUpdateNextPrayer = false}) {
globalConfigurationsModel = value;
if (needNotify) {
notifyListeners();
}
if (shouldUpdateNextPrayer) {
getNextPrayerToShow();
}
getInfoWidgetsDetailsFromServer();
}
Future<void> getInfoWidgetsDetailsFromServer() async {
if (globalConfigurationsModel.isWeatherReq) {
await getWeatherDetailsFromServer();
}
if (globalConfigurationsModel.isPrayerTimeReq) {
await getPrayerDetailsFromServer();
}
if (globalConfigurationsModel.isRssFeedReq) {
await getRssFeedDetailsFromServer();
}
int currentDate = DateTime.now().millisecondsSinceEpoch;
await cacheService.setLastTimeUpdatedInCache(lasTimeUpdated: currentDate.toString());
}
RssFeedModel rssFeedModel = RssFeedModel();
Future<void> getRssFeedDetailsFromServer() async {
RssFeedModel? response = await screenDetailsRepo.getRssFeedDetailsByLanguageID(languageId: 0);
if (response == null) {
return;
}
rssFeedModel = response;
notifyListeners();
}
PrayersWidgetModel prayersWidgetModel = PrayersWidgetModel();
Future<void> getPrayerDetailsFromServer() async {
double testLatitude = 24.722136;
double testLongitude = 46.774303;
PrayersWidgetModel? response = await screenDetailsRepo.getPrayerDetailsByLatLong(
latitude: globalConfigurationsModel.projectLatitude == 0.0 ? testLatitude : globalConfigurationsModel.projectLatitude ?? testLatitude,
longitude: globalConfigurationsModel.projectLongitude == 0.0 ? testLongitude : globalConfigurationsModel.projectLongitude ?? testLongitude,
);
if (response == null) {
return;
}
prayersWidgetModel = response;
getNextPrayerToShow();
notifyListeners();
}
WeathersWidgetModel weathersWidgetModel = WeathersWidgetModel();
Future<void> getWeatherDetailsFromServer() async {
int testCityKey = 297030;
WeathersWidgetModel? response = await screenDetailsRepo.getWeatherDetailsByCity(
cityId: ((globalConfigurationsModel.cityKey == null || globalConfigurationsModel.cityKey == 0) ? testCityKey : globalConfigurationsModel.cityKey).toString(),
);
if (response == null) {
return;
}
weathersWidgetModel = response;
notifyListeners();
}
String nextPrayerToShowWithTime = '';
void getNextPrayerToShow() async {
final current = DateTime.now();
log("Checking Namaz time Locally at $current and ${current.timeZoneName}");
// log("Data Before Check : ${prayersWidgetModel.fajr}");
// log("Data Before Check : ${globalConfigurationsModel.isPrayerTimeReq}");
if (globalConfigurationsModel.isPrayerTimeReq && prayersWidgetModel.fajr == null) {
await getPrayerDetailsFromServer();
}
// log("prayersWidgetModel.dhuhr: ${prayersWidgetModel.dhuhr!}");
// log("dhuhr: ${prayersWidgetModel.dhuhr!.toDateTimeFromInt()}");
// log("current: ${DateFormat('yyyy-MM-dd hh:mm a').format(DateTime.now().toLocal())}");
// log("current: ${DateTime.now().timeZoneName}");
if (prayersWidgetModel.fajr != null && prayersWidgetModel.fajr!.toDateTimeFromInt().isAfter(current)) {
final namazTime = prayersWidgetModel.fajr!.toFormattedDateTimeFromInt();
nextPrayerToShowWithTime = "${globalConfigurationsModel.fajarText} at $namazTime";
notifyListeners();
return;
}
if (prayersWidgetModel.dhuhr != null && prayersWidgetModel.dhuhr!.toDateTimeFromInt().isAfter(current)) {
final namazTime = prayersWidgetModel.dhuhr!.toFormattedDateTimeFromInt();
nextPrayerToShowWithTime = "${globalConfigurationsModel.dhuhrText} at $namazTime";
notifyListeners();
return;
}
if (prayersWidgetModel.asr != null && prayersWidgetModel.asr!.toDateTimeFromInt().isAfter(current)) {
final namazTime = prayersWidgetModel.asr!.toFormattedDateTimeFromInt();
nextPrayerToShowWithTime = "${globalConfigurationsModel.asarText} at $namazTime";
notifyListeners();
return;
}
if (prayersWidgetModel.maghrib != null && prayersWidgetModel.maghrib!.toDateTimeFromInt().isAfter(current)) {
final namazTime = prayersWidgetModel.maghrib!.toFormattedDateTimeFromInt();
nextPrayerToShowWithTime = "${globalConfigurationsModel.maghribText} at $namazTime";
notifyListeners();
return;
}
if (prayersWidgetModel.isha != null && prayersWidgetModel.isha!.toDateTimeFromInt().isAfter(current)) {
final namazTime = prayersWidgetModel.isha!.toFormattedDateTimeFromInt();
nextPrayerToShowWithTime = "${globalConfigurationsModel.ishaText} at $namazTime";
notifyListeners();
return;
}
}
int counter = 0;
DateTime lastChecked = DateTime.now();
Timer? _midnightTimer;
Future<void> getTheWidgetsConfigurationsEveryMidnight() async {
// Cancel any existing timer to avoid multiple timers running
_midnightTimer?.cancel();
if (!(globalConfigurationsModel.isWeatherReq) && !(globalConfigurationsModel.isPrayerTimeReq) && !(globalConfigurationsModel.isRssFeedReq)) {
return;
}
// Only start timer if not already running
if (_midnightTimer != null) {
return;
}
_midnightTimer = Timer.periodic(const Duration(minutes: 1), (timer) async {
counter++;
DateTime now = DateTime.now();
log("counterValue: $counter");
// Every hour, update RSS feed if required
if (counter % 60 == 0 && globalConfigurationsModel.isRssFeedReq) {
await getRssFeedDetailsFromServer();
}
log("lastChecked: [${lastChecked.day}]");
log("now: [${now.day}]");
// At midnight, update weather and prayer details if required
if (now.day != lastChecked.day) {
if (globalConfigurationsModel.isWeatherReq) {
await getWeatherDetailsFromServer();
}
if (globalConfigurationsModel.isPrayerTimeReq) {
await getPrayerDetailsFromServer();
}
lastChecked = now;
}
getNextPrayerToShow();
syncHubConnectionState();
});
}
@override
void dispose() {
_midnightTimer?.cancel();
patientIdController.dispose();
super.dispose();
}
DateTime currentLastTimeUpdated = DateTime.now();
Future<void> getLastTimeUpdatedFromCache() async {
DateTime? response = await cacheService.getLastTimeUpdatedFromCache();
if (response != null) {
currentLastTimeUpdated = response;
}
}
DateTime? lastTimeLogsCleared = DateTime.now();
Future<void> getLastTimeLogsClearedFromCache() async {
lastTimeLogsCleared = await cacheService.getLastTimeLogsCleared();
if (lastTimeLogsCleared == null) {
await cacheService.setLastTimeLogsCleared(lastTimeCleared: DateTime.now().millisecondsSinceEpoch).whenComplete(() => lastTimeLogsCleared = DateTime.now());
}
}
// *************************** KIOSK FUNCTIONS *************************
KioskScreenStateEnums kioskScreenStateEnum = KioskScreenStateEnums.languageState;
void updateKioskScreenState(KioskScreenStateEnums state) {
kioskScreenStateEnum = state;
notifyListeners();
}
KioskPatientTicket? kioskPatientTicket = KioskPatientTicket();
void updateTicketGeneratedFromKiosk(KioskPatientTicket? value) {
kioskPatientTicket = value;
notifyListeners();
}
KioskLanguageConfigModel? currentSelectedKioskLanguage;
void updateCurrentSelectedKioskLanguageModel(KioskLanguageConfigModel value) {
currentSelectedKioskLanguage = value;
notifyListeners();
}
late KioskQueueModel currentSelectedKioskQueueModel;
void updateCurrentSelectedKioskQueueModel(KioskQueueModel value) {
currentSelectedKioskQueueModel = value;
notifyListeners();
}
Future<bool> generateTicketForQueue({required KioskQueueModel kioskQueueModel, int patientId = 0}) async {
updateKioskScreenState(KioskScreenStateEnums.busyState);
KioskPatientTicket? kioskPatientTicket = await createTicketFromKiosk(
projectId: kioskQueueModel.projectID ?? 0,
queueId: kioskQueueModel.queueID ?? 0,
patientId: patientId,
);
if (kioskPatientTicket == null) {
updateKioskScreenState(KioskScreenStateEnums.languageState);
return false;
}
updateTicketGeneratedFromKiosk(kioskPatientTicket);
updateKioskScreenState(KioskScreenStateEnums.ticketNumState);
return true;
}
Future<void> createTestTickets({required int numOfTicketsToCreate}) async {
int startTicket = 123457100;
for (int i = 0; i < numOfTicketsToCreate; i++) {
GenericRespModel? response = await screenDetailsRepo.createTestTickets(ticketNumber: startTicket);
startTicket = startTicket + 1;
if (response == null || response.messageStatus != 1) {
log("response null from createNextTickets");
return;
}
}
log("last ticket is: $startTicket ");
}
Future<KioskPatientTicket?> createTicketFromKiosk({required int projectId, required int queueId, int patientId = 0}) async {
try {
GenericRespModel? response = await screenDetailsRepo.createTicketFromKiosk(
projectId: projectId,
queueId: queueId,
patientId: patientId,
);
if (response == null || response.messageStatus != 1) {
loggerService.logError("response null from createTicketFromKiosk");
return null;
}
return response.data;
} catch (e) {
InfoComponents.showToast(e.toString());
loggerService.logError(e.toString());
return null;
}
}
Future<void> acknowledgeTicket({required int ticketQueueID}) async {
GenericRespModel? response = await screenDetailsRepo.acknowledgeTicket(
ipAddress: currentScreenIP,
ticketQueueID: ticketQueueID,
qTypeEnum: globalConfigurationsModel.qTypeEnum,
);
if (response == null || response.messageStatus != 1) {
loggerService.logError("response null from acknowledgeTicket");
return;
} else {
loggerService.logInfo("response from acknowledgeTicket ${response.data}");
}
}
Future<void> acknowledgeTicketForAppointmentOnly({required int ticketQueueID, required String ipAddress, required CallTypeEnum callTypeEnum}) async {
GenericRespModel? response = await screenDetailsRepo.acknowledgeTicketForAppointment(
ticketId: ticketQueueID,
ipAddress: ipAddress,
callTypeEnum: callTypeEnum,
);
if (response == null || response.messageStatus != 1) {
loggerService.logError("response null from acknowledgeTicketForAppointmentOnly");
return;
} else {
loggerService.logInfo("response from acknowledgeTicketForAppointmentOnly ${response.data}");
}
}
// ***************************** TEXT INPUT PATIENT ID FUNCTIONS *********************
final TextEditingController patientIdController = TextEditingController();
Future<void> onPatientIdSubmitted(String text) async {
int? patientId = int.tryParse(text);
if (patientId != null && patientId > 0) {
isGeneratingTicket = true;
await generateTicketForQueue(kioskQueueModel: currentSelectedKioskQueueModel, patientId: patientId);
patientIdController.clear();
await Future.delayed(const Duration(seconds: 2)).whenComplete(() => isGeneratingTicket = false);
}
}
// *************************** QR SCANNER FUNCTIONS *************************
Barcode? qrCodeResult;
QRViewController? qrViewController;
bool isGeneratingTicket = false;
Future<void> flipCamera() async {
await qrViewController!.flipCamera();
}
void onQRViewCreated({
required QRViewController controller,
required VoidCallback onSuccess,
required VoidCallback onFailure,
}) async {
qrViewController = controller;
controller.scannedDataStream.listen((scanData) async {
loggerService.logInfo("Found: ${scanData.code} and isGeneratingTicket: $isGeneratingTicket");
if (isGeneratingTicket) return;
await validateAndGenerateTicketFromQR(data: scanData, onFailure: onFailure, onSuccess: onSuccess);
});
await flipCamera();
}
Future<void> validateAndGenerateTicketFromQR({required var data, required VoidCallback onSuccess, required VoidCallback onFailure}) async {
if (data == null) return;
qrCodeResult = data;
notifyListeners();
final code = qrCodeResult!.code;
int? patientId = int.tryParse(code ?? '');
if (patientId != null && patientId > 0) {
isGeneratingTicket = true;
bool status = await generateTicketForQueue(kioskQueueModel: currentSelectedKioskQueueModel, patientId: patientId);
qrCodeResult = null;
notifyListeners();
if (status) {
log("status: $status");
onSuccess();
}
await Future.delayed(const Duration(seconds: 2)).whenComplete(() => isGeneratingTicket = false);
}
}
Future<void> reassemble() async {
if (qrViewController != null) {
if (defaultTargetPlatform == TargetPlatform.android) {
await qrViewController!.pauseCamera();
} else if (defaultTargetPlatform == TargetPlatform.iOS) {
await qrViewController!.resumeCamera();
}
await qrViewController!.flipCamera();
}
}
void syncHubConnectionState() async {
QueuingViewModel queuingViewModel = getIt.get<QueuingViewModel>();
bool newState = (queuingViewModel.signalrRepo as SignalrRepoImp).isConnected;
if (isHubConnected != newState) {
updateIsHubConnected(newState);
}
if (!isHubConnected) {
log("Hub is Not Connected!, I will try to reconnect now.");
QueuingViewModel queuingViewModel = getIt.get<QueuingViewModel>();
bool? status = await queuingViewModel.startHubConnection();
// syncHubConnectionState will update isHubConnected
} else {
log("Hub is Connected!");
}
}
}