~ knotless Search/
↑↓ move↵ open⇧↵ all results

grain.social/grain

photography-focused photo sharing platform

grain.social
1 collaborator
0
2
52 lines · 1.7 KB Raw History
// Comment on a gallery in a group's pool.
//   POST /xrpc/social.grain.unspecced.createPoolComment
//
// The comment is the commenter's own record, in their own repo, inside the
// group's space — beside the gallery it answers rather than in the public
// repo, where it would name a private gallery to everyone. Which is also why
// the thread is assembled by reading every member's repo: your reply to my
// gallery was never mine to hold.

import { defineProcedure, InvalidRequestError } from "$hatk";
import { tid } from "../helpers/tid.ts";
import { poolUri } from "../spaces/client.ts";
import { throwSpaceError } from "../spaces/errors.ts";

const COLLECTION = "social.grain.comment";

export default defineProcedure("social.grain.unspecced.createPoolComment", async (ctx) => {
  const { ok, db, viewer, pds, input } = ctx;
  if (!viewer) throw new InvalidRequestError("Authentication required");

  const { group, gallery, text, facets, replyTo } = input;
  const trimmed = String(text ?? "").trim();
  if (!trimmed) throw new InvalidRequestError("A comment needs something in it");

  const space = poolUri(group);
  const rkey = tid();

  try {
    await pds("com.atproto.space.putRecord", {
      method: "POST",
      body: {
        space,
        repo: viewer.did,
        collection: COLLECTION,
        rkey,
        validate: false,
        record: {
          $type: COLLECTION,
          text: trimmed,
          subject: gallery,
          ...(facets ? { facets } : {}),
          ...(replyTo ? { replyTo } : {}),
          createdAt: new Date().toISOString(),
        },
      },
    });
  } catch (err) {
    return throwSpaceError(err, db, viewer.did);
  }

  return ok({ uri: `at://${viewer.did}/${COLLECTION}/${rkey}`, rkey });
});