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

909 lines
31 KiB
Dart

import 'dart:async';
import 'dart:developer';
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/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:mobile_scanner/mobile_scanner.dart';
import 'package:wakelock_plus/wakelock_plus.dart';
import 'package:universal_html/html.dart' as html;
// 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 {
appStartTime = DateTime.now(); // Record when app actually started
await getGlobalConfigurationsByIP();
await getLastTimeUpdatedFromCache();
await getLastTimeLogsClearedFromCache();
listenNetworkConnectivity();
initializeTimer();
// Platform-specific lifecycle
if (kIsWeb) {
_initializeWebLifecycle();
}
}
StreamSubscription? _visibilitySubscription;
void _initializeWebLifecycle() {
// Listen to page visibility changes
_visibilitySubscription = html.document.onVisibilityChange.listen((event) {
if (html.document.hidden!) {
onAppPaused();
} else {
onAppResumed();
}
});
// Prevent accidental tab close
html.window.onBeforeUnload.listen((event) {
event.preventDefault();
});
}
Future<void> onAppResumed() async {
final uptimeHours = DateTime.now().difference(appStartTime).inHours;
loggerService.logToFile(
message:
"[didChangeAppLifecycleState] : [onAppResumed] - App uptime: ${uptimeHours}h",
source: "onAppResumed -> screen_config_view_model.dart",
type: LogTypeEnum.data,
);
// Re-enable wakelock when resuming
try {
await WakelockPlus.enable();
} catch (e) {
loggerService.logError("Failed to enable wakelock on resume: $e");
}
// On web, wait a bit for network to stabilize after tab becomes visible
if (kIsWeb) {
await Future.delayed(const Duration(milliseconds: 500));
}
// Verify connections are still alive and reconnect if needed
syncHubConnectionState();
// On web, proactively try to reconnect if disconnected
if (kIsWeb && !isHubConnected) {
loggerService.logToFile(
message: "Web page resumed - attempting SignalR reconnection",
source: "onAppResumed -> screen_config_view_model.dart",
type: LogTypeEnum.connectivity,
);
QueuingViewModel queuingViewModel = getIt.get<QueuingViewModel>();
try {
// Try to stop existing connection (may be null/disconnected already)
try {
await queuingViewModel.stopHubConnection();
} catch (e) {
log("Stop connection error (expected if already disconnected): $e");
}
await Future.delayed(const Duration(seconds: 1));
// Start fresh connection
final result = await queuingViewModel.startHubConnection();
if (result == true) {
loggerService.logToFile(
message: "SignalR reconnected successfully on resume",
source: "onAppResumed -> screen_config_view_model.dart",
type: LogTypeEnum.connectivity,
);
}
} catch (e) {
loggerService.logError("Failed to reconnect on resume: $e");
}
}
}
Future<void> onAppPaused() async {
final uptimeHours = DateTime.now().difference(appStartTime).inHours;
loggerService.logToFile(
message:
"[didChangeAppLifecycleState] : [onAppPaused] - App uptime: ${uptimeHours}h - WARNING: App going to background!",
source: "onAppPaused -> screen_config_view_model.dart",
type: LogTypeEnum.data,
);
}
Future<void> onAppDetached() async {
final uptimeHours = DateTime.now().difference(appStartTime).inHours;
loggerService.logToFile(
message:
"[didChangeAppLifecycleState] : [onAppDetached] - App uptime: ${uptimeHours}h - CRITICAL: App being killed!",
source: "onAppDetached -> screen_config_view_model.dart",
type: LogTypeEnum.data,
);
}
Future<void> waitForIPAndInitializeConfigVM() async {
int attempts = 0;
const maxAttempts = kIsWeb ? 3 : 30;
// On web, check localStorage immediately on startup (before any detection)
if (kIsWeb && currentScreenIP == "") {
try {
final storedIp = html.window.localStorage['device_ip'];
if (storedIp != null && storedIp.isNotEmpty && storedIp != 'null') {
currentScreenIP = storedIp;
log('✅ IP retrieved from localStorage on startup: $currentScreenIP');
initializeScreenConfigVM();
return;
} else {
log('⚠️ No IP found in localStorage on startup (storedIp: "$storedIp")');
}
} catch (e) {
log('❌ Error accessing localStorage on startup: $e');
loggerService.logError('localStorage access error: $e');
}
}
while (currentScreenIP == "") {
await getCurrentScreenIP();
if (currentScreenIP != "") {
initializeScreenConfigVM();
return;
} else {
attempts++;
if (attempts >= maxAttempts) {
if (kIsWeb) {
// On web, show configuration dialog
loggerService.logError(
"No IP auto-detected or configured after $attempts attempts.");
throw Exception("IP_CONFIGURATION_REQUIRED");
} else {
// On Android native, keep trying
loggerService.logError("Failed to get IP after $attempts attempts");
}
}
// Wait 4 seconds on first attempt to allow auto-detection
final waitTime = attempts == 1 ? 4 : 2;
await Future.delayed(Duration(seconds: waitTime));
}
}
}
bool isInternetConnected = true;
updateIsInternetConnected(bool value) {
isInternetConnected = value;
notifyListeners();
}
bool isHubConnected = false; // Start as disconnected until connection succeeds
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 {
log("Using TEST IP");
if (useTestIP) {
currentScreenIP = AppConstants.testIP;
log("🧪 Using TEST IP: $currentScreenIP");
} else {
currentScreenIP = await connectivityService.getCurrentScreenIP();
if (currentScreenIP.isEmpty) {
log("⚠️ WARNING: No IP address detected!");
if (kIsWeb) {
log("📌 For web, add IP to URL: ?ip=YOUR_IP_ADDRESS");
log(" Example: http://localhost:port/?ip=12.4.5.1");
}
} else {
log("✅ Current Screen IP: $currentScreenIP");
// On web, ensure IP is saved to localStorage
if (kIsWeb) {
try {
html.window.localStorage['device_ip'] = currentScreenIP;
log("💾 Saved IP to localStorage: $currentScreenIP");
} catch (e) {
log("❌ Failed to save IP to localStorage: $e");
loggerService.logError('localStorage save error: $e');
}
}
}
}
}
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);
loggerService.logError("response was: $response");
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}) {
// Ensure the incoming value is the GlobalConfigurationsModel and set the Takhasusi flag based on projectID
if (value is GlobalConfigurationsModel) {
// Use projectID-based detection (projectID = 16 for Takhasusi Main)
try {
value.isFromTakhasusiMain =
value.projectID == AppConstants.projectIdTakhasusiMain;
// Debug logging for Takhasusi Main detection
log("=== TAKHASUSI MAIN CHECK (ProjectID-based) ===");
log("Current Screen IP: $currentScreenIP");
log("Project ID: ${value.projectID}");
log("Is Takhasusi Main (projectID == 16): ${value.isFromTakhasusiMain}");
if (value.isFromTakhasusiMain) {
log("✅ This device will use getTurnsByOrientationForOlderVersions()");
} else {
log("✅ This device will use getTurnsByOrientationForNewVersions()");
}
log("==========================================");
} catch (_) {
value.isFromTakhasusiMain = false;
}
globalConfigurationsModel = value;
} else {
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 nextPrayerTime = '';
String nextPrayerToShowEng = '';
String nextPrayerToShowArb = '';
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();
nextPrayerTime = namazTime;
nextPrayerToShowEng = "${globalConfigurationsModel.fajarTextEng}";
nextPrayerToShowArb = "${globalConfigurationsModel.fajarTextArb}";
notifyListeners();
return;
}
if (prayersWidgetModel.dhuhr != null &&
prayersWidgetModel.dhuhr!.toDateTimeFromInt().isAfter(current)) {
final namazTime = prayersWidgetModel.dhuhr!.toFormattedDateTimeFromInt();
nextPrayerTime = namazTime;
nextPrayerToShowEng = "${globalConfigurationsModel.dhuhrTextEng}";
nextPrayerToShowArb = "${globalConfigurationsModel.dhuhrTextArb}";
notifyListeners();
return;
}
if (prayersWidgetModel.asr != null &&
prayersWidgetModel.asr!.toDateTimeFromInt().isAfter(current)) {
final namazTime = prayersWidgetModel.asr!.toFormattedDateTimeFromInt();
nextPrayerTime = namazTime;
nextPrayerToShowEng = "${globalConfigurationsModel.asarTextEng}";
nextPrayerToShowArb = "${globalConfigurationsModel.asarTextArb}";
notifyListeners();
return;
}
if (prayersWidgetModel.maghrib != null &&
prayersWidgetModel.maghrib!.toDateTimeFromInt().isAfter(current)) {
final namazTime =
prayersWidgetModel.maghrib!.toFormattedDateTimeFromInt();
nextPrayerTime = namazTime;
nextPrayerToShowEng = "${globalConfigurationsModel.maghribTextEng}";
nextPrayerToShowArb = "${globalConfigurationsModel.maghribTextArb}";
notifyListeners();
return;
}
if (prayersWidgetModel.isha != null &&
prayersWidgetModel.isha!.toDateTimeFromInt().isAfter(current)) {
final namazTime = prayersWidgetModel.isha!.toFormattedDateTimeFromInt();
nextPrayerTime = namazTime;
nextPrayerToShowEng = "${globalConfigurationsModel.ishaTextEng}";
nextPrayerToShowArb = "${globalConfigurationsModel.ishaTextArb}";
notifyListeners();
return;
}
final namazTime = prayersWidgetModel.fajr!.toFormattedDateTimeFromInt();
nextPrayerTime = namazTime;
nextPrayerToShowEng = "${globalConfigurationsModel.fajarTextEng}";
nextPrayerToShowArb = "${globalConfigurationsModel.fajarTextArb}";
notifyListeners();
return;
}
int counter = 0;
DateTime lastChecked = DateTime.now();
DateTime appStartTime = DateTime.now(); // Track actual app start time
Timer? _midnightTimer;
Timer? _healthCheckTimer;
int healthCheckFailures = 0; // Track consecutive failures
Future<void> initializeTimer() async {
// Cancel any existing timer to avoid multiple timers running
_midnightTimer?.cancel();
_healthCheckTimer?.cancel();
if (!(globalConfigurationsModel.isWeatherReq) &&
!(globalConfigurationsModel.isPrayerTimeReq) &&
!(globalConfigurationsModel.isRssFeedReq)) {
// Even if widgets are not required, start a minimal health check
_startHealthCheckTimer();
return;
}
// Start the main periodic timer
_midnightTimer = Timer.periodic(
const Duration(minutes: 10),
(timer) async {
counter++;
DateTime now = DateTime.now();
log("counterValue: $counter");
if (globalConfigurationsModel.id == null || state == ViewState.error) {
await getGlobalConfigurationsByIP();
}
// Every Two hour, update RSS feed if required
if (counter % 12 == 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 (now
.difference(now.copyWith(
hour: 0,
minute: 0,
second: 0,
millisecond: 0,
microsecond: 0))
.inMinutes >=
5) {
await nativeMethodChannelService.smartRestart(
forceRestart: true, cleanupFirst: true);
lastChecked = now;
}
}
getNextPrayerToShow();
syncHubConnectionState();
},
);
// Start health check timer
_startHealthCheckTimer();
}
void _startHealthCheckTimer() {
// Health check every 5 minutes to keep app active and check connections
_healthCheckTimer = Timer.periodic(
const Duration(minutes: 5),
(timer) async {
try {
final now = DateTime.now();
final actualUptime = now.difference(appStartTime);
final uptimeHours = actualUptime.inHours;
final uptimeMinutes = actualUptime.inMinutes % 60;
log("Health check - App uptime: ${uptimeHours}h ${uptimeMinutes}m");
loggerService.logToFile(
message:
"Health check performed - App uptime: ${uptimeHours}h ${uptimeMinutes}m (started: ${appStartTime.toString().substring(0, 19)})",
source: "_startHealthCheckTimer -> screen_config_view_model.dart",
type: LogTypeEnum.data,
);
// Check if app has been running for dangerously long (>60 hours)
if (uptimeHours > 60) {
loggerService.logToFile(
message:
"WARNING: App running for ${uptimeHours} hours - scheduling proactive restart",
source: "_startHealthCheckTimer -> screen_config_view_model.dart",
type: LogTypeEnum.data,
);
}
// Sync hub connection state
try {
syncHubConnectionState();
// On web, if disconnected for 2 consecutive checks, force reconnection
if (kIsWeb && !isHubConnected && healthCheckFailures >= 2) {
loggerService.logToFile(
message:
"Web disconnected for ${healthCheckFailures} checks - forcing reconnection",
source:
"_startHealthCheckTimer -> screen_config_view_model.dart",
type: LogTypeEnum.connectivity,
);
QueuingViewModel queuingViewModel = getIt.get<QueuingViewModel>();
try {
await queuingViewModel.stopHubConnection();
} catch (e) {
log("Stop connection error during health check (may be expected): $e");
}
await Future.delayed(const Duration(seconds: 2));
await queuingViewModel.startHubConnection();
}
healthCheckFailures = 0; // Reset on success
} catch (e) {
healthCheckFailures++;
loggerService.logError(
"Health check - Hub sync failed (${healthCheckFailures} consecutive): $e");
// If multiple consecutive failures, try to recover
if (healthCheckFailures >= 3) {
loggerService.logToFile(
message:
"CRITICAL: ${healthCheckFailures} consecutive health check failures - attempting recovery",
source:
"_startHealthCheckTimer -> screen_config_view_model.dart",
type: LogTypeEnum.data,
);
QueuingViewModel queuingViewModel = getIt.get<QueuingViewModel>();
try {
await queuingViewModel.stopHubConnection();
} catch (e) {
log("Stop connection error during recovery (may be expected): $e");
}
await Future.delayed(const Duration(seconds: 2));
await queuingViewModel.startHubConnection();
}
}
// Ensure wakelock is still enabled
try {
await WakelockPlus.enable();
} catch (e) {
loggerService.logError("Failed to enable wakelock: $e");
}
// Log memory/performance indicators
loggerService.logToFile(
message:
"Health check - Hub connected: $isHubConnected, Internet: $isInternetConnected",
source: "_startHealthCheckTimer -> screen_config_view_model.dart",
type: LogTypeEnum.connectivity,
);
// Trigger a small notifyListeners to keep UI thread active
notifyListeners();
} catch (e, stackTrace) {
healthCheckFailures++;
loggerService.logError(
"Health check timer error (${healthCheckFailures} consecutive): $e\n$stackTrace");
// If health check itself is failing repeatedly, something is seriously wrong
if (healthCheckFailures >= 5) {
loggerService.logToFile(
message:
"FATAL: Health check failing repeatedly - app may be in bad state",
source: "_startHealthCheckTimer -> screen_config_view_model.dart",
type: LogTypeEnum.data,
);
}
}
},
);
}
@override
void dispose() {
_visibilitySubscription?.cancel();
_midnightTimer?.cancel();
_healthCheckTimer?.cancel();
patientIdController.dispose();
mobileScannerController?.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, required String ipAddress}) async {
GenericRespModel? response = await screenDetailsRepo.acknowledgeTicket(
ipAddress: ipAddress,
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 *************************
MobileScannerController? mobileScannerController;
bool isGeneratingTicket = false;
void initializeMobileScanner() {
mobileScannerController = MobileScannerController(
facing: kIsWeb ? CameraFacing.front : CameraFacing.back,
detectionSpeed: DetectionSpeed.normal,
);
}
void onQRDetected(BarcodeCapture barcodes) async {
if (isGeneratingTicket) return;
final barcode = barcodes.barcodes.firstOrNull;
if (barcode == null || barcode.rawValue == null) return;
loggerService.logInfo(
"QR Found: ${barcode.rawValue} and isGeneratingTicket: $isGeneratingTicket");
final patientId = int.tryParse(barcode.rawValue!);
if (patientId != null && patientId > 0) {
isGeneratingTicket = true;
await generateTicketForQueue(
kioskQueueModel: currentSelectedKioskQueueModel,
patientId: patientId,
);
await Future.delayed(const Duration(seconds: 2))
.whenComplete(() => isGeneratingTicket = false);
}
}
void syncHubConnectionState() async {
QueuingViewModel queuingViewModel = getIt.get<QueuingViewModel>();
bool newState =
(queuingViewModel.signalrRepo as SignalrRepoImp).isConnected;
loggerService.logToFile(
message: "Hub Current Status : $newState",
source: "syncHubConnectionState-> screen_config_view_model.dart",
type: LogTypeEnum.connectivity,
);
log("[isHubConnected: $isHubConnected] , [newState: $newState]");
if (isHubConnected != newState) {
updateIsHubConnected(newState);
}
if (!isHubConnected) {
QueuingViewModel queuingViewModel = getIt.get<QueuingViewModel>();
try {
await queuingViewModel.startHubConnection();
// syncHubConnectionState will update isHubConnected
} catch (e) {
loggerService.logError("Failed to start hub connection in sync: $e");
}
} else {
log("Hub is Connected!");
}
}
}