redesign(public): session detail as hero-overlay composition, NowPlaying-themed
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
@page "/sessions/{Id:long}"
|
||||
@using DeepDrftModels.DTOs
|
||||
@using DeepDrftPublic.Client.Controls
|
||||
@using DeepDrftPublic.Client.Services
|
||||
@inherits ReleaseDetailBase
|
||||
|
||||
<PageTitle>@(ViewModel.Release?.Title ?? "Session") - DeepDrft</PageTitle>
|
||||
@@ -44,54 +46,103 @@ else
|
||||
var hasGenre = release.Genre is not null;
|
||||
var hasDate = release.ReleaseDate is not null;
|
||||
|
||||
<ReleaseDetailScaffold Title="@release.Title"
|
||||
Artist="@release.Artist"
|
||||
Track="@ViewModel.Track"
|
||||
BackHref="/sessions"
|
||||
BackLabel="All sessions"
|
||||
ShowMeta="@(hasGenre || hasDate)">
|
||||
<Hero>
|
||||
<div class="session-detail-hero">
|
||||
@if (!string.IsNullOrEmpty(heroImage))
|
||||
<MudContainer MaxWidth="MaxWidth.Large" Class="session-detail-page">
|
||||
|
||||
<MudLink Href="/sessions" Typo="Typo.body2" Class="deepdrft-track-detail-back">
|
||||
← All sessions
|
||||
</MudLink>
|
||||
|
||||
@* The hero is the positioning context for every overlay row; the gradient shim and the
|
||||
top/bottom overlays are absolutely positioned children of this wrapper. *@
|
||||
<div class="session-hero">
|
||||
@if (!string.IsNullOrEmpty(heroImage))
|
||||
{
|
||||
<MudPaper Elevation="0" Square="true" Class="session-hero-img"
|
||||
Style="@($"background-image: url('api/image/{Uri.EscapeDataString(heroImage)}');")" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudPaper Elevation="0" Square="true" Class="session-hero-placeholder deepdrft-gradient-soft-secondary">
|
||||
<MudIcon Icon="@Icons.Material.Filled.Piano" Color="Color.Primary" />
|
||||
</MudPaper>
|
||||
}
|
||||
|
||||
@* Darkening shim so overlaid text/controls stay legible over any image. *@
|
||||
<div class="session-hero-shim"></div>
|
||||
|
||||
@* Top overlay: secondary details (genre, release date) and the share affordance. *@
|
||||
<div class="session-hero-top">
|
||||
<MudStack Row AlignItems="AlignItems.Center" Spacing="3" Class="session-hero-meta">
|
||||
@if (hasGenre)
|
||||
{
|
||||
<MudChip T="string" Variant="Variant.Outlined" Class="session-overlay-chip">
|
||||
@release.Genre
|
||||
</MudChip>
|
||||
}
|
||||
@if (hasDate)
|
||||
{
|
||||
<div class="session-overlay-date">
|
||||
<span class="session-overlay-label">Released</span>
|
||||
<span class="session-overlay-value">@release.ReleaseDate!.Value.ToString("MMMM yyyy")</span>
|
||||
</div>
|
||||
}
|
||||
</MudStack>
|
||||
@if (ViewModel.Track is not null)
|
||||
{
|
||||
<MudPaper Elevation="2" Class="session-detail-hero-img"
|
||||
Style="@($"background-image: url('api/image/{Uri.EscapeDataString(heroImage)}');")" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudPaper Elevation="2" Class="deepdrft-track-detail-cover-placeholder deepdrft-gradient-soft-secondary">
|
||||
<MudIcon Icon="@Icons.Material.Filled.Piano" Color="Color.Primary" />
|
||||
</MudPaper>
|
||||
<div class="session-hero-share">
|
||||
<SharePopover EntryKey="@ViewModel.Track.EntryKey" />
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@if (showCover)
|
||||
{
|
||||
<div class="session-detail-cover">
|
||||
<MudPaper Elevation="2" Class="deepdrft-track-detail-cover-art"
|
||||
Style="@($"background-image: url('api/image/{Uri.EscapeDataString(release.ImagePath!)}');")" />
|
||||
</div>
|
||||
}
|
||||
</Hero>
|
||||
<MetaContent>
|
||||
@if (hasGenre)
|
||||
{
|
||||
<div>
|
||||
<MudChip T="string" Variant="Variant.Outlined" Color="Color.Tertiary" Class="deepdrft-genre-chip">
|
||||
@release.Genre
|
||||
</MudChip>
|
||||
</div>
|
||||
}
|
||||
@if (hasDate)
|
||||
{
|
||||
<div>
|
||||
<MudText Typo="Typo.overline">Released</MudText>
|
||||
<MudText Typo="Typo.body1">@release.ReleaseDate!.Value.ToString("MMMM yyyy")</MudText>
|
||||
</div>
|
||||
}
|
||||
</MetaContent>
|
||||
</ReleaseDetailScaffold>
|
||||
|
||||
@* Bottom overlay: cover thumbnail, title/artist, and the play affordance in one row. *@
|
||||
<div class="session-hero-bottom">
|
||||
<MudStack Row AlignItems="AlignItems.Center" Spacing="4" Class="session-hero-bottom-row">
|
||||
@if (showCover)
|
||||
{
|
||||
<div class="session-cover-thumb">
|
||||
<MudPaper Elevation="0" Square="true" Class="deepdrft-track-detail-cover-art"
|
||||
Style="@($"background-image: url('api/image/{Uri.EscapeDataString(release.ImagePath!)}');")" />
|
||||
</div>
|
||||
}
|
||||
<div class="session-hero-titles">
|
||||
<div class="session-overlay-title">@release.Title</div>
|
||||
<div class="session-overlay-artist">@release.Artist</div>
|
||||
</div>
|
||||
@if (ViewModel.Track is not null)
|
||||
{
|
||||
<div class="session-hero-play">
|
||||
<PlayStateIcon Track="@ViewModel.Track" Size="Size.Large" Color="Color.Secondary" OnToggle="@PlayTrack" />
|
||||
</div>
|
||||
}
|
||||
</MudStack>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</MudContainer>
|
||||
}
|
||||
|
||||
@code {
|
||||
protected override string PersistKey => "session-detail";
|
||||
|
||||
[CascadingParameter] public IStreamingPlayerService? PlayerService { get; set; }
|
||||
|
||||
// Mirrors the play-toggle wiring the shared scaffold owns. Session detail composes the player
|
||||
// affordance directly (it diverges from ReleaseDetailScaffold for the overlay layout), so the
|
||||
// toggle logic lives here: toggle if this track is already active, otherwise start a fresh stream.
|
||||
private async Task PlayTrack()
|
||||
{
|
||||
var track = ViewModel.Track;
|
||||
if (track is null || PlayerService is null) return;
|
||||
|
||||
var isThisTrack = PlayerService.CurrentTrack?.Id == track.Id;
|
||||
if (isThisTrack && (PlayerService.IsPlaying || PlayerService.IsPaused))
|
||||
{
|
||||
await PlayerService.TogglePlayPause();
|
||||
}
|
||||
else
|
||||
{
|
||||
await PlayerService.SelectTrackStreaming(track);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user