Files
spsays-training/app/(app)/u/[handle]/collections/[id]/page.tsx
T
2026-06-14 18:04:11 +05:30

78 lines
2.2 KiB
TypeScript

"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<Collection | null>(null);
const [posts, setPosts] = useState<Post[]>([]);
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 <p className="text-sm text-muted-foreground">Loading…</p>;
if (!c) return null;
return (
<div className="space-y-6">
<Link
href={`/u/${handle}/collections`}
className="inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground"
>
<ArrowLeft className="size-4" /> All collections
</Link>
<CollectionScrollCard
collection={c}
href={`/u/${handle}/collections/${c.id}`}
initialPosts={posts}
/>
</div>
);
}