// One gallery from a group's pool. // GET /xrpc/social.grain.unspecced.getPoolGallery?group=&did=&rkey= // // The same read as getPrivateGallery, pointed at a space the group owns: // the author wrote the gallery, other members wrote the favourites and the // comments on it, and the viewer's credential is what proves they are one of // those people. All of it comes back together because all of it is in the same // space — there is no index to ask separately. // // Photos come back as CIDs for /space-blob; a space's blob has no public // URL, which is the point of putting it there. import { defineQuery, InvalidRequestError } from "$hatk"; import type { GrainActorProfile } from "$hatk"; import { lookupHandles } from "../helpers/lookupHandles.ts"; import { readPool } from "../helpers/pool.ts"; import { poolUri } from "../spaces/client.ts"; import { throwSpaceError } from "../spaces/errors.ts"; export default defineQuery("social.grain.unspecced.getPoolGallery", async (ctx) => { const { ok, db, viewer, params } = ctx; if (!viewer) throw new InvalidRequestError("Authentication required"); const group = params.group as string; const did = params.did as string; const rkey = params.rkey as string; try { const found = (await readPool(ctx, viewer.did, group)).find( (g) => g.did === did && g.rkey === rkey, ); // Everyone the answer names — the author and whoever has said something. // A DID grain has never indexed keeps its row: in a private pool an // unindexed account is an ordinary case, and its handle is not the point. const dids = [...new Set([did, ...(found?.comments ?? []).map((c) => c.did)])]; const [handles, profiles] = await Promise.all([ lookupHandles(db, dids), ctx.lookup("social.grain.actor.profile", "did", dids), ]); const who = (actor: string) => ({ did: actor, ...(handles.get(actor) ? { handle: handles.get(actor) } : {}), ...(profiles.get(actor)?.value.displayName ? { displayName: profiles.get(actor)!.value.displayName } : {}), ...(profiles.get(actor)?.value.avatar ? { avatar: ctx.blobUrl(actor, profiles.get(actor)!.value.avatar, "avatar") } : {}), }); return ok({ space: poolUri(group), did, ...who(did), viewerIsAuthor: viewer.did === did, ...(found ? { gallery: { uri: found.uri, title: found.title, ...(found.description ? { description: found.description } : {}), ...(found.createdAt ? { createdAt: found.createdAt } : {}), }, favCount: found.favCount, ...(found.viewerFav ? { viewerFav: found.viewerFav } : {}), commentCount: found.commentCount, comments: found.comments.map((c) => ({ uri: c.uri, rkey: c.rkey, text: c.text, ...(c.facets ? { facets: c.facets } : {}), ...(c.replyTo ? { replyTo: c.replyTo } : {}), ...(c.createdAt ? { createdAt: c.createdAt } : {}), author: who(c.did), })), } : { favCount: 0, commentCount: 0, comments: [] }), items: (found?.photos ?? []).map((p) => ({ uri: p.uri, did: p.did, cid: p.cid, ...(p.alt ? { alt: p.alt } : {}), ...(p.aspectRatio ? { aspectRatio: p.aspectRatio } : {}), position: p.position, })), }); } catch (err) { return throwSpaceError(err, db, viewer.did); } });