"use client"; /** * PostsGrid — client-side body for /u/[handle] (Posts tab). * * Split out from page.tsx so the page itself can be a server component * that exports `generateMetadata` for the public-profile OG card. The * /u/[handle]/layout.tsx still owns ProfileHeader + tab strip + the * outer max-w-3xl container; this grid just renders the post grid * inside that shell. Tapping a thumbnail jumps to * /u/[handle]/posts#post- where the Instagram-style scroll feed * shows the full passages. */ import { use, useEffect, useState } from "react"; import { profile, type Post } from "@/lib/social"; import { PostViews } from "@/components/social/post-views"; import { usePublicProfile } from "./profile-context"; export function PostsGrid({ params, }: { params: Promise<{ handle: string }>; }) { const { handle } = use(params); const data = usePublicProfile(); const [feed, setFeed] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { let cancelled = false; setLoading(true); profile .feed(handle, 30) .then((rows) => { if (!cancelled) setFeed(rows ?? []); }) .catch(() => { // Layout already 404'd if the handle is unknown; if the feed // itself fails we just show an empty grid rather than blocking // the page with an error toast. }) .finally(() => !cancelled && setLoading(false)); return () => { cancelled = true; }; }, [handle]); if (loading) { return

Loading…

; } return ( `/u/${data.handle}/posts#post-${p.id}`} emptyMessage="No posts yet — quotes shared by this user will appear here." /> ); }