fix: Geräte-Pairing - Fehlschlag beim Einlösen war unsichtbar

RedeemPairingCode() rief nach einem Fehlschlag sofort RestartApplication()
auf, direkt nach dem Setzen von PairingStatus - die Fehlermeldung konnte
nie gerendert werden, und wurde auch nirgends geloggt. Ein Fehlschlag
(falscher/abgelaufener Code, Server nicht erreichbar) wirkte dadurch wie
ein kommentarloser Absturz ohne jede Spur.

- SettingsViewModel loggt den Fehler jetzt über AppLogger und zeigt vor
  dem Neustart einen Bestätigungsdialog mit der echten Fehlermeldung an.
- AppBootstrapper.RestartApplication() gibt unter dotnet run/IDE-Debug
  (wo ProcessPath auf den dotnet-Host statt die App zeigt) die
  ursprünglichen Kommandozeilenargumente beim Neustart mit, statt nur
  die dotnet-CLI-Hilfe zu öffnen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-18 16:35:11 +02:00
co-authored by Claude Sonnet 5
parent d59d658bba
commit f2cb3c98c6
6 changed files with 71 additions and 6 deletions
@@ -212,6 +212,12 @@ public partial class SettingsViewModel : ObservableObject
/// den Stand des anderen Geräts ersetzt wird.
public Func<Task<bool>>? OnConfirmPairingRestore { get; set; }
/// Vom Code-Behind gesetzt: zeigt einen Fehlerdialog, den der Nutzer aktiv wegklicken muss —
/// notwendig, weil RedeemPairingCode() nach einem Fehlschlag sofort neu startet (_dbContext ist
/// zu dem Zeitpunkt bereits disposed, siehe dort) und PairingStatus dadurch sonst nie sichtbar
/// gerendert würde.
public Func<string, Task>? OnShowPairingError { get; set; }
// ── Konstruktor ───────────────────────────────────────────────────────────
private readonly ISchoolHolidayRepository _schoolHolidays;
@@ -225,6 +231,7 @@ public partial class SettingsViewModel : ObservableObject
private readonly EventQueue _eventQueue;
private readonly SnapshotService? _snapshotService;
private readonly CompetencyCatalogImportService _catalogImport;
private readonly AppLogger _logger;
public SettingsViewModel(ISubjectRepository subjects, ICompetencyDomainRepository domainRepo,
IGradingKeyTemplateRepository gradingKeyTemplates, IGradingSchemeRepository gradingSchemes,
@@ -236,8 +243,9 @@ public partial class SettingsViewModel : ObservableObject
ISupervisionDutyRepository supervisionDuties, LetterTemplateService letterTemplates,
AiSettingsService aiSettings, AiPlanningService aiPlanning,
SyncSettingsService syncSettings, SyncAuthService syncAuth, EventQueue eventQueue,
SnapshotService? snapshotService = null)
AppLogger logger, SnapshotService? snapshotService = null)
{
_logger = logger;
_subjects = subjects;
_domainRepo = domainRepo;
_gradingKeyTemplates = gradingKeyTemplates;
@@ -564,6 +572,11 @@ public partial class SettingsViewModel : ObservableObject
// _dbContext ist bereits disposed, PairingStatus ist aber ein reines ViewModel-Feld
// ohne DB-Zugriff - das Setzen ist unabhängig davon noch sicher.
PairingStatus = $"Fehlgeschlagen: {ex.Message}";
_logger.Error("Geräte-Pairing (Redeem) fehlgeschlagen", ex);
// Ohne diesen Dialog würde PairingStatus nie gerendert: der direkt folgende Neustart
// (unten, auch im Fehlerfall nötig, siehe Kommentar oben) beendet den Prozess, bevor
// Avalonia den nächsten Frame zeichnen könnte.
if (OnShowPairingError is not null) await OnShowPairingError(ex.Message);
}
AppBootstrapper.RestartApplication();
}