@using DeepDrftPublic.Client.Controls @using DeepDrftPublic.Client.Controls.AudioPlayerBar @using DeepDrftPublic.Client.Services @using DeepDrftPublic.Client.Common @using DeepDrftShared.Client.Common @using Microsoft.AspNetCore.Components @inherits LayoutComponentBase @implements IDisposable @implements IAsyncDisposable
@Body @* Spacer to prevent content overlap. Height tracks the fixed player dock's live size via the --player-height var the player publishes (see AudioPlayerBar / Interop/layout/spacer.ts). *@
An unhandled error has occurred. Reload 🗙
@code { private string _audioPlayerClass = "minimized"; private const string DarkModeKey = "darkMode"; private const string StreamQualityKey = "streamQuality"; private bool _isDarkMode = false; private bool? _lastAppliedDarkMode = null; private PersistingComponentStateSubscription _persistingSubscription; private IJSObjectReference? _themeModule; [Inject] public required PersistentComponentState PersistentState { get; set; } [Inject] public required DarkModeSettings DarkModeSettings { get; set; } [Inject] public required PublicSiteSettings PublicSiteSettings { get; set; } [Inject] public required IJSRuntime JS { get; set; } protected override void OnInitialized() { base.OnInitialized(); // Restore persisted dark mode state (from server prerender) if (PersistentState.TryTakeFromJson(DarkModeKey, out var restored)) { _isDarkMode = restored; DarkModeSettings.IsDarkMode = restored; } else { _isDarkMode = DarkModeSettings.IsDarkMode; } // Restore the prerender-seeded streaming-quality preference (Phase 18 wave 18.6). Same bridge dark // mode uses: the server SettingsService seeded PublicSiteSettings from the streamQuality cookie, and // this carries it into WASM so the client boots already knowing the preference (no re-read flash, no // wrong default before the first stream). if (PersistentState.TryTakeFromJson(StreamQualityKey, out var restoredQuality)) { PublicSiteSettings.StreamQuality = restoredQuality; } // Register to persist state when prerendering completes _persistingSubscription = PersistentState.RegisterOnPersisting(PersistState); } // Sync dark mode class on so portaled MudBlazor elements (popovers, menus, selects) // inherit --deepdrft-popover-surface from body.deepdrft-theme-dark rather than from :root only. // Popovers portal outside the ThemeWrapperClass div, so only a body-level class can reach them. // Gated: only fires on first render or when _isDarkMode actually changes, to avoid redundant // JS calls on unrelated re-renders (e.g. audio player minimize/expand). protected override async Task OnAfterRenderAsync(bool firstRender) { await base.OnAfterRenderAsync(firstRender); if (firstRender || _isDarkMode != _lastAppliedDarkMode) { _lastAppliedDarkMode = _isDarkMode; _themeModule ??= await JS.InvokeAsync( "import", "./_content/DeepDrftShared.Client/js/theme/theme.js"); await _themeModule.InvokeVoidAsync("setBodyThemeClass", _isDarkMode); } } // Theme wrapper class for CSS targeting private string ThemeWrapperClass => _isDarkMode ? "deepdrft-theme-dark" : "deepdrft-theme-light"; private Task PersistState() { PersistentState.PersistAsJson(DarkModeKey, _isDarkMode); PersistentState.PersistAsJson(StreamQualityKey, PublicSiteSettings.StreamQuality); return Task.CompletedTask; } public void Dispose() { _persistingSubscription.Dispose(); } public async ValueTask DisposeAsync() { if (_themeModule != null) { try { await _themeModule.DisposeAsync(); } catch (JSDisconnectedException) { /* circuit torn down */ } } } private void ToggleAudioPlayerMinimized(bool isMinimized) { _audioPlayerClass = isMinimized ? "minimized" : "expanded"; } }