diff --git a/web/src/components/nav/ThreadLink.tsx b/web/src/components/nav/ThreadLink.tsx index c201671..5e562d4 100644 --- a/web/src/components/nav/ThreadLink.tsx +++ b/web/src/components/nav/ThreadLink.tsx @@ -1,28 +1,101 @@ import { Link } from "react-router-dom"; +import Avatar from "../Avatar"; +import type { Participant } from "../../router/loaders/board"; + +const COL_POSTERS = "w-20"; +const COL_REPLIES = "w-14"; +const COL_ACTIVITY = "w-16"; + +const MAX_AVATARS = 3; + +export function ThreadListHeader() { + return ( +
+ {preview} +
+ )} +{preview}
); } diff --git a/web/src/lib/atproto.ts b/web/src/lib/atproto.ts index d4673f5..766928c 100644 --- a/web/src/lib/atproto.ts +++ b/web/src/lib/atproto.ts @@ -45,6 +45,7 @@ async function fetchJsonNo threads yet.
)} diff --git a/web/src/router/loaders/board.ts b/web/src/router/loaders/board.ts index a92b532..cc61d42 100644 --- a/web/src/router/loaders/board.ts +++ b/web/src/router/loaders/board.ts @@ -1,6 +1,8 @@ import type { LoaderFunctionArgs } from "react-router-dom"; import { resolveBBS, type BBS } from "../../lib/bbs"; import { + getAvatars, + getBacklinkCountsBatch, getBacklinks, getRecordsBatch, getRecordsByUri, @@ -12,6 +14,12 @@ import { is } from "@atcute/lexicons/validations"; import { mainSchema as postSchema } from "../../lexicons/types/xyz/atbbs/post"; import type { XyzAtbbsPost } from "../../lexicons"; +export interface Participant { + did: string; + handle: string; + avatar?: string; +} + export interface ThreadItem { uri: string; did: string; @@ -21,6 +29,8 @@ export interface ThreadItem { body: string; createdAt: string; lastActivityAt: string; + replyCount: number; + participants: Participant[]; } const MAX_SCANS = 4; @@ -41,12 +51,14 @@ export async function hydrateThreadPage( ): Promise<{ threads: ThreadItem[]; cursor: string | null }> { const boardUri = makeAtUri(bbs.identity.did, BOARD, slug); - // Phase 1: Scan board activity to find unique thread URIs. - // Keys are thread URIs, values are the timestamp of their last activity. + // Phase 1: Scan board activity to find unique thread URIs and their posters. + // Constellation returns newest-first, so first-seen activity per thread = most + // recent, and Set insertion order preserves newest-first poster order. const lastActivity = new Map