diff --git a/LehrerApp.Desktop/ViewModels/Settings/SettingsViewModel.cs b/LehrerApp.Desktop/ViewModels/Settings/SettingsViewModel.cs index 182a976..ca4e28a 100644 --- a/LehrerApp.Desktop/ViewModels/Settings/SettingsViewModel.cs +++ b/LehrerApp.Desktop/ViewModels/Settings/SettingsViewModel.cs @@ -195,6 +195,8 @@ public partial class SettingsViewModel : ObservableObject [ObservableProperty] private string _syncLoginError = ""; [ObservableProperty] private bool _syncIsLoggedIn; [ObservableProperty] private string _syncConnectionStatus = ""; + [ObservableProperty] private string _syncForceResyncStatus = ""; + [ObservableProperty] private bool _syncForceResyncBusy; public ObservableCollection SyncConflicts { get; } = []; @@ -229,6 +231,7 @@ public partial class SettingsViewModel : ObservableObject private readonly SyncSettingsService _syncSettings; private readonly SyncAuthService _syncAuth; private readonly EventQueue _eventQueue; + private readonly SyncEngine? _syncEngine; private readonly SnapshotService? _snapshotService; private readonly CompetencyCatalogImportService _catalogImport; private readonly AppLogger _logger; @@ -243,7 +246,7 @@ public partial class SettingsViewModel : ObservableObject ISupervisionDutyRepository supervisionDuties, LetterTemplateService letterTemplates, AiSettingsService aiSettings, AiPlanningService aiPlanning, SyncSettingsService syncSettings, SyncAuthService syncAuth, EventQueue eventQueue, - AppLogger logger, SnapshotService? snapshotService = null) + AppLogger logger, SnapshotService? snapshotService = null, SyncEngine? syncEngine = null) { _logger = logger; _subjects = subjects; @@ -270,6 +273,7 @@ public partial class SettingsViewModel : ObservableObject _syncAuth = syncAuth; _eventQueue = eventQueue; _snapshotService = snapshotService; + _syncEngine = syncEngine; _catalogImport = new CompetencyCatalogImportService(domainRepo); LoadSubjects(); LoadShorthandCodes(); @@ -489,6 +493,31 @@ public partial class SettingsViewModel : ObservableObject AppBootstrapper.RestartApplication(); } + /// + /// Setzt den lokalen Sync-Fortschritt zurück und lädt danach alle Ereignisse dieses Nutzers + /// erneut vom Server - manueller Reparaturweg, falls dieses Gerät wiederholt keine Änderungen + /// eines anderen Geräts erhält (z.B. nach einem lokal bereits zu weit vorgerückten Cursor, + /// siehe TODO 10.1.10). Sicher wiederholbar: EventApplier wendet jedes Ereignis über Upsert/ + /// Delete-by-Id idempotent an. + /// + [RelayCommand] + private async Task SyncForceFullResync() + { + if (_syncEngine is null) { SyncForceResyncStatus = "Sync nicht konfiguriert."; return; } + SyncForceResyncBusy = true; + SyncForceResyncStatus = "Lade alle Ereignisse erneut…"; + try + { + _eventQueue.SetLastServerSeq(0); + var result = await _syncEngine.SyncNowAsync(); + SyncForceResyncStatus = result.Success + ? $"Abgeschlossen - {result.EventsPulled} Ereignis(se) erneut geladen." + : $"Fehlgeschlagen: {result.Reason}"; + LoadSyncConflicts(); + } + finally { SyncForceResyncBusy = false; } + } + // ── Synchronisation: Konflikte ──────────────────────────────────────────── // // Zeigt, was ConflictResolver bereits entschieden hat (welche Seite gewonnen hat) — kein diff --git a/LehrerApp.Desktop/Views/Settings/SettingsView.axaml b/LehrerApp.Desktop/Views/Settings/SettingsView.axaml index b20d081..055b9fd 100644 --- a/LehrerApp.Desktop/Views/Settings/SettingsView.axaml +++ b/LehrerApp.Desktop/Views/Settings/SettingsView.axaml @@ -775,6 +775,17 @@ + + + + +