feat: Dunkelmodus, Fenstergröße merken, Papierkorb für Löschvorgänge (12.4, 14.3, 14.6)

Dunkelmodus über neuen Einstellungen-Tab "Darstellung" (Systemvorgabe/Hell/Dunkel),
Fenstergröße/Maximiert-Status wird über Sitzungen hinweg gemerkt (bewusst ohne
Fensterposition), und ein generischer Snapshot-basierter Papierkorb (30 Tage) für
Sitzpläne, Noten, Notenschlüssel-Vorlagen, Aufgaben und Zeiteinträge. Details und
bewusste Scope-Entscheidungen (Spaltenbreiten zurückgestellt, Farb-Audit für
Dunkelmodus offen, welche Entitäten der Papierkorb abdeckt) in TODO.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-20 16:26:10 +02:00
co-authored by Claude Sonnet 5
parent d9d007cd4b
commit 331033db5c
20 changed files with 1023 additions and 12 deletions
+29
View File
@@ -2,6 +2,7 @@ using LehrerApp.Core.Interfaces;
using LehrerApp.Core.Models;
using LehrerApp.Core.Services;
using LehrerApp.Desktop.Services;
using LehrerApp.Desktop.ViewModels.Settings;
using LehrerApp.Sync;
using LehrerApp.Sync.Crypto;
@@ -69,6 +70,19 @@ public static class TestSupport
/// Für Tests, die nicht speziell den "Schlüssel neu erzeugt"-Warnzustand prüfen.
public static SyncKeyStatus BuildSyncKeyStatus(bool keyWasRegenerated = false) => new(keyWasRegenerated);
/// Neue, leere Fakes je Aufruf - für Tests, die nicht speziell TrashViewModel-Verhalten prüfen.
public static TrashViewModel BuildTrashViewModel() => new(
new FakeTrash(), new FakeGrades(), new FakeWorkTasks(), new FakeTimeEntries(),
new FakeSeatingPlans(), new FakeGradingKeyTemplates());
/// Analog zu <see cref="BuildAiSettingsService"/>, eigenes Temp-Verzeichnis je Aufruf.
public static AppearanceSettingsService BuildAppearanceSettingsService()
{
var tempPath = Path.Combine(Path.GetTempPath(), $"lehrerapp-appearance-tests-{Guid.NewGuid():N}");
Directory.CreateDirectory(tempPath);
return new AppearanceSettingsService(tempPath);
}
/// Eigenes Temp-Verzeichnis je Aufruf, damit Tests sich nicht gegenseitig über dieselbe
/// sync.key stören.
public static SyncKeyRecoveryService BuildSyncKeyRecoveryService()
@@ -123,6 +137,17 @@ public class FakeSeatingPlans(List<SeatingPlan>? initial = null) : ISeatingPlanR
_all.Add(plan);
}
public void Delete(Guid id) => _all.RemoveAll(p => p.Id == id);
// Trash-Mechanismus (14.3) lebt real in LiteDbContext.MoveToTrash/RestoreFromTrash - Fakes
// bilden ihn bewusst nicht nach, ViewModel-Tests brauchen das nicht.
public void Restore(Guid trashId) { }
}
public class FakeTrash(List<TrashedItem>? initial = null) : ITrashRepository
{
private readonly List<TrashedItem> _all = initial ?? [];
public void Add(TrashedItem item) => _all.Add(item);
public List<TrashedItem> GetAll() => _all.OrderByDescending(t => t.DeletedAt).ToList();
public void PurgeOlderThan(DateTime cutoffUtc) => _all.RemoveAll(t => t.DeletedAt < cutoffUtc);
}
public class FakeSessions(List<ParticipationSession> all) : IParticipationSessionRepository
@@ -190,6 +215,7 @@ public class FakeGrades : IGradeRepository
_all.Add(grade);
}
public void Delete(Guid id) => _all.RemoveAll(g => g.Id == id);
public void Restore(Guid trashId) { } // siehe FakeSeatingPlans.Restore
}
public class FakeExams(List<Exam> all) : IExamRepository
@@ -227,6 +253,7 @@ public class FakeGradingKeyTemplates : IGradingKeyTemplateRepository
public GradingKeyTemplate? GetById(Guid id) => _all.FirstOrDefault(t => t.Id == id);
public void Save(GradingKeyTemplate template) { _all.RemoveAll(t => t.Id == template.Id); _all.Add(template); }
public void Delete(Guid id) => _all.RemoveAll(t => t.Id == id);
public void Restore(Guid trashId) { } // siehe FakeSeatingPlans.Restore
}
public class FakeSchemes : IGradingSchemeRepository
@@ -407,6 +434,7 @@ public class FakeWorkTasks : IWorkTaskRepository
public List<WorkTask> GetAll() => _all.ToList();
public void Save(WorkTask task) { _all.RemoveAll(t => t.Id == task.Id); _all.Add(task); }
public void Delete(Guid id) => _all.RemoveAll(t => t.Id == id);
public void Restore(Guid trashId) { } // siehe FakeSeatingPlans.Restore
}
public class FakeTimeEntries : ITimeEntryRepository
@@ -419,6 +447,7 @@ public class FakeTimeEntries : ITimeEntryRepository
public List<TimeEntry> GetByTask(Guid taskId) => _all.Where(e => e.TaskId == taskId).ToList();
public void Save(TimeEntry entry) { _all.RemoveAll(e => e.Id == entry.Id); _all.Add(entry); }
public void Delete(Guid id) => _all.RemoveAll(e => e.Id == id);
public void Restore(Guid trashId) { } // siehe FakeSeatingPlans.Restore
}
public class FakeReportGrades : IReportGradeRepository