feature: Home Page & Footer Mobile Friendly
Deploy DeepDrftAPI / Build, Publish & Bundle (push) Successful in 1m56s
Deploy DeepDrftManager / Build & Publish (push) Successful in 1m3s
Deploy DeepDrftPublic / Build & Publish (push) Successful in 3m22s
Deploy DeepDrftAPI / Deploy (push) Successful in 1m33s
Deploy DeepDrftManager / Deploy (push) Successful in 1m27s
Deploy DeepDrftPublic / Deploy (push) Successful in 1m29s
Deploy DeepDrftAPI / Build, Publish & Bundle (push) Successful in 1m56s
Deploy DeepDrftManager / Build & Publish (push) Successful in 1m3s
Deploy DeepDrftPublic / Build & Publish (push) Successful in 3m22s
Deploy DeepDrftAPI / Deploy (push) Successful in 1m33s
Deploy DeepDrftManager / Deploy (push) Successful in 1m27s
Deploy DeepDrftPublic / Deploy (push) Successful in 1m29s
This commit is contained in:
@@ -9,7 +9,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="@PlayerModeClass d-flex flex-column">
|
||||
<div class="@PlayerModeClass d-flex flex-column" @ref="_playerRoot">
|
||||
<MudContainer MaxWidth="MaxWidth.Large" Class="player-inner-container">
|
||||
<MudPaper Elevation="8" Class="player-surface pa-3">
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using DeepDrftModels.DTOs;
|
||||
using DeepDrftPublic.Client.Services;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.JSInterop;
|
||||
using MudBlazor;
|
||||
|
||||
namespace DeepDrftPublic.Client.Controls.AudioPlayerBar;
|
||||
@@ -9,12 +10,25 @@ public partial class AudioPlayerBar : ComponentBase, IAsyncDisposable
|
||||
{
|
||||
[CascadingParameter] public IStreamingPlayerService? PlayerService { get; set; }
|
||||
[Parameter] public bool Fixed { get; set; } = false;
|
||||
|
||||
|
||||
[Parameter] public EventCallback<bool> OnMinimized { get; set; }
|
||||
|
||||
[Inject] private IJSRuntime JsRuntime { get; set; } = default!;
|
||||
|
||||
private bool _isMinimized = true;
|
||||
private bool _isSeeking = false;
|
||||
private double _seekPosition = 0;
|
||||
private IStreamingPlayerService? _subscribedService;
|
||||
|
||||
// Spacer-height bridge: the expanded dock is position:fixed, so MainLayout's
|
||||
// spacer reserves its space. We mirror this element's live height into a CSS
|
||||
// var via a ResizeObserver (see Interop/layout/spacer.ts) rather than a static
|
||||
// value, because the player reflows across breakpoints and grows with the
|
||||
// error banner.
|
||||
private ElementReference _playerRoot;
|
||||
private IJSObjectReference? _spacerModule;
|
||||
private bool _spacerObserved;
|
||||
|
||||
private bool IsLoaded => PlayerService?.IsLoaded ?? false;
|
||||
private bool IsLoading => PlayerService?.IsLoading ?? false;
|
||||
|
||||
@@ -66,6 +80,41 @@ public partial class AudioPlayerBar : ComponentBase, IAsyncDisposable
|
||||
|
||||
private void OnPlayerStateChanged() => InvokeAsync(StateHasChanged);
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
// Only the docked, expanded shape needs a spacer: the Fixed embed is
|
||||
// already in normal flow, and the minimized FAB floats clear of content.
|
||||
// Toggle the observer on the minimized/expanded transition only — the
|
||||
// ResizeObserver itself handles every size change in between.
|
||||
var shouldObserve = !_isMinimized && !Fixed;
|
||||
if (shouldObserve == _spacerObserved) return;
|
||||
|
||||
var module = await GetSpacerModuleAsync();
|
||||
if (module is null) return;
|
||||
|
||||
if (shouldObserve)
|
||||
await module.InvokeVoidAsync("observe", _playerRoot);
|
||||
else
|
||||
await module.InvokeVoidAsync("unobserve");
|
||||
|
||||
_spacerObserved = shouldObserve;
|
||||
}
|
||||
|
||||
private async Task<IJSObjectReference?> GetSpacerModuleAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
return _spacerModule ??= await JsRuntime.InvokeAsync<IJSObjectReference>(
|
||||
"import", "./js/layout/spacer.js");
|
||||
}
|
||||
catch (JSException)
|
||||
{
|
||||
// Module failed to load — the spacer falls back to 0px (no overlap
|
||||
// guard, but the player still works). Nothing actionable here.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task Expand()
|
||||
{
|
||||
if (_isMinimized)
|
||||
@@ -127,9 +176,10 @@ public partial class AudioPlayerBar : ComponentBase, IAsyncDisposable
|
||||
PlayerService?.ClearError();
|
||||
}
|
||||
|
||||
private void ToggleMinimized()
|
||||
private async Task ToggleMinimized()
|
||||
{
|
||||
_isMinimized = !_isMinimized;
|
||||
if (OnMinimized.HasDelegate) await OnMinimized.InvokeAsync(_isMinimized);
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
@@ -147,13 +197,27 @@ public partial class AudioPlayerBar : ComponentBase, IAsyncDisposable
|
||||
}
|
||||
}
|
||||
|
||||
public ValueTask DisposeAsync()
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_subscribedService != null)
|
||||
{
|
||||
_subscribedService.StateChanged -= OnPlayerStateChanged;
|
||||
_subscribedService = null;
|
||||
}
|
||||
return ValueTask.CompletedTask;
|
||||
|
||||
if (_spacerModule is not null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Clear the var so a torn-down player can't strand a spacer height.
|
||||
await _spacerModule.InvokeVoidAsync("unobserve");
|
||||
await _spacerModule.DisposeAsync();
|
||||
}
|
||||
catch (JSException)
|
||||
{
|
||||
// Runtime already gone (navigation/teardown) — nothing to clean up.
|
||||
}
|
||||
_spacerModule = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user