174 lines
5.5 KiB
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;
|
|
}
|
|
}
|