v3: added an "in collections" section to dump pages, listing the collections a dump belongs to
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -11,14 +11,17 @@ import { API_URL, VALIDATION } from "../config/api.ts";
|
||||
import type {
|
||||
Comment,
|
||||
Dump,
|
||||
Playlist,
|
||||
PublicUser,
|
||||
RawComment,
|
||||
RawDump,
|
||||
RawPlaylist,
|
||||
UpdateDumpRequest,
|
||||
} from "../model.ts";
|
||||
import {
|
||||
deserializeComment,
|
||||
deserializeDump,
|
||||
deserializePlaylist,
|
||||
deserializePublicUser,
|
||||
parseAPIResponse,
|
||||
} from "../model.ts";
|
||||
@@ -33,6 +36,7 @@ import RichContentCard from "../components/RichContentCard.tsx";
|
||||
import FilePreview from "../components/FilePreview.tsx";
|
||||
import { VoteButton } from "../components/VoteButton.tsx";
|
||||
import { DumpCard } from "../components/DumpCard.tsx";
|
||||
import { PlaylistCard } from "../components/PlaylistCard.tsx";
|
||||
import { PageShell } from "../components/PageShell.tsx";
|
||||
import { PageError } from "../components/PageError.tsx";
|
||||
import { Markdown } from "../components/Markdown.tsx";
|
||||
@@ -67,6 +71,7 @@ export function Dump() {
|
||||
|
||||
const [comments, setComments] = useState<Comment[]>([]);
|
||||
const [relatedDumps, setRelatedDumps] = useState<Dump[]>([]);
|
||||
const [collections, setCollections] = useState<Playlist[]>([]);
|
||||
|
||||
const [titleEditing, setTitleEditing] = useState(false);
|
||||
const [titleDraft, setTitleDraft] = useState("");
|
||||
@@ -82,6 +87,7 @@ export function Dump() {
|
||||
removeVote,
|
||||
lastDumpEvent,
|
||||
lastCommentEvent,
|
||||
lastPlaylistEvent,
|
||||
connectionEpoch,
|
||||
} = useWS();
|
||||
|
||||
@@ -180,6 +186,28 @@ export function Dump() {
|
||||
return () => controller.abort();
|
||||
}, [selectedDump, token]);
|
||||
|
||||
// Fetch the collections this dump appears in (public ones, plus the
|
||||
// viewer's own private ones — the server does the filtering)
|
||||
useEffect(() => {
|
||||
if (!selectedDump) return;
|
||||
const controller = new AbortController();
|
||||
fetch(`${API_URL}/api/dumps/${selectedDump}/playlists`, {
|
||||
signal: controller.signal,
|
||||
headers: token ? { Authorization: `Bearer ${token}` } : {},
|
||||
})
|
||||
.then((r) => r.json())
|
||||
.then((body) => {
|
||||
if (body.success) {
|
||||
setCollections((body.data as RawPlaylist[]).map(deserializePlaylist));
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
return () => controller.abort();
|
||||
// lastPlaylistEvent: adding/removing this dump, flipping a collection's
|
||||
// visibility or deleting it all change the list — playlist events are rare
|
||||
// enough to just refetch on any of them.
|
||||
}, [selectedDump, token, lastPlaylistEvent]);
|
||||
|
||||
// Scroll to and highlight a comment when navigating to #comment-{id}
|
||||
useEffect(() => {
|
||||
if (!location.hash.startsWith("#comment-")) return;
|
||||
@@ -517,6 +545,20 @@ export function Dump() {
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* In collections (playlists containing this dump) */}
|
||||
{collections.length > 0 && (
|
||||
<section className="related-section collections-section">
|
||||
<h2 className="related-section-title">
|
||||
<Trans>In collections</Trans>
|
||||
</h2>
|
||||
<ul className="dump-feed">
|
||||
{collections.map((collection) => (
|
||||
<PlaylistCard key={collection.id} playlist={collection} />
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* Comments */}
|
||||
<CommentThread
|
||||
dumpId={dump.id}
|
||||
|
||||
Reference in New Issue
Block a user