diff --git a/app/(app)/u/[handle]/collections/[id]/page.tsx b/app/(app)/u/[handle]/collections/[id]/page.tsx new file mode 100644 index 00000000..de8f4ad3 --- /dev/null +++ b/app/(app)/u/[handle]/collections/[id]/page.tsx @@ -0,0 +1,77 @@ +"use client"; + +/** + * /u/[handle]/collections/[id] — single public-collection view for another + * user's profile. Mirrors the auth user's /u/me/collections/[id] exactly — + * the CollectionScrollCard carousel (cover + posts) — but read-only: no Edit + * affordance, since the viewer doesn't own it. Private collections (or a bad + * id) 403/404 from the BFF, which we treat as "not found". + */ + +import { use, useEffect, useState } from "react"; +import Link from "next/link"; +import { notFound } from "next/navigation"; +import { ArrowLeft } from "lucide-react"; +import { + collections, + SocialError, + type Collection, + type Post, +} from "@/lib/social"; +import { CollectionScrollCard } from "@/components/social/collection/collection-scroll-card"; + +export default function PublicCollectionDetailPage(props: { + params: Promise<{ handle: string; id: string }>; +}) { + const { handle, id } = use(props.params); + + const [c, setC] = useState(null); + const [posts, setPosts] = useState([]); + const [loading, setLoading] = useState(true); + const [missing, setMissing] = useState(false); + + useEffect(() => { + let cancelled = false; + collections + .get(id) + .then(({ collection, posts }) => { + if (cancelled) return; + setC(collection); + setPosts(posts ?? []); + }) + .catch((err) => { + if (cancelled) return; + if ( + err instanceof SocialError && + (err.status === 404 || err.status === 403) + ) { + setMissing(true); + } + }) + .finally(() => !cancelled && setLoading(false)); + return () => { + cancelled = true; + }; + }, [id]); + + if (missing) notFound(); + if (loading) return

Loading…

; + if (!c) return null; + + return ( +
+ + All collections + + + +
+ ); +} diff --git a/app/(app)/u/[handle]/collections/page.tsx b/app/(app)/u/[handle]/collections/page.tsx index e6a1c34d..3cfa3482 100644 --- a/app/(app)/u/[handle]/collections/page.tsx +++ b/app/(app)/u/[handle]/collections/page.tsx @@ -6,6 +6,9 @@ * The list comes from the layout's profile fetch (`public_collections`), * so this page only has to decide between the empty state and the grid. * Layout owns the ProfileHeader + tabs + container. + * + * Cards link into /u/[handle]/collections/[id] — the same grid → carousel + * detail structure the auth user gets at /u/me/collections. */ import { CollectionViews } from "@/components/social/collection/collection-views"; @@ -23,5 +26,10 @@ export default function PublicProfileCollectionsPage() { ); } - return `/c/${c.id}`} />; + return ( + `/u/${data.handle}/collections/${c.id}`} + /> + ); }