feat(web): discovery modals — artist + album, not preview navigation
Clicking a suggested (or find-similar) artist opens an ArtistModal: Follow + a cover-art grid of their releases with per-album Want (reuses the preview endpoint), plus a "Full page" link to the deep-linkable preview route. Clicking a suggested album opens the AlbumModal (tracklist + Want). Lighter than jumping to a full preview page. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { PageHead } from "../_ui/page-head";
|
||||
import { SectionHeader } from "../_ui/section-header";
|
||||
import { ArtistModal } from "../_ui/artist-modal";
|
||||
import { AlbumModal } from "../_ui/album-modal";
|
||||
|
||||
type Suggestion = {
|
||||
id: string;
|
||||
@@ -29,6 +31,8 @@ export function DiscoverClient() {
|
||||
const [search, setSearch] = useState<SearchResult | null>(null);
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [followedSimilar, setFollowedSimilar] = useState<Set<string>>(new Set());
|
||||
const [artistModal, setArtistModal] = useState<{ mbid: string; name: string } | null>(null);
|
||||
const [albumModal, setAlbumModal] = useState<Suggestion | null>(null);
|
||||
|
||||
const loadFeed = useCallback(async () => {
|
||||
setFeed(await (await fetch("/api/discover")).json());
|
||||
@@ -121,7 +125,9 @@ export function DiscoverClient() {
|
||||
<li key={s.mbid} className="list-row">
|
||||
<div className="main">
|
||||
<div className="rtitle">
|
||||
<a href={`/discover/artist/${s.mbid}?name=${encodeURIComponent(s.name)}`}>{s.name}</a>
|
||||
<button className="linkish" onClick={() => setArtistModal({ mbid: s.mbid, name: s.name })}>
|
||||
{s.name}
|
||||
</button>
|
||||
</div>
|
||||
<div className="rmeta">
|
||||
<span className="score">similarity {s.score.toFixed(1)}</span>
|
||||
@@ -150,7 +156,9 @@ export function DiscoverClient() {
|
||||
<li key={s.id} className="list-row">
|
||||
<div className="main">
|
||||
<div className="rtitle">
|
||||
<a href={`/discover/artist/${s.artistMbid}?name=${encodeURIComponent(s.artistName)}`}>{s.artistName}</a>
|
||||
<button className="linkish" onClick={() => setArtistModal({ mbid: s.artistMbid, name: s.artistName })}>
|
||||
{s.artistName}
|
||||
</button>
|
||||
</div>
|
||||
<div className="rmeta">
|
||||
<span className="score">score {s.score.toFixed(2)}</span>
|
||||
@@ -182,9 +190,9 @@ export function DiscoverClient() {
|
||||
<li key={s.id} className="list-row">
|
||||
<div className="main">
|
||||
<div className="rtitle">
|
||||
<a href={`/discover/artist/${s.artistMbid}?name=${encodeURIComponent(s.artistName)}#rg-${s.rgMbid}`}>
|
||||
<button className="linkish" onClick={() => setAlbumModal(s)}>
|
||||
{s.album}
|
||||
</a>{" "}
|
||||
</button>{" "}
|
||||
<span className="artist">· {s.artistName}</span>
|
||||
</div>
|
||||
<div className="rmeta">
|
||||
@@ -203,6 +211,27 @@ export function DiscoverClient() {
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
{artistModal ? (
|
||||
<ArtistModal open onClose={() => setArtistModal(null)} mbid={artistModal.mbid} initialName={artistModal.name} />
|
||||
) : null}
|
||||
{albumModal ? (
|
||||
<AlbumModal
|
||||
open
|
||||
onClose={() => setAlbumModal(null)}
|
||||
album={{ rgMbid: albumModal.rgMbid, album: albumModal.album ?? "", artist: albumModal.artistName, year: null, type: null }}
|
||||
actions={
|
||||
<button
|
||||
className="btn sm"
|
||||
onClick={() => {
|
||||
act(albumModal.id, "want");
|
||||
setAlbumModal(null);
|
||||
}}
|
||||
>
|
||||
Want
|
||||
</button>
|
||||
}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user