diff --git a/web/src/lib/api/graph.ts b/web/src/lib/api/graph.ts index 48116cdbf..e73b71181 100644 --- a/web/src/lib/api/graph.ts +++ b/web/src/lib/api/graph.ts @@ -1,5 +1,6 @@ import { ok } from "@atcute/client"; import { mainSchema as createRecordSchema } from "@atcute/atproto/types/repo/createRecord"; +import { mainSchema as putRecordSchema } from "@atcute/atproto/types/repo/putRecord"; import { mainSchema as deleteRecordSchema } from "@atcute/atproto/types/repo/deleteRecord"; import type { Did, Nsid, RecordKey } from "@atcute/lexicons/syntax"; import type { OAuthUserAgent } from "@atcute/oauth-browser-client"; @@ -8,16 +9,17 @@ import { ClientResponseError, type BobbinContext } from "./client"; import { jsonGet } from "./_request"; import { httpStatusFor } from "./load"; import { rkeyFromUri } from "./uri"; +import type { VouchRecord } from "./records"; import type * as ShTangledFeedGetStar from "./lexicons/types/sh/tangled/feed/getStar"; import type * as ShTangledGraphFollow from "./lexicons/types/sh/tangled/graph/follow"; import type * as ShTangledGraphGetFollow from "./lexicons/types/sh/tangled/graph/getFollow"; -import type * as ShTangledGraphVouch from "./lexicons/types/sh/tangled/graph/vouch"; export type FollowRecord = ShTangledGraphFollow.Main; -export type VouchRecord = ShTangledGraphVouch.Main; +export type { VouchRecord }; const FOLLOW_COLLECTION = "sh.tangled.graph.follow" as Nsid; const STAR_COLLECTION = "sh.tangled.feed.star" as Nsid; +const VOUCH_COLLECTION = "sh.tangled.graph.vouch" as Nsid; // 404 means no follow exists, other errors propagate export const getFollowRkey = async ( @@ -70,6 +72,21 @@ const createGenericRecord = async ( return rkeyFromUri(result.uri); }; +const putGenericRecord = async ( + agent: OAuthUserAgent, + collection: Nsid, + rkey: string, + record: T +): Promise => { + const rpc = createClient(agent); + const result = await ok( + rpc.call(putRecordSchema, { + input: { repo: agent.sub, collection, rkey: rkey as RecordKey, record } + }) + ); + return rkeyFromUri(result.uri); +}; + const deleteGenericRecord = async ( agent: OAuthUserAgent, collection: Nsid, @@ -105,3 +122,27 @@ export const createStar = (agent: OAuthUserAgent, repoDid: string): Promise => deleteGenericRecord(agent, STAR_COLLECTION, rkey); + +export type VouchKind = "vouch" | "denounce"; + +export interface VouchInput { + kind: VouchKind; + reason?: string; + evidences?: string[]; +} + +export const putVouch = ( + agent: OAuthUserAgent, + subject: string, + input: VouchInput +): Promise => + putGenericRecord(agent, VOUCH_COLLECTION, subject, { + $type: "sh.tangled.graph.vouch", + kind: input.kind, + ...(input.reason ? { reason: input.reason } : {}), + ...(input.evidences?.length ? { evidences: input.evidences } : {}), + createdAt: new Date().toISOString() + }); + +export const deleteVouch = (agent: OAuthUserAgent, subject: string): Promise => + deleteGenericRecord(agent, VOUCH_COLLECTION, subject); diff --git a/web/src/lib/api/records.ts b/web/src/lib/api/records.ts index bfa00af74..2de73fd8a 100644 --- a/web/src/lib/api/records.ts +++ b/web/src/lib/api/records.ts @@ -3,6 +3,7 @@ import { jsonGet } from "./_request"; import type * as ShTangledActorProfile from "./lexicons/types/sh/tangled/actor/profile"; import type * as ShTangledPullGetPullView from "./lexicons/types/sh/tangled/pull/getPullView"; import type * as ShTangledFeedStar from "./lexicons/types/sh/tangled/feed/star"; +import type * as ShTangledGraphVouch from "./lexicons/types/sh/tangled/graph/vouch"; import type * as ShTangledRepo from "./lexicons/types/sh/tangled/repo"; import type * as ShTangledRepoPull from "./lexicons/types/sh/tangled/repo/pull"; import type * as ShTangledRepoPullStatus from "./lexicons/types/sh/tangled/repo/pull/status"; @@ -24,6 +25,7 @@ export type PullRecord = ShTangledRepoPull.Main; export type PullStatusRecord = ShTangledRepoPullStatus.Main; export type StringRecord = ShTangledString.Main; export type StarRecord = ShTangledFeedStar.Main; +export type VouchRecord = ShTangledGraphVouch.Main; export const BULK_LIMIT = 50;