Files
votianlt/app/lib/app_state.dart

174 lines
5.5 KiB
Dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'models/job.dart';
import 'services/database_service.dart';
import 'services/dart_mq.dart';
import 'l10n/app_localizations.dart';
/// Global notifier for language changes
final ValueNotifier<Locale> localeNotifier = ValueNotifier<Locale>(const Locale('de'));
class AppState {
static final AppState _instance = AppState._internal();
factory AppState() => _instance;
AppState._internal();
String? _loggedInEmail;
List<Job> _assignedJobs = [];
final DatabaseService _databaseService = DatabaseService();
// Language settings
String _languageCode = 'de';
String get languageCode => _languageCode;
/// Get current locale
Locale get currentLocale => Locale(_languageCode);
/// Set language and update the global notifier
Future<void> setLanguage(String languageCode) async {
if (supportedLanguageCodes.contains(languageCode)) {
_languageCode = languageCode;
await _databaseService.saveLanguagePreference(languageCode);
localeNotifier.value = Locale(languageCode);
}
}
/// Load language preference from database
Future<void> loadLanguagePreference() async {
final savedLanguage = await _databaseService.loadLanguagePreference();
if (savedLanguage != null && supportedLanguageCodes.contains(savedLanguage)) {
_languageCode = savedLanguage;
localeNotifier.value = Locale(savedLanguage);
}
}
// Serialize persistence to avoid overlapping DB save/load cycles
bool _isPersistingJobs = false;
List<Job>? _pendingJobs; // holds the latest jobs to persist if calls overlap
// Jobs update notification (emitted once after DB was updated)
final StreamController<void> _jobsUpdatedController = StreamController<void>.broadcast();
Stream<void> get jobsUpdated => _jobsUpdatedController.stream;
/// The logged-in user's email (used as local identifier for chats)
String? get loggedInEmail => _loggedInEmail;
List<Job> get assignedJobs => List.unmodifiable(_assignedJobs);
void setLoggedInEmail(String email) {
_loggedInEmail = email;
}
Future<void> clearLogin() async {
_loggedInEmail = null;
_assignedJobs.clear();
// Clear database
await _databaseService.clearAllData();
// Notify listeners/UI that jobs were cleared
_jobsUpdatedController.add(null);
DartMQ().publish<void>(MQTopics.jobsUpdated, null);
}
bool get isLoggedIn => _loggedInEmail != null;
Future<void> setAssignedJobs(List<Job> jobs) async {
// Coalesce overlapping calls: if a persist is already running, remember only the latest
if (_isPersistingJobs) {
_pendingJobs = jobs;
return;
}
_isPersistingJobs = true;
try {
// Start with the initial batch to persist
var toPersist = jobs;
while (true) {
// Normalize first
final normalized = toPersist.map((j) => j.normalized()).toList();
// Persist normalized list to DB only (no UI notifications here)
await _databaseService.saveJobs(normalized);
// If another request came in during persistence, handle only the latest once
if (_pendingJobs != null) {
toPersist = _pendingJobs!;
_pendingJobs = null;
continue;
}
break;
}
// After DB is updated with the latest data, notify listeners once to refresh UI
_jobsUpdatedController.add(null);
// Also publish via dart_mq for app-wide decoupled messaging
DartMQ().publish<void>(MQTopics.jobsUpdated, null);
} finally {
_isPersistingJobs = false;
}
}
void addJob(Job job) {
if (!_assignedJobs.contains(job)) {
_assignedJobs.add(job);
}
}
void removeJob(String jobId) {
_assignedJobs.removeWhere((job) => job.id == jobId);
// Update database
_databaseService.saveJobs(_assignedJobs);
}
/// Delete a job by ID (called when server sends job_deleted event)
Future<void> deleteJob(String jobId) async {
_assignedJobs.removeWhere((job) => job.id == jobId);
// Delete from database
await _databaseService.deleteJob(jobId);
// Notify listeners
_jobsUpdatedController.add(null);
DartMQ().publish<void>(MQTopics.jobsUpdated, null);
}
/// Add a new job (called when server sends job_created event)
Future<void> addNewJob(Job job) async {
// Check if job already exists
if (_assignedJobs.any((j) => j.id == job.id)) {
return;
}
// Add to memory
_assignedJobs.insert(0, job);
// Persist to database
await _databaseService.saveOrUpdateJob(job);
// Notify listeners
_jobsUpdatedController.add(null);
DartMQ().publish<void>(MQTopics.jobsUpdated, null);
}
/// Load login state from saved credentials on app start
Future<void> loadLoginFromDatabase() async {
final credentials = await _databaseService.loadCredentials();
if (credentials != null) {
_loggedInEmail = credentials.email;
}
}
void updateJob(Job updatedJob) {
final index = _assignedJobs.indexWhere((job) => job.id == updatedJob.id);
if (index != -1) {
_assignedJobs[index] = updatedJob;
}
}
/// Refresh in-memory jobs from the database without emitting other notifications
Future<void> refreshJobsFromDatabase() async {
final jobs = await _databaseService.loadJobs();
_assignedJobs = jobs;
}
/// Persistently upsert a single job and refresh in-memory list
Future<void> upsertJob(Job job) async {
await _databaseService.saveOrUpdateJob(job);
final persisted = await _databaseService.loadJobs();
_assignedJobs = persisted.isNotEmpty ? persisted : _assignedJobs;
}
}