diff --git a/DeepDrftPublic.Client/Controls/AudioPlayerBar/AudioPlayerBar.razor b/DeepDrftPublic.Client/Controls/AudioPlayerBar/AudioPlayerBar.razor index 1c14002..11c1658 100644 --- a/DeepDrftPublic.Client/Controls/AudioPlayerBar/AudioPlayerBar.razor +++ b/DeepDrftPublic.Client/Controls/AudioPlayerBar/AudioPlayerBar.razor @@ -14,8 +14,7 @@ else
- PlayerService?.IsLoading ?? false; private bool IsStreaming => PlayerService?.CanStartStreaming ?? false; private bool IsStreamingMode => PlayerService?.IsStreamingMode ?? false; - private bool IsPlaying => PlayerService?.IsPlaying ?? false; - private bool IsPaused => PlayerService?.IsPaused ?? false; private double? Duration => PlayerService?.Duration; private double Volume => PlayerService?.Volume ?? 0; private double LoadProgress => PlayerService?.LoadProgress ?? 0; diff --git a/DeepDrftPublic.Client/Controls/AudioPlayerBar/PlayerControls.razor b/DeepDrftPublic.Client/Controls/AudioPlayerBar/PlayerControls.razor index b091987..8a0134e 100644 --- a/DeepDrftPublic.Client/Controls/AudioPlayerBar/PlayerControls.razor +++ b/DeepDrftPublic.Client/Controls/AudioPlayerBar/PlayerControls.razor @@ -1,9 +1,11 @@ +@namespace DeepDrftPublic.Client.Controls.AudioPlayerBar +@using DeepDrftPublic.Client.Controls + - + Disabled="!IsLoaded" + OnToggle="@TogglePlayPause"/> - @if (IsLoading && !IsStreaming) diff --git a/DeepDrftPublic.Client/Controls/AudioPlayerBar/PlayerTransportZone.razor.cs b/DeepDrftPublic.Client/Controls/AudioPlayerBar/PlayerTransportZone.razor.cs index d30a2dc..c99ce3c 100644 --- a/DeepDrftPublic.Client/Controls/AudioPlayerBar/PlayerTransportZone.razor.cs +++ b/DeepDrftPublic.Client/Controls/AudioPlayerBar/PlayerTransportZone.razor.cs @@ -4,7 +4,6 @@ namespace DeepDrftPublic.Client.Controls.AudioPlayerBar; public partial class PlayerTransportZone : ComponentBase { - [Parameter] public bool IsPlaying { get; set; } [Parameter] public bool IsLoaded { get; set; } [Parameter] public bool IsLoading { get; set; } [Parameter] public bool IsStreaming { get; set; } diff --git a/DeepDrftPublic.Client/Controls/PlayStateIcon.razor b/DeepDrftPublic.Client/Controls/PlayStateIcon.razor new file mode 100644 index 0000000..e64917d --- /dev/null +++ b/DeepDrftPublic.Client/Controls/PlayStateIcon.razor @@ -0,0 +1,7 @@ +@namespace DeepDrftPublic.Client.Controls + + diff --git a/DeepDrftPublic.Client/Controls/PlayStateIcon.razor.cs b/DeepDrftPublic.Client/Controls/PlayStateIcon.razor.cs new file mode 100644 index 0000000..a9a9ceb --- /dev/null +++ b/DeepDrftPublic.Client/Controls/PlayStateIcon.razor.cs @@ -0,0 +1,62 @@ +using DeepDrftModels.DTOs; +using DeepDrftPublic.Client.Helpers; +using DeepDrftPublic.Client.Services; +using Microsoft.AspNetCore.Components; +using MudBlazor; + +namespace DeepDrftPublic.Client.Controls; + +/// +/// Renders the play/pause transport glyph for either a specific track or global playback, +/// reading live state off the cascaded . The icon is +/// always resolved through . +/// +public partial class PlayStateIcon : ComponentBase, IDisposable +{ + [CascadingParameter] public IStreamingPlayerService? PlayerService { get; set; } + + /// + /// When non-null, the icon reflects this track's playback state (active only when it is the + /// current track). When null, it reflects global playback state. + /// + [Parameter] public TrackDto? Track { get; set; } + + [Parameter] public Size Size { get; set; } = Size.Medium; + [Parameter] public Color Color { get; set; } = Color.Primary; + [Parameter] public bool Disabled { get; set; } = false; + [Parameter] public EventCallback OnToggle { get; set; } + + private IStreamingPlayerService? _subscribedService; + + private bool IsActive => Track is null || PlayerService?.CurrentTrack?.Id == Track.Id; + private bool IsPlaying => IsActive && (PlayerService?.IsPlaying ?? false); + private bool IsPaused => IsActive && (PlayerService?.IsPaused ?? false); + + private string Icon => PlaybackIcons.Resolve(IsPlaying, IsPaused); + + protected override void OnParametersSet() + { + // The cascade is IsFixed, so the provider's re-render never reaches us; subscribe to the + // multicast side-channel to re-render on every player state change. Reference-guarded so + // re-parametering is idempotent. Mirrors AudioPlayerBar / TracksView. + if (PlayerService != null && !ReferenceEquals(PlayerService, _subscribedService)) + { + if (_subscribedService != null) + _subscribedService.StateChanged -= OnPlayerStateChanged; + + PlayerService.StateChanged += OnPlayerStateChanged; + _subscribedService = PlayerService; + } + } + + private void OnPlayerStateChanged() => InvokeAsync(StateHasChanged); + + public void Dispose() + { + if (_subscribedService != null) + { + _subscribedService.StateChanged -= OnPlayerStateChanged; + _subscribedService = null; + } + } +} diff --git a/DeepDrftPublic.Client/Helpers/PlaybackIcons.cs b/DeepDrftPublic.Client/Helpers/PlaybackIcons.cs new file mode 100644 index 0000000..1357b04 --- /dev/null +++ b/DeepDrftPublic.Client/Helpers/PlaybackIcons.cs @@ -0,0 +1,14 @@ +using MudBlazor; + +namespace DeepDrftPublic.Client.Helpers; + +/// +/// Single source of truth for mapping playback state to a transport glyph across +/// DeepDrftPublic.Client. Surfaces that render a play/pause icon call +/// instead of inlining their own ternary. +/// +public static class PlaybackIcons +{ + public static string Resolve(bool isPlaying, bool isPaused) + => (isPlaying && !isPaused) ? Icons.Material.Filled.Pause : Icons.Material.Filled.PlayArrow; +}