@inherits LocalizableComponentBase @* On a phone the menu opens from the hamburger over the page, and closes after a choice (any navigation, a persona switch), on a tap outside it or on Escape; from md it is the sidebar, which scrolls on its own when the window is short (a phone held sideways). *@ @code { [Inject] ConfirmService Confirm { get; set; } [Inject] OverlayService Overlays { get; set; } bool isMenuOpen; IDisposable overlay; // open, the Back button closes it void SetMenu(bool open) { isMenuOpen = open; if (open && overlay is null) overlay = Overlays.Open(() => InvokeAsync(() => { SetMenu(false); StateHasChanged(); })); else if (!open && overlay is not null) { overlay.Dispose(); overlay = null; } } bool isPersonaMenuOpen; string PersonaBackground => CascadingState.Persona?.Header is { Length: > 0 } header && !header.Contains("/missing-") ? $"background-image:linear-gradient(to left, var(--color-base-100) 55%, transparent), url('{header}');" : null; // The toggle and the slider work on the variant that is showing; with system theming that is the OS's choice. bool IsDark => CascadingState.Status.IsDarkTheme(); short ThemeIndexColour => IsDark ? CascadingState.PublicCacheData.PageSettings.DarkThemeIndexColour : CascadingState.PublicCacheData.PageSettings.LightThemeIndexColour; protected override void OnInitialized() { IsLoading = false; Navigation.LocationChanged += OnLocationChanged; } void OnLocationChanged(object sender, LocationChangedEventArgs e) { if (!isMenuOpen) return; SetMenu(false); InvokeAsync(StateHasChanged); } void CloseMenu() => SetMenu(false); void OnKeyDown(KeyboardEventArgs e) { if (e.Key == "Escape") SetMenu(false); } public override async ValueTask DisposeAsync() { Navigation.LocationChanged -= OnLocationChanged; overlay?.Dispose(); await base.DisposeAsync(); } async Task SwitchPersona(string personaId) { SetMenu(false); var result = await CascadingState.SwitchPersona(personaId); if (result.IsInvalid) Toast.AddResponseError(result); } private void ToggleNavMenu() => SetMenu(!isMenuOpen); async Task Logout() { SetMenu(false); if (!await Confirm.Ask(Localizer["Log out?"], Localizer["Every persona of this login is signed out on this device."], Localizer["Logout"], true)) return; Navigation.NavigateTo("logout"); } protected async Task ResetToOriginalColour() { var isDark = IsDark; await CascadingState.UpdatePageSettings(settings => settings.SetHue(isDark, isDark ? PageSettings.DefaultDarkThemeIndexColour : PageSettings.DefaultLightThemeIndexColour)); // if (AuthData?.User != null) // { // AuthData.User.UserSettings.ThemeIndexColour = ThemeIndexColour; // await Storage.SetItemAsync(nameof(AuthData), AuthData); // } } // Picking a hue also leaves the grey themes, which otherwise ignore it. protected async Task UpdateThemeColour(short indexColour) { var isDark = IsDark; await CascadingState.UpdatePageSettings(settings => settings.SetHue(isDark, indexColour)); // if (AuthData?.User != null) // { // AuthData.User.UserSettings.ThemeIndexColour = ThemeIndexColour; // await Storage.SetItemAsync(nameof(AuthData), AuthData); // } } // Choosing light or dark here leaves system theming; Settings can turn it back on. protected async Task UpdateThemeDarkMode(bool isDarkMode) { await CascadingState.UpdatePageSettings(settings => settings.SetDarkMode(isDarkMode)); // if (AuthData?.User != null) // { // AuthData.User.UserSettings.ThemeIsDarkMode = ThemeIsDarkMode; // await Storage.SetItemAsync(nameof(AuthData), AuthData); // } } }