grain.social is a photo sharing platform built on atproto. grain.social
atproto photography appview
57
fork

Configure Feed

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

fix: show handle instead of DID for notifications without displayName

Look up handles from _repos for all notification authors so users
without a grain profile or displayName show their handle instead
of a raw DID string.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

+12 -2
+12 -2
server/xrpc/getNotifications.ts
··· 155 155 const dids = [...new Set(items.map((r) => r.did))]; 156 156 const profiles = await lookup<GrainActorProfile>("social.grain.actor.profile", "did", dids); 157 157 158 + // Look up handles from _repos for all notification authors 159 + const handleMap = new Map<string, string>(); 160 + if (dids.length > 0) { 161 + const handleRows = (await db.query( 162 + `SELECT did, handle FROM _repos WHERE did IN (${dids.map((_, i) => `$${i + 1}`).join(",")})`, 163 + dids, 164 + )) as { did: string; handle: string }[]; 165 + for (const row of handleRows) handleMap.set(row.did, row.handle); 166 + } 167 + 158 168 // Hydrate galleries for thumbnails 159 169 const galleryUris = [...new Set(items.map((r) => r.gallery_uri).filter(Boolean))] as string[]; 160 170 const galleries = ··· 216 226 ? views.grainActorDefsProfileView({ 217 227 cid: author.cid, 218 228 did: author.did, 219 - handle: author.handle ?? author.did, 229 + handle: author.handle ?? handleMap.get(author.did) ?? author.did, 220 230 displayName: author.value.displayName, 221 231 avatar: blobUrl(author.did, author.value.avatar) ?? undefined, 222 232 }) 223 233 : views.grainActorDefsProfileView({ 224 234 cid: row.uri, 225 235 did: row.did, 226 - handle: row.did, 236 + handle: handleMap.get(row.did) ?? row.did, 227 237 }), 228 238 ...(gallery ? { galleryUri: row.gallery_uri!, galleryTitle: gallery.value.title } : {}), 229 239 ...(thumb ? { galleryThumb: thumb } : {}),