web-player: unify Artwork and Avatar
Add src/lib/components/ui/Artwork.svelte and Avatar.svelte (B.3) and migrate every use of Cover, PlaylistArt, MixArt and ArtistAvatar (Sidebar, Queue, TrackInfo, TrackContextMenu, AlbumContextMenu, TrackTitleCell, AlbumCard, PlaylistCard, MixTile, AlbumHeader, PlaylistHeader, Home, Explore, mix, user profile, TopBar, MobileHeader). Remove the 4 old components. Artwork: size decides both dimension and radius automatically (cover-*/radius tokens from the earlier token pass); fallback chain is src -> mosaic (>=4 trackIds) -> first cover -> placeholder, cascading if each step fails (previously split across 3 components with slightly different logic each). Adds a 'fill' size (aspect-square w-full), not in the spec, needed for fluid grid cards until MediaCard lands later. TrackTitleCell's `coverSize` prop (free-form Tailwind classes) becomes `size: 'sm'|'lg'`, delegating to Artwork. SearchResultRow loses its `kind` prop and the per-type radius (now decided by Artwork/Avatar). Product decision (not blocking): remove the name under the profile avatar on /u/[id] since it duplicated the CollectionHeader title. Small radius/size deltas are documented in the plan's README (auto-radius by size replaces ad-hoc classes; see table B.1.1/B.1.2). Verified with npm run check/build; no authenticated visual QA (Docker unavailable this session).
This commit is contained in:
@@ -9,8 +9,8 @@
|
||||
import Alert from '$lib/components/ui/Alert.svelte';
|
||||
import EmptyState from '$lib/components/ui/EmptyState.svelte';
|
||||
import InfiniteScroll from '$lib/components/ui/InfiniteScroll.svelte';
|
||||
import Cover from '$lib/components/ui/Cover.svelte';
|
||||
import PlaylistArt from '$lib/components/ui/PlaylistArt.svelte';
|
||||
import Artwork from '$lib/components/ui/Artwork.svelte';
|
||||
import Avatar from '$lib/components/ui/Avatar.svelte';
|
||||
import NowPlaying from '$lib/components/ui/NowPlaying.svelte';
|
||||
import SearchResultRow from '$lib/components/ui/SearchResultRow.svelte';
|
||||
import SectionHeading from '$lib/components/ui/SectionHeading.svelte';
|
||||
@@ -296,40 +296,25 @@
|
||||
{#if topResult}
|
||||
{#snippet topResultBody()}
|
||||
{#if topResult.kind === 'track'}
|
||||
<Cover
|
||||
trackId={targetId(topResult.target)}
|
||||
size="large"
|
||||
<Artwork
|
||||
trackIds={[targetId(topResult.target)]}
|
||||
size="xl"
|
||||
alt={targetTitle(topResult.target)}
|
||||
class="h-22 w-22 shrink-0 rounded-2xl"
|
||||
class="shrink-0"
|
||||
/>
|
||||
{:else if topResult.kind === 'album'}
|
||||
<PlaylistArt
|
||||
trackIds={topResult.album.coverTrackIds}
|
||||
class="h-22 w-22 shrink-0 rounded-2xl"
|
||||
/>
|
||||
<Artwork trackIds={topResult.album.coverTrackIds} size="xl" class="shrink-0" />
|
||||
{:else if topResult.kind === 'playlist'}
|
||||
<PlaylistArt
|
||||
playlistId={topResult.playlist.id}
|
||||
<Artwork
|
||||
src="/api/playlists/{topResult.playlist.id}/cover?size=large&v={encodeURIComponent(
|
||||
topResult.playlist.updatedAt
|
||||
)}"
|
||||
trackIds={topResult.playlist.coverTrackIds}
|
||||
version={topResult.playlist.updatedAt}
|
||||
class="h-22 w-22 shrink-0 rounded-2xl"
|
||||
size="xl"
|
||||
class="shrink-0"
|
||||
/>
|
||||
{:else}
|
||||
<div
|
||||
class="grid h-22 w-22 shrink-0 place-items-center overflow-hidden rounded-full bg-surface-2"
|
||||
>
|
||||
{#if topResult.user.profilePictureUrl}
|
||||
<img
|
||||
src={topResult.user.profilePictureUrl}
|
||||
alt=""
|
||||
class="h-full w-full object-cover"
|
||||
/>
|
||||
{:else}
|
||||
<span class="text-xl font-semibold text-fg uppercase"
|
||||
>{topResult.user.name.charAt(0)}</span
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
<Avatar name={topResult.user.name} src={topResult.user.profilePictureUrl} size="lg" />
|
||||
{/if}
|
||||
|
||||
<div class="flex h-22 min-w-0 flex-1 flex-col justify-center gap-3">
|
||||
@@ -443,7 +428,6 @@
|
||||
{#each capped(songRows) as { target: item, seconds }, i (targetId(item))}
|
||||
<li>
|
||||
<SearchResultRow
|
||||
kind="track"
|
||||
title={targetTitle(item)}
|
||||
subtitle={targetArtist(item)}
|
||||
meta="{fmtTime(seconds)} · {fmtPlays(targetListensCount(item))}"
|
||||
@@ -452,16 +436,16 @@
|
||||
oncontextmenu={(e) => openContextMenu(e, item)}
|
||||
>
|
||||
{#snippet art(artClass)}
|
||||
<Cover
|
||||
trackId={targetId(item)}
|
||||
size="small"
|
||||
<Artwork
|
||||
trackIds={[targetId(item)]}
|
||||
size="lg"
|
||||
alt={targetTitle(item)}
|
||||
class="{artClass} ring-1 ring-line ring-inset"
|
||||
>
|
||||
{#if isTargetCurrent(item)}
|
||||
<NowPlaying paused={!player.playing} />
|
||||
{/if}
|
||||
</Cover>
|
||||
</Artwork>
|
||||
{/snippet}
|
||||
</SearchResultRow>
|
||||
</li>
|
||||
@@ -495,14 +479,13 @@
|
||||
{#each capped(albums) as album (album.id)}
|
||||
<li>
|
||||
<SearchResultRow
|
||||
kind="album"
|
||||
title={album.title}
|
||||
meta="{Number(album.trackCount)} canciones"
|
||||
href="/albums/{album.id}"
|
||||
oncontextmenu={(e) => openAlbumMenu(e, album.id)}
|
||||
>
|
||||
{#snippet art(artClass)}
|
||||
<PlaylistArt trackIds={album.coverTrackIds} class={artClass} />
|
||||
<Artwork trackIds={album.coverTrackIds} size="lg" class={artClass} />
|
||||
{/snippet}
|
||||
</SearchResultRow>
|
||||
</li>
|
||||
@@ -525,17 +508,9 @@
|
||||
<ul class="flex flex-col gap-1">
|
||||
{#each capped(users) as u (u.id)}
|
||||
<li>
|
||||
<SearchResultRow kind="user" title={u.name} href="/u/{u.id}">
|
||||
<SearchResultRow title={u.name} href="/u/{u.id}">
|
||||
{#snippet art(artClass)}
|
||||
<div class="{artClass} grid place-items-center bg-surface-2">
|
||||
{#if u.profilePictureUrl}
|
||||
<img src={u.profilePictureUrl} alt="" class="h-full w-full object-cover" />
|
||||
{:else}
|
||||
<span class="text-sm font-semibold text-fg uppercase">
|
||||
{u.name.charAt(0)}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
<Avatar name={u.name} src={u.profilePictureUrl} size="md" class={artClass} />
|
||||
{/snippet}
|
||||
</SearchResultRow>
|
||||
</li>
|
||||
@@ -559,16 +534,17 @@
|
||||
{#each capped(playlistMatches) as playlist (playlist.id)}
|
||||
<li>
|
||||
<SearchResultRow
|
||||
kind="playlist"
|
||||
title={playlist.name}
|
||||
subtitle={playlist.description}
|
||||
href="/playlists/{playlist.id}"
|
||||
>
|
||||
{#snippet art(artClass)}
|
||||
<PlaylistArt
|
||||
playlistId={playlist.id}
|
||||
<Artwork
|
||||
src="/api/playlists/{playlist.id}/cover?size=medium&v={encodeURIComponent(
|
||||
playlist.updatedAt
|
||||
)}"
|
||||
trackIds={playlist.coverTrackIds}
|
||||
version={playlist.updatedAt}
|
||||
size="lg"
|
||||
class={artClass}
|
||||
/>
|
||||
{/snippet}
|
||||
|
||||
Reference in New Issue
Block a user