This repository has no description
0

Configure Feed

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

web/routes: load reactions on visiting issues page

Signed-off-by: oppiliappan <me@oppi.li>

author
oppiliappan
date (Jul 30, 2026, 6:02 PM +0100) commit 26c97874 parent 38df99ca change-id nxypvnkx
+53 -5
+7
web/src/routes/[handle]/[repo]/issues/[aturi]/+page.svelte
··· 5 5 import IssueLabelPanel from "$lib/components/repo/issues/IssueLabelPanel.svelte"; 6 6 import CommentList from "$lib/components/comment/CommentList.svelte"; 7 7 import CommentBox from "$lib/components/comment/CommentBox.svelte"; 8 + import Reactions from "$lib/components/reaction/Reactions.svelte"; 8 9 import SignupPrompt from "$lib/components/ui/SignupPrompt.svelte"; 9 10 import TabPanel from "$lib/components/ui/TabPanel.svelte"; 10 11 import IssueForm from "$lib/components/repo/issues/IssueForm.svelte"; ··· 143 144 actions={isAuthor ? issueActions : undefined} 144 145 /> 145 146 <IssueBody body={issue.body} bodyHtml={issue.bodyHtml} /> 147 + <Reactions 148 + reactions={issue.reactions} 149 + subjectUri={issue.uri} 150 + alwaysShow 151 + class="mt-2" 152 + /> 146 153 {#if deleteError} 147 154 <div class="mt-3"> 148 155 <ErrorAlert label={deleteError} />
+46 -5
web/src/routes/[handle]/[repo]/issues/[aturi]/+page.ts
··· 1 1 import { error } from "@sveltejs/kit"; 2 2 import { createBobbinClient } from "$lib/api/client"; 3 3 import { IdentityCache } from "$lib/api/identity"; 4 - import { getIssue, listComments, listIssueStates } from "$lib/api/records"; 4 + import { 5 + getIssue, 6 + listComments, 7 + listIssueStates, 8 + listReactions, 9 + type RecordView, 10 + type ReactionRecord 11 + } from "$lib/api/records"; 5 12 import { didFromUri, rkeyFromUri } from "$lib/api/uri"; 6 13 import { renderMarkup } from "$lib/markup"; 7 14 import { buildCommentThreads, type ThreadInput } from "$lib/components/comment/comments"; 15 + import { buildReactions, type ReactionGroup } from "$lib/components/reaction/reactions"; 8 16 import type { PageLoad } from "./$types"; 9 17 10 18 export const load: PageLoad = async (event) => { ··· 34 42 35 43 const latest = states?.items[0]?.value.state; 36 44 const state = latest?.endsWith(".closed") ? ("closed" as const) : ("open" as const); 37 - 38 45 const body = record.value.body ?? ""; 39 46 const bodyHtml = body ? await renderMarkup(body, markupOpts) : null; 47 + const viewerDid = parent.auth?.did; 48 + const commentItems = comments?.items ?? []; 40 49 41 - const commentItems = comments?.items ?? []; 50 + // reactions: one listReactions per subject (the issue itself + each comment). 51 + // bobbin has no batch-by-subject query and enrich can't break counts down by 52 + // emoji, so we fan out and group client-side. empty subjects come back empty. 53 + const subjects = [record.uri, ...commentItems.map((item) => item.uri)]; 54 + const reactionPages = await Promise.all( 55 + subjects.map((subject) => 56 + listReactions(ctx, subject, { order: "asc", limit: 100 }).catch(() => null) 57 + ) 58 + ); 59 + const reactionsBySubject = new Map<string, RecordView<ReactionRecord>[]>(); 60 + subjects.forEach((subject, i) => reactionsBySubject.set(subject, reactionPages[i]?.items ?? [])); 61 + 62 + // resolve every reactor's handle once for the whole page (IdentityCache dedups) 63 + const reactorDids = new Set<string>(); 64 + for (const items of reactionsBySubject.values()) { 65 + for (const item of items) reactorDids.add(didFromUri(item.uri)); 66 + } 67 + const reactorHandles = new Map<string, string>(); 68 + await Promise.all( 69 + [...reactorDids].map(async (did) => { 70 + const doc = await identities.resolve(did).catch(() => null); 71 + reactorHandles.set(did, doc?.handle ?? did); 72 + }) 73 + ); 74 + const reactionsFor = (subject: string): ReactionGroup[] => 75 + buildReactions( 76 + reactionsBySubject.get(subject) ?? [], 77 + viewerDid, 78 + (did) => reactorHandles.get(did) ?? did 79 + ); 80 + 42 81 const threadInputs: ThreadInput[] = await Promise.all( 43 82 commentItems.map(async (item): Promise<ThreadInput> => { 44 83 const commentDid = didFromUri(item.uri); ··· 56 95 authorHandle: doc?.handle ?? commentDid, 57 96 createdAt: item.value.createdAt, 58 97 body: commentBody, 59 - bodyHtml: commentBodyHtml 98 + bodyHtml: commentBodyHtml, 99 + reactions: reactionsFor(item.uri) 60 100 }, 61 101 replyTo: item.value.replyTo?.uri ?? null, 62 102 replyToCid: item.value.replyTo?.cid ··· 75 115 state, 76 116 authorDid, 77 117 authorHandle: author?.handle ?? authorDid, 78 - createdAt: record.value.createdAt 118 + createdAt: record.value.createdAt, 119 + reactions: reactionsFor(record.uri) 79 120 }, 80 121 comments: buildCommentThreads(threadInputs), 81 122 markup: markupOpts