CI / build-and-test (push) Canceled after 0s
Zeigt die Fälligkeit der vier bestehenden, manuell ausgelösten WebUntis-Abgleiche (Fehlzeiten je Lerngruppe, offene Stunden, Klassenbuch-, Hausaufgabenabgleich) in einem neuen Hub-Fenster, ohne selbst WebUntis anzufragen - nur gespeicherte Zeitstempel werden ausgewertet. Konsolidiert die bisher verstreuten Einstiegspunkte (Sidebar-Button, Dashboard-Buttons) in ein neues WebUntis-Menü plus einen kompakten Gesundheits-Indikator auf dem Dashboard. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
182 lines
6.3 KiB
C#
182 lines
6.3 KiB
C#
using Avalonia.Controls;
|
|
using Avalonia.Input;
|
|
using Avalonia.Threading;
|
|
using LehrerApp.Desktop.Services;
|
|
using LehrerApp.Desktop.ViewModels;
|
|
using LehrerApp.Desktop.Views.UntisHub;
|
|
using LehrerApp.Sync;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
|
namespace LehrerApp.Desktop.Views;
|
|
|
|
public partial class MainWindow : Window
|
|
{
|
|
private static readonly TimeSpan FinalSyncDelay = TimeSpan.FromMilliseconds(350);
|
|
private SyncEngine? _syncEngine;
|
|
private bool _finalSyncStarted;
|
|
private bool _closeAfterFinalSync;
|
|
|
|
public MainWindow()
|
|
{
|
|
InitializeComponent();
|
|
PointerMoved += (_, _) => NotifyActivity();
|
|
PointerPressed += (_, _) => NotifyActivity();
|
|
KeyDown += (_, _) => NotifyActivity();
|
|
KeyDown += OnWindowKeyDown;
|
|
}
|
|
|
|
private async void OnOpenUntisHub(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
|
|
{
|
|
await new UntisHubDialog().ShowDialog(this);
|
|
}
|
|
|
|
private async void OnOpenUntisPeriods(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
|
|
{
|
|
await UntisHubActions.RunOffenePeriodsAsync(this, App.Services.GetRequiredService<UntisHubService>());
|
|
}
|
|
|
|
private async void OnCompareUntisKlassenbuch(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
|
|
{
|
|
await UntisHubActions.RunKlassenbuchAsync(this, App.Services.GetRequiredService<UntisHubService>());
|
|
}
|
|
|
|
private async void OnCompareUntisHausaufgaben(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
|
|
{
|
|
await UntisHubActions.RunHausaufgabenAsync(this, App.Services.GetRequiredService<UntisHubService>());
|
|
}
|
|
|
|
private void OnWindowKeyDown(object? sender, KeyEventArgs e)
|
|
{
|
|
if (DataContext is not MainWindowViewModel vm) return;
|
|
|
|
var commandModifier = (e.KeyModifiers & (KeyModifiers.Control | KeyModifiers.Meta)) != 0;
|
|
if (commandModifier && e.Key == Key.K)
|
|
{
|
|
vm.OpenCommandPaletteCommand.Execute(null);
|
|
FocusCommandPalette();
|
|
e.Handled = true;
|
|
return;
|
|
}
|
|
|
|
if (commandModifier && TryGetNavigationShortcut(e.Key, out var destination))
|
|
{
|
|
vm.NavigateToCommand.Execute(destination);
|
|
e.Handled = true;
|
|
return;
|
|
}
|
|
|
|
if (!vm.IsCommandPaletteOpen) return;
|
|
if (e.Key == Key.Escape)
|
|
{
|
|
vm.CloseCommandPaletteCommand.Execute(null);
|
|
e.Handled = true;
|
|
}
|
|
else if (e.Key == Key.Enter)
|
|
{
|
|
vm.CommandPalette.ExecuteCommand.Execute(vm.CommandPalette.SelectedResult);
|
|
e.Handled = true;
|
|
}
|
|
else if (e.Key is Key.Down or Key.Up)
|
|
{
|
|
MoveCommandPaletteSelection(vm, e.Key == Key.Down ? 1 : -1);
|
|
e.Handled = true;
|
|
}
|
|
}
|
|
|
|
private static bool TryGetNavigationShortcut(Key key, out NavItem destination)
|
|
{
|
|
destination = key switch
|
|
{
|
|
Key.D1 or Key.NumPad1 => NavItem.Dashboard,
|
|
Key.D2 or Key.NumPad2 => NavItem.Groups,
|
|
Key.D3 or Key.NumPad3 => NavItem.Students,
|
|
Key.D4 or Key.NumPad4 => NavItem.Exams,
|
|
Key.D5 or Key.NumPad5 => NavItem.Planner,
|
|
Key.D6 or Key.NumPad6 => NavItem.Workload,
|
|
Key.D7 or Key.NumPad7 => NavItem.ClassTeacher,
|
|
Key.D8 or Key.NumPad8 => NavItem.Settings,
|
|
_ => default,
|
|
};
|
|
return key is >= Key.D1 and <= Key.D8 or >= Key.NumPad1 and <= Key.NumPad8;
|
|
}
|
|
|
|
private void OnOpenCommandPaletteClick(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
|
|
{
|
|
if (DataContext is MainWindowViewModel vm)
|
|
vm.OpenCommandPaletteCommand.Execute(null);
|
|
FocusCommandPalette();
|
|
}
|
|
|
|
private void FocusCommandPalette() => Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (this.FindControl<TextBox>("CommandPaletteSearchBox") is { } search)
|
|
{
|
|
search.Focus();
|
|
search.SelectAll();
|
|
}
|
|
});
|
|
|
|
private static void MoveCommandPaletteSelection(MainWindowViewModel vm, int delta)
|
|
{
|
|
var results = vm.CommandPalette.Results;
|
|
if (results.Count == 0) return;
|
|
var current = vm.CommandPalette.SelectedResult is { } selected ? results.IndexOf(selected) : -1;
|
|
vm.CommandPalette.SelectedResult = results[Math.Clamp(current + delta, 0, results.Count - 1)];
|
|
}
|
|
|
|
public void EnableFinalSync(SyncEngine syncEngine)
|
|
{
|
|
_syncEngine = syncEngine;
|
|
Closing += OnClosing;
|
|
}
|
|
|
|
/// <summary>Wendet die zuletzt gespeicherte Fenstergröße an und merkt sich die aktuelle beim
|
|
/// Schließen (14.6). Eigenständig von <see cref="EnableFinalSync"/> verdrahtet (mehrere
|
|
/// Closing-Handler sind unproblematisch) und immer aktiv, auch ohne konfigurierten
|
|
/// Sync-Server.</summary>
|
|
public void EnableWindowSizePersistence(WindowSettingsService settings)
|
|
{
|
|
var saved = settings.Load();
|
|
Width = saved.Width;
|
|
Height = saved.Height;
|
|
if (saved.IsMaximized) WindowState = WindowState.Maximized;
|
|
|
|
Closing += (_, _) =>
|
|
{
|
|
var current = settings.Load();
|
|
settings.Save(new WindowSettings
|
|
{
|
|
IsMaximized = WindowState == WindowState.Maximized,
|
|
// Im maximierten Zustand spiegeln Width/Height die Bildschirmgröße wider, nicht
|
|
// die zuletzt genutzte Normalgröße - dann den vorherigen Wert beibehalten statt
|
|
// ihn zu überschreiben.
|
|
Width = WindowState == WindowState.Normal ? Width : current.Width,
|
|
Height = WindowState == WindowState.Normal ? Height : current.Height,
|
|
});
|
|
};
|
|
}
|
|
|
|
private async void OnClosing(object? sender, WindowClosingEventArgs e)
|
|
{
|
|
if (_closeAfterFinalSync || _finalSyncStarted || _syncEngine is null)
|
|
return;
|
|
|
|
e.Cancel = true;
|
|
_finalSyncStarted = true;
|
|
await _syncEngine.SyncBeforeShutdownAsync(FinalSyncDelay);
|
|
_closeAfterFinalSync = true;
|
|
Close();
|
|
}
|
|
|
|
private void NotifyActivity()
|
|
{
|
|
if (DataContext is MainWindowViewModel vm) vm.AppLock.NotifyActivity();
|
|
}
|
|
|
|
private void OnLockScreenKeyDown(object? sender, KeyEventArgs e)
|
|
{
|
|
if (e.Key == Key.Enter && DataContext is MainWindowViewModel vm)
|
|
vm.AppLock.UnlockCommand.Execute(null);
|
|
}
|
|
}
|