This repository has no description
0

Configure Feed

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

core / web / src / lib / api / graph.ts
3.6 kB 107 lines
1import { ok } from "@atcute/client"; 2import { mainSchema as createRecordSchema } from "@atcute/atproto/types/repo/createRecord"; 3import { mainSchema as deleteRecordSchema } from "@atcute/atproto/types/repo/deleteRecord"; 4import type { Did, Nsid, RecordKey } from "@atcute/lexicons/syntax"; 5import type { OAuthUserAgent } from "@atcute/oauth-browser-client"; 6import { createClient } from "$lib/auth/agent"; 7import { ClientResponseError, type BobbinContext } from "./client"; 8import { jsonGet } from "./_request"; 9import { httpStatusFor } from "./load"; 10import { rkeyFromUri } from "./uri"; 11import type * as ShTangledFeedGetStar from "./lexicons/types/sh/tangled/feed/getStar"; 12import type * as ShTangledGraphFollow from "./lexicons/types/sh/tangled/graph/follow"; 13import type * as ShTangledGraphGetFollow from "./lexicons/types/sh/tangled/graph/getFollow"; 14import type * as ShTangledGraphVouch from "./lexicons/types/sh/tangled/graph/vouch"; 15 16export type FollowRecord = ShTangledGraphFollow.Main; 17export type VouchRecord = ShTangledGraphVouch.Main; 18 19const FOLLOW_COLLECTION = "sh.tangled.graph.follow" as Nsid; 20const STAR_COLLECTION = "sh.tangled.feed.star" as Nsid; 21 22// 404 means no follow exists, other errors propagate 23export const getFollowRkey = async ( 24 ctx: BobbinContext, 25 actor: string, 26 subject: string 27): Promise<string | null> => { 28 try { 29 const { uri } = await jsonGet<ShTangledGraphGetFollow.$output>( 30 ctx, 31 "sh.tangled.graph.getFollow", 32 { actor: actor as Did, subject: subject as Did } satisfies ShTangledGraphGetFollow.$params 33 ); 34 return rkeyFromUri(uri); 35 } catch (cause) { 36 if (cause instanceof ClientResponseError && httpStatusFor(cause) === 404) return null; 37 throw cause; 38 } 39}; 40 41// 404 means no star exists, other errors propagate 42export const getStarRkey = async ( 43 ctx: BobbinContext, 44 actor: string, 45 subject: string 46): Promise<string | null> => { 47 try { 48 const { uri } = await jsonGet<ShTangledFeedGetStar.$output>(ctx, "sh.tangled.feed.getStar", { 49 actor: actor as Did, 50 subject 51 } satisfies ShTangledFeedGetStar.$params); 52 return rkeyFromUri(uri); 53 } catch (cause) { 54 if (cause instanceof ClientResponseError && httpStatusFor(cause) === 404) return null; 55 throw cause; 56 } 57}; 58 59const createGenericRecord = async <T extends { $type: string }>( 60 agent: OAuthUserAgent, 61 collection: Nsid, 62 record: T 63): Promise<string> => { 64 const rpc = createClient(agent); 65 const result = await ok( 66 rpc.call(createRecordSchema, { 67 input: { repo: agent.sub, collection, record } 68 }) 69 ); 70 return rkeyFromUri(result.uri); 71}; 72 73const deleteGenericRecord = async ( 74 agent: OAuthUserAgent, 75 collection: Nsid, 76 rkey: string 77): Promise<void> => { 78 const rpc = createClient(agent); 79 await ok( 80 rpc.call(deleteRecordSchema, { 81 input: { repo: agent.sub, collection, rkey: rkey as RecordKey } 82 }) 83 ); 84}; 85 86export const createFollow = (agent: OAuthUserAgent, subject: string): Promise<string> => 87 createGenericRecord(agent, FOLLOW_COLLECTION, { 88 $type: "sh.tangled.graph.follow", 89 subject: subject as Did, 90 createdAt: new Date().toISOString() 91 }); 92 93export const deleteFollow = (agent: OAuthUserAgent, rkey: string): Promise<void> => 94 deleteGenericRecord(agent, FOLLOW_COLLECTION, rkey); 95 96export const createStar = (agent: OAuthUserAgent, repoDid: string): Promise<string> => 97 createGenericRecord(agent, STAR_COLLECTION, { 98 $type: "sh.tangled.feed.star", 99 subject: { 100 $type: "sh.tangled.feed.star#repo", 101 did: repoDid as Did 102 }, 103 createdAt: new Date().toISOString() 104 }); 105 106export const deleteStar = (agent: OAuthUserAgent, rkey: string): Promise<void> => 107 deleteGenericRecord(agent, STAR_COLLECTION, rkey);