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.7 kB 110 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 type { BobbinContext } from './client'; 8import { items } from './pagination'; 9import { rkeyFromUri } from './uri'; 10import type * as ShTangledGraphFollow from './lexicons/types/sh/tangled/graph/follow'; 11import type * as ShTangledGraphVouch from './lexicons/types/sh/tangled/graph/vouch'; 12import type * as ShTangledFeedStar from './lexicons/types/sh/tangled/feed/star'; 13 14export type FollowRecord = ShTangledGraphFollow.Main; 15export type VouchRecord = ShTangledGraphVouch.Main; 16export type StarRecord = ShTangledFeedStar.Main; 17 18const FOLLOW_COLLECTION = 'sh.tangled.graph.follow' as Nsid; 19const STAR_COLLECTION = 'sh.tangled.feed.star' as Nsid; 20 21// TODO(bobbin): needs a relation point-lookup (e.g. graph.getFollow?actor=&subject=) 22// or a `viewer` hydration param on lists; scanning listFollowsBy pages is O(follows). 23export const findFollowRkey = async ( 24 ctx: BobbinContext, 25 viewerDid: string, 26 subject: string, 27 options: { maxPages?: number } = {} 28): Promise<string | null> => { 29 for await (const item of items( 30 ctx, 31 'sh.tangled.graph.listFollowsBy', 32 { subject: viewerDid as Did }, 33 { maxPages: options.maxPages ?? 10 } 34 )) { 35 if ((item.value as FollowRecord).subject === subject) return rkeyFromUri(item.uri); 36 } 37 return null; 38}; 39 40const createGenericRecord = async <T extends { $type: string }>( 41 agent: OAuthUserAgent, 42 collection: Nsid, 43 record: T 44): Promise<string> => { 45 const rpc = createClient(agent); 46 const result = await ok( 47 rpc.call(createRecordSchema, { 48 input: { repo: agent.sub, collection, record } 49 }) 50 ); 51 return rkeyFromUri(result.uri); 52}; 53 54const deleteGenericRecord = async ( 55 agent: OAuthUserAgent, 56 collection: Nsid, 57 rkey: string 58): Promise<void> => { 59 const rpc = createClient(agent); 60 await ok( 61 rpc.call(deleteRecordSchema, { 62 input: { repo: agent.sub, collection, rkey: rkey as RecordKey } 63 }) 64 ); 65}; 66 67export const createFollow = (agent: OAuthUserAgent, subject: string): Promise<string> => 68 createGenericRecord(agent, FOLLOW_COLLECTION, { 69 $type: 'sh.tangled.graph.follow', 70 subject: subject as Did, 71 createdAt: new Date().toISOString() 72 }); 73 74export const deleteFollow = (agent: OAuthUserAgent, rkey: string): Promise<void> => 75 deleteGenericRecord(agent, FOLLOW_COLLECTION, rkey); 76 77export const createStar = (agent: OAuthUserAgent, repoDid: string): Promise<string> => 78 createGenericRecord(agent, STAR_COLLECTION, { 79 $type: 'sh.tangled.feed.star', 80 subject: { 81 $type: 'sh.tangled.feed.star#repo', 82 did: repoDid as Did 83 }, 84 createdAt: new Date().toISOString() 85 }); 86 87export const deleteStar = (agent: OAuthUserAgent, rkey: string): Promise<void> => 88 deleteGenericRecord(agent, STAR_COLLECTION, rkey); 89 90// TODO(bobbin): same relation-lookup gap as findFollowRkey; walking every star of 91// the viewer to build this map is O(stars) per page load. 92export const listStarRkeys = async ( 93 ctx: BobbinContext, 94 viewerDid: string, 95 options: { maxPages?: number } = {} 96): Promise<Map<string, string>> => { 97 const rkeys = new Map<string, string>(); 98 for await (const item of items( 99 ctx, 100 'sh.tangled.feed.listStarsBy', 101 { subject: viewerDid as Did }, 102 { maxPages: options.maxPages ?? 10 } 103 )) { 104 const value = item.value as StarRecord; 105 if (value.subject.$type === 'sh.tangled.feed.star#repo') { 106 rkeys.set(value.subject.did, rkeyFromUri(item.uri)); 107 } 108 } 109 return rkeys; 110};