using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using LehrerApp.Desktop.Services; using LehrerApp.Sync; using LehrerApp.Sync.Models; namespace LehrerApp.Desktop.ViewModels; public partial class SyncStatusViewModel : ObservableObject { private readonly SyncEngine? _engine; private readonly NotificationService? _notifications; private bool _incompatibleNotificationShown; public const string IncompatibleVersionMessage = "Diese App-Version ist veraltet oder nicht mit dem Sync-Server kompatibel. " + "Bitte aktualisiere die LehrerApp. Die Synchronisierung wurde zum Schutz deiner Daten angehalten."; [ObservableProperty] private string _statusText = "Kein Server konfiguriert"; [ObservableProperty] private string _lastSyncText = ""; [ObservableProperty] private bool _isSyncing; [ObservableProperty] private bool _isServerConfigured; [ObservableProperty] private bool _isIncompatibleVersion; [ObservableProperty] private bool _canAttemptSync; [ObservableProperty] private int _pendingCount; /// Feuert, wenn ein Sync tatsächlich Daten angewendet hat (siehe SyncEngine. /// DataChanged, TODO 10.1.11) - MainWindowViewModel nutzt das, um die gerade sichtbare Seite /// neu zu laden, da EventApplier absichtlich an jedem ViewModel vorbei direkt auf die LiteDB /// schreibt. public event Action? DataChanged; public SyncStatusViewModel(SyncEngine? engine, NotificationService? notifications = null) { _engine = engine; _notifications = notifications; IsServerConfigured = engine is not null; CanAttemptSync = IsServerConfigured; if (_engine is not null) { _engine.StatusChanged += OnStatus; _engine.DataChanged += () => DataChanged?.Invoke(); // SyncEngine feuert sein erstes StatusChanged bereits im eigenen Konstruktor // (UpdateStatus) - der läuft aber schon, während wir hier noch in GetService() // stecken, also VOR dem obigen Abonnieren. Ohne diesen Nachhol-Aufruf bliebe StatusText // nach jedem Neustart beim Default "Kein Server konfiguriert", bis der nächste // Auto-Sync oder Klick auf "Jetzt synchronisieren" den Text erstmals aktualisiert. OnStatus(_engine.Status); } } private void OnStatus(SyncStatus s) { IsSyncing = s.State == SyncState.Syncing; IsIncompatibleVersion = s.State == SyncState.IncompatibleVersion; CanAttemptSync = IsServerConfigured && !IsIncompatibleVersion; PendingCount = s.PendingEvents; StatusText = s.State switch { SyncState.Idle => PendingCount > 0 ? $"{PendingCount} ausstehend" : "Synchronisiert", SyncState.Syncing => "Synchronisiere…", SyncState.Offline => "Offline", SyncState.Error => $"Fehler: {s.ErrorMessage}", SyncState.IncompatibleVersion => "Update erforderlich", _ => "", }; LastSyncText = s.LastSyncAt.HasValue ? $"Zuletzt: {s.LastSyncAt:HH:mm}" : "Noch nie"; if (IsIncompatibleVersion && !_incompatibleNotificationShown) { _incompatibleNotificationShown = true; _notifications?.ShowError(IncompatibleVersionMessage); } } [RelayCommand(CanExecute = nameof(CanSync))] private async Task SyncNow() { if (_engine is not null) await _engine.SyncNowAsync(); } private bool CanSync() => _engine is not null && !IsSyncing; }