Retro Bulletin Board Systems on atproto. Web app and TUI. lazy mirror of alyraffauf/atbbs atbbs.xyz
forums python tui atproto bbs
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

web/activity: fix users being sent to wrong board

+29 -13
+2 -3
web/src/components/dashboard/ActivityList.tsx
··· 11 11 12 12 interface ActivityListProps { 13 13 items: ActivityItem[]; 14 - userHandle: string; 15 14 } 16 15 17 - export default function ActivityList({ items, userHandle }: ActivityListProps) { 16 + export default function ActivityList({ items }: ActivityListProps) { 18 17 const [shown, setShown] = useState(PAGE_SIZE); 19 18 20 19 if (items.length === 0) ··· 25 24 {items.slice(0, shown).map((item) => { 26 25 const { did: threadDid, rkey: threadRkey } = parseAtUri(item.threadUri); 27 26 const { rkey: replyRkey } = parseAtUri(item.replyUri); 28 - const url = `${threadUrl(userHandle, threadDid, threadRkey)}#reply-${replyRkey}`; 27 + const url = `${threadUrl(item.bbsHandle, threadDid, threadRkey)}#reply-${replyRkey}`; 29 28 return ( 30 29 <Link 31 30 key={item.replyUri}
+26 -9
web/src/lib/activity.ts
··· 1 1 /** Activity data — replies to your posts from other users. */ 2 2 3 - import { fetchAndHydrate, listRecords } from "./atproto"; 3 + import { fetchAndHydrate, listRecords, resolveIdentitiesBatch } from "./atproto"; 4 4 import { POST } from "./lexicon"; 5 5 import { isPostRecord } from "./recordGuards"; 6 + import { parseAtUri } from "./util"; 6 7 7 8 export interface ActivityItem { 8 9 type: "reply" | "parent_reply"; 9 10 threadTitle: string; 10 11 threadUri: string; 12 + bbsHandle: string; 11 13 replyUri: string; 12 14 handle: string; 13 15 body: string; ··· 21 23 type: ActivityItem["type"], 22 24 threadTitle: string, 23 25 threadUri: string, 26 + bbsHandle: string, 24 27 ): Promise<ActivityItem[]> { 25 28 try { 26 29 const { records } = await fetchAndHydrate(sourceUri, backlinkSource, { ··· 31 34 type, 32 35 threadTitle, 33 36 threadUri, 37 + bbsHandle, 34 38 replyUri: record.uri, 35 39 handle: record.handle, 36 40 body: ((record.value.body as string) ?? "").substring(0, 200), ··· 52 56 const rootPosts = validPosts.filter((record) => !record.value.root); 53 57 const replyPosts = validPosts.filter((record) => !!record.value.root); 54 58 59 + const bbsDids = new Set( 60 + validPosts.map((record) => parseAtUri(record.value.scope).did), 61 + ); 62 + const bbsIdentities = await resolveIdentitiesBatch([...bbsDids]); 63 + 55 64 const results = await Promise.all([ 56 - ...rootPosts.map((post) => 57 - fetchBacklinkItems( 65 + ...rootPosts.map((post) => { 66 + const bbsDid = parseAtUri(post.value.scope).did; 67 + const bbsHandle = bbsIdentities[bbsDid]?.handle; 68 + if (!bbsHandle) return Promise.resolve([] as ActivityItem[]); 69 + return fetchBacklinkItems( 58 70 post.uri, 59 71 `${POST}:root`, 60 72 did, 61 73 "reply", 62 74 post.value.title ?? "", 63 75 post.uri, 64 - ), 65 - ), 66 - ...replyPosts.map((reply) => 67 - fetchBacklinkItems( 76 + bbsHandle, 77 + ); 78 + }), 79 + ...replyPosts.map((reply) => { 80 + const bbsDid = parseAtUri(reply.value.scope).did; 81 + const bbsHandle = bbsIdentities[bbsDid]?.handle; 82 + if (!bbsHandle) return Promise.resolve([] as ActivityItem[]); 83 + return fetchBacklinkItems( 68 84 reply.uri, 69 85 `${POST}:parent`, 70 86 did, 71 87 "parent_reply", 72 88 "", 73 89 reply.value.root ?? "", 74 - ), 75 - ), 90 + bbsHandle, 91 + ); 92 + }), 76 93 ]); 77 94 78 95 // Deduplicate — prefer "parent-reply" type when the same reply appears as both.
+1 -1
web/src/pages/Dashboard.tsx
··· 120 120 <p className="text-neutral-400 text-xs mb-4"> 121 121 Recent replies from other users. 122 122 </p> 123 - <ActivityList items={activity} userHandle={user.handle} /> 123 + <ActivityList items={activity} /> 124 124 </> 125 125 )} 126 126