using Avalonia.Controls; using Avalonia.Interactivity; using Avalonia.Platform.Storage; using LehrerApp.Core.Interfaces; using LehrerApp.Desktop.Services; using LehrerApp.Desktop.ViewModels; using LehrerApp.Desktop.ViewModels.Groups; using LehrerApp.Desktop.ViewModels.Planning; using LehrerApp.Desktop.ViewModels.Settings; using LehrerApp.Desktop.Views.Groups; using LehrerApp.Desktop.Views.Planning; using LehrerApp.Desktop.Views.Shared; using Microsoft.Extensions.DependencyInjection; namespace LehrerApp.Desktop.Views.Settings; public partial class SettingsView : UserControl { public SettingsView() => InitializeComponent(); protected override void OnDataContextChanged(EventArgs e) { base.OnDataContextChanged(e); if (DataContext is SettingsViewModel vm) { vm.OnConfirmRestore = ShowRestoreConfirmDialog; vm.OnAppLockChanged = () => App.Services.GetRequiredService().ApplyConfig(); vm.OnConfirmHardDelete = ShowHardDeleteConfirmDialog; vm.OnConfirmPairingRestore = ShowPairingRestoreConfirmDialog; vm.OnShowPairingError = ShowPairingErrorDialog; vm.OnSaveRecoveryFile = SaveRecoveryFile; vm.OnPickRecoveryFile = PickRecoveryFile; vm.OnConfirmRecoveryRestore = ShowRecoveryRestoreConfirmDialog; vm.OnThemeChanged = App.ApplyTheme; vm.OnReviewUntisMapping = ShowUntisMappingReviewDialog; vm.OnPickHomeroomClass = PickHomeroomClassAsync; vm.OnPickBackupDirectory = PickBackupDirectoryAsync; _ = vm.LoadSchoolLocationCommand.ExecuteAsync(null); } } private async Task ShowUntisMappingReviewDialog() { var owner = TopLevel.GetTopLevel(this) as Window; var untisSync = App.Services.GetService(); if (owner is null || untisSync is null) return; var groups = App.Services.GetRequiredService().GetAll(); var mappings = App.Services.GetRequiredService(); var subjects = App.Services.GetRequiredService(); var dialog = new UntisMappingReviewDialog { DataContext = new UntisMappingReviewDialogViewModel(untisSync, mappings, groups, subjects), }; await dialog.ShowDialog(owner); } private async Task<(int UntisId, string Name)?> PickHomeroomClassAsync() { var owner = TopLevel.GetTopLevel(this) as Window; if (owner is null) return null; var untis = App.Services.GetRequiredService(); var selectionVm = new WebUntisClassSelectionViewModel(untis); var selection = new WebUntisClassSelectionDialog { DataContext = selectionVm }; await selectionVm.InitializeAsync(); if (!await selection.ShowDialog(owner) || selectionVm.SelectedClass is null) return null; return (selectionVm.SelectedClass.UntisId, selectionVm.SelectedClass.Name); } private async Task SaveRecoveryFile(string content) { var topLevel = TopLevel.GetTopLevel(this); if (topLevel is null) return false; var file = await topLevel.StorageProvider.SaveFilePickerAsync(new FilePickerSaveOptions { Title = "Sync-Wiederherstellungsdatei speichern", SuggestedFileName = "LehrerApp-Sync-Wiederherstellung.json", FileTypeChoices = [new FilePickerFileType("JSON-Dateien") { Patterns = ["*.json"] }], }); if (file is null) return false; await File.WriteAllTextAsync(file.Path.LocalPath, content); return true; } private async Task PickBackupDirectoryAsync() { var topLevel = TopLevel.GetTopLevel(this); if (topLevel is null) return null; var folders = await topLevel.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions { Title = "Zweiten Sicherungsordner wählen", AllowMultiple = false, }); return folders.Count == 0 ? null : folders[0].Path.LocalPath; } private async Task PickRecoveryFile() { var topLevel = TopLevel.GetTopLevel(this); if (topLevel is null) return null; var files = await topLevel.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions { Title = "Sync-Wiederherstellungsdatei auswählen", AllowMultiple = false, FileTypeFilter = [new FilePickerFileType("JSON-Dateien") { Patterns = ["*.json"] }], }); return files.Count == 0 ? null : await File.ReadAllTextAsync(files[0].Path.LocalPath); } private async Task ShowRecoveryRestoreConfirmDialog() { var info = new ConfirmDialogInfo { Title = "Sync-Schlüssel ersetzen?", Message = "Der aktuelle Sync-Schlüssel dieses Geräts wird durch den aus der Datei ersetzt. " + "Die App wird danach automatisch neu gestartet.", ConfirmText = "Ersetzen", }; var dialog = new ConfirmDialog { DataContext = info }; var owner = TopLevel.GetTopLevel(this) as Window; return owner is not null && await dialog.ShowDialog(owner); } private async Task ShowRestoreConfirmDialog(BackupListItem item) { var info = new ConfirmDialogInfo { Title = "Backup wiederherstellen?", Message = $"Die aktuelle Datenbank wird durch das Backup vom {item.Display} ersetzt. " + "Die App wird danach automatisch neu gestartet.", ConfirmText = "Wiederherstellen", }; var dialog = new ConfirmDialog { DataContext = info }; var owner = TopLevel.GetTopLevel(this) as Window; return owner is not null && await dialog.ShowDialog(owner); } private async Task ShowHardDeleteConfirmDialog(ExpiredDocumentItem item) { var info = new ConfirmDialogInfo { Title = "Eintrag endgültig löschen?", Message = $"\"{item.Title}\" ({item.StudentName}) wird unwiderruflich gelöscht, nicht nur ausgeblendet.", ConfirmText = "Endgültig löschen", }; var dialog = new ConfirmDialog { DataContext = info }; var owner = TopLevel.GetTopLevel(this) as Window; return owner is not null && await dialog.ShowDialog(owner); } private async Task ShowPairingRestoreConfirmDialog() { var info = new ConfirmDialogInfo { Title = "Gerät koppeln?", Message = "Die lokale Datenbank auf diesem Gerät wird vollständig durch den Stand des anderen " + "Geräts ersetzt. Der bisherige Stand wird automatisch als Backup gesichert. " + "Die App wird danach automatisch neu gestartet.", ConfirmText = "Ersetzen", }; var dialog = new ConfirmDialog { DataContext = info }; var owner = TopLevel.GetTopLevel(this) as Window; return owner is not null && await dialog.ShowDialog(owner); } private async Task ShowPairingErrorDialog(string message) { var info = new ConfirmDialogInfo { Title = "Kopplung fehlgeschlagen", Message = $"{message}\n\nDie App wird jetzt neu gestartet (unveränderte Datenbank).", ConfirmText = "OK", }; var dialog = new ConfirmDialog { DataContext = info }; var owner = TopLevel.GetTopLevel(this) as Window; if (owner is not null) await dialog.ShowDialog(owner); } private async void OnImportClick(object? sender, RoutedEventArgs e) { var topLevel = TopLevel.GetTopLevel(this); if (topLevel is null || DataContext is not SettingsViewModel vm) return; var files = await topLevel.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions { Title = "Kompetenzkatalog importieren", AllowMultiple = false, FileTypeFilter = [new FilePickerFileType("JSON-Dateien") { Patterns = ["*.json"] }], }); if (files.Count == 0) return; var json = await File.ReadAllTextAsync(files[0].Path.LocalPath); var preview = vm.PrepareCatalogImport(json); if (preview is null) return; var owner = topLevel as Window; if (owner is null) return; var dialog = new CompetencyCatalogImportDialog { DataContext = new CompetencyCatalogImportViewModel(vm, preview), }; await dialog.ShowDialog(owner); } private async void OnExportClick(object? sender, RoutedEventArgs e) { var topLevel = TopLevel.GetTopLevel(this); if (topLevel is null || DataContext is not SettingsViewModel vm) return; var file = await topLevel.StorageProvider.SaveFilePickerAsync(new FilePickerSaveOptions { Title = "Kompetenzkatalog exportieren", SuggestedFileName = $"Kompetenzkatalog_{vm.CatalogSubject?.Name}_{vm.CatalogGradeLevel}.json", FileTypeChoices = [new FilePickerFileType("JSON-Dateien") { Patterns = ["*.json"] }], }); if (file is null) return; var json = vm.ExportCatalog(); await File.WriteAllTextAsync(file.Path.LocalPath, json); } private async void OnCopyCatalogClick(object? sender, RoutedEventArgs e) { if (DataContext is not SettingsViewModel { CatalogSubject: not null } vm) return; var owner = TopLevel.GetTopLevel(this) as Window; if (owner is null) return; var targetDialog = new CompetencyCatalogCopyTargetDialog { DataContext = new CompetencyCatalogCopyTargetViewModel( vm.CatalogSubject.Name, vm.CatalogGradeLevel), }; var targetGradeLevel = await targetDialog.ShowDialog(owner); if (targetGradeLevel is null) return; var preview = vm.PrepareCatalogCopy(targetGradeLevel.Value); if (preview is null) return; var previewDialog = new CompetencyCatalogImportDialog { DataContext = new CompetencyCatalogImportViewModel(vm, preview, "Kopie prüfen"), }; if (await previewDialog.ShowDialog(owner)) vm.SetCatalogCopyStatus(targetGradeLevel.Value); } private async void OnEditGradingKeyTemplateClick(object? sender, RoutedEventArgs e) { if (sender is not Button { Tag: GradingKeyTemplateEditItem item }) return; var dialog = new GradingKeyTemplateDialog { DataContext = item }; var owner = TopLevel.GetTopLevel(this) as Window; if (owner is not null) await dialog.ShowDialog(owner); } private async void OnImportLetterTemplateClick(object? sender, RoutedEventArgs e) { var topLevel = TopLevel.GetTopLevel(this); if (topLevel is null || DataContext is not SettingsViewModel vm) return; var files = await topLevel.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions { Title = "LehrerApp-Briefvorlage importieren", AllowMultiple = false, FileTypeFilter = [new FilePickerFileType("LehrerApp-Vorlagen") { Patterns = ["*.lavorlage"] }], }); if (files.Count > 0) vm.ImportLetterTemplate(files[0].Path.LocalPath); } private void OnOpenLetterTemplateClick(object? sender, RoutedEventArgs e) { if (sender is not Button { Tag: LetterTemplateListItem item }) return; var path = item.Model.PackagePath; if (!File.Exists(path)) return; System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(path) { UseShellExecute = true }); } private async void OnImportWorksheetTemplateClick(object? sender, RoutedEventArgs e) { var topLevel = TopLevel.GetTopLevel(this); if (topLevel is null || DataContext is not SettingsViewModel vm) return; var files = await topLevel.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions { Title = "LehrerApp-Arbeitsblattvorlage importieren", AllowMultiple = false, FileTypeFilter = [new FilePickerFileType("LehrerApp-Vorlagen") { Patterns = ["*.lavorlage"] }], }); if (files.Count > 0) vm.ImportWorksheetTemplate(files[0].Path.LocalPath); } private void OnOpenWorksheetTemplateClick(object? sender, RoutedEventArgs e) { if (sender is not Button { Tag: LetterTemplateListItem item }) return; var path = item.Model.PackagePath; if (!File.Exists(path)) return; System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(path) { UseShellExecute = true }); } }