Files
decePubClient/Shared/NavMenu.razor
T
thepraandClaude Opus 5.5 81b92e012b Make every theme setting reachable and the theme controls effective
With the default settings (follow the system, light grey) the nav's hue slider
and dark toggle did nothing visible, and no UI could leave that mode. The slider
also always wrote the light hue, even in dark mode.

PageSettings gains SetDarkMode, SetGray and SetHue, and CascadingState gains
UpdatePageSettings. They keep the flags consistent:
- choosing light or dark leaves system theming;
- picking a hue leaves grey;
- grey follows the variant shown.
theme.js reports which variant is showing (AppStatusService.IsDarkTheme), so the
nav toggle and slider edit that one, including the OS's choice in system mode.

Settings > General, which was empty, now edits every theme setting:
- follow the system;
- dark mode;
- grey;
- the light and dark hues.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
2026-10-03 12:43:07 +02:00

161 lines
5.9 KiB
Plaintext

@inherits LocalizableComponentBase
<nav class="flex justify-between items-center md:items-start pt-3 px-3 md:p-0">
<div class="relative md:w-52">
<DButton Size="DSize.Sm" class="md:hidden" OnClick="ToggleNavMenu">
<i class="ion-md-menu text-lg"></i>
</DButton>
<div class="absolute bg-base-100 md:relative top-full md:top-auto mt-2 w-52 md:w-full overflow-y-auto rounded-lg neo-raised neo-sm md:shadow-none! z-50 @menuToggle md:block">
<div class="flex flex-col gap-3 p-3 md:p-4">
<div class="flex gap-3 p-3 md:p-4 neo-raised neo-xs rounded-xl bg-cover bg-no-repeat bg-right" style="background-image:linear-gradient(to left, var(--color-base-100) 55%, transparent), url('@(CascadingState.User?.BackgroundUrl)');">
<a class="block h-8 w-8 md:h-12 md:w-12 flex-none" href="@CascadingState.User?.ProfileUrl" title="@CascadingState.User?.UserName">
<img alt="@CascadingState.User?.UserName" class="h-8 w-8 md:h-12 md:w-12 object-cover rounded-full neo-raised neo-2xs" src="@(CascadingState.User?.PictureUrl ?? "/imgs/icon-192.png")" />
</a>
<div class="flex flex-col w-full min-w-0 gap-2">
<p class="shrink truncate">
<b title="@CascadingState.User?.DisplayName">
@CascadingState.User?.DisplayName
</b>
</p>
<p class="flex-1 min-w-6 truncate text-xs">
<a class="underline" href="@CascadingState.User?.ProfileUrl" title="@CascadingState.User?.UserName">
@CascadingState.User?.UserName
</a>
</p>
</div>
</div>
<NavLink ActiveClass="btn-active" class="btn justify-start" href="/" Match="NavLinkMatch.All">
<i class="ion-md-home"></i>
<span>@Localizer["Home"]</span>
</NavLink>
<NavLink ActiveClass="btn-active" class="btn justify-start" href="settings">
<i class="ion-md-settings"></i>
<span>@Localizer["Settings"]</span>
</NavLink>
@*<AuthorizeView>
<Authorized>
<NavLink ActiveClass="btn-active" class="btn justify-start" href="administration">
@Localizer["Administration"]
</NavLink>
<NavLink ActiveClass="btn-active" class="btn justify-start" href="logout">
@Localizer["Logout"]
</NavLink>
</Authorized>
<NotAuthorized>
<NavLink ActiveClass="btn-active" class="btn justify-start" href="login">
@Localizer["Login"]
</NavLink>
</NotAuthorized>
</AuthorizeView>*@
<NavLink ActiveClass="btn-active" class="btn justify-start" href="administration">
<i class="ion-md-switch text-error"></i>
<span>@Localizer["Administration"]</span>
</NavLink>
<NavLink ActiveClass="btn-active" class="btn justify-start" href="login">
<i class="ion-md-log-in text-success"></i>
<span>@Localizer["Login"]</span>
</NavLink>
<NavLink ActiveClass="btn-active" class="btn justify-start" href="logout">
<i class="ion-md-log-out text-error"></i>
<span>@Localizer["Logout"]</span>
</NavLink>
<div class="flex items-center gap-3">
<DButton Size="DSize.Sm" Shape="DButtonShape.Circle" class="flex-none" OnClick="ResetToOriginalColour"
title="@Localizer["Reset"]">
<i class="ion-md-refresh"></i>
</DButton>
<DRange TValue="short" class="neo-hue-track w-full" Size="DSize.Xs" Min="0" Max="359" Step="1"
Value="ThemeIndexColour" ValueChanged="UpdateThemeColour" ValueExpression="() => ThemeIndexColour" />
</div>
<div class="flex justify-end">
<DToggle Value="IsDark" ValueChanged="UpdateThemeDarkMode" ValueExpression="() => IsDark">
<UncheckedIcon>
<i class="ion-md-sunny"></i>
</UncheckedIcon>
<CheckedIcon>
<i class="ion-md-moon"></i>
</CheckedIcon>
</DToggle>
</div>
</div>
</div>
</div>
<div class="md:hidden">
<img alt="" class="h-[30px]" src="imgs/icon-512.png">
</div>
<div class="md:hidden">
<a class="block rounded-full neo-raised neo-2xs" href="settings">
<img alt="@CascadingState.User?.UserName" class="object-cover w-[30px] h-[30px] rounded-full"
src="@CascadingState.User?.PictureUrl">
</a>
</div>
</nav>
@code {
[CascadingParameter] CascadingState CascadingState { get; set; }
string menuToggle = "hidden";
PageSettings Settings => CascadingState.PublicCacheData?.PageSettings ?? new();
// 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 ? Settings.DarkThemeIndexColour : Settings.LightThemeIndexColour;
protected override void OnInitialized()
{
if (!CascadingState.Status.IsMobileMedia())
menuToggle = default;
}
private void ToggleNavMenu()
{
menuToggle = menuToggle is { Length: > 0 } ? default : "hidden";
}
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);
// }
}
}