[READ ONLY MIRROR] Spark Social AppView Server github.com/sprksocial/server
atproto deno hono lexicon
1
fork

Configure Feed

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

at main 135 lines 4.0 kB view raw
1import { DataPlane } from "../data-plane/index.ts"; 2import * as so from "../lex/so.ts"; 3import { HydrationMap, parseRecord, RecordInfo } from "./util.ts"; 4 5export type BlockRecord = so.sprk.graph.block.Main; 6export type FollowRecord = so.sprk.graph.follow.Main; 7 8export type Follow = RecordInfo<FollowRecord>; 9export type Follows = HydrationMap<Follow>; 10 11export type FollowInfo = { 12 uri: string; 13 actorDid: string; 14 subjectDid: string; 15}; 16 17export type Block = RecordInfo<BlockRecord>; 18 19export type RelationshipPair = [didA: string, didB: string]; 20 21const dedupePairs = (pairs: RelationshipPair[]): RelationshipPair[] => { 22 const deduped = pairs.reduce((acc, pair) => { 23 return acc.set(Blocks.key(...pair), pair); 24 }, new Map<string, RelationshipPair>()); 25 return [...deduped.values()]; 26}; 27 28export class Blocks { 29 _blocks: Map<string, BlockEntry> = new Map(); // did:a,did:b -> block 30 constructor() {} 31 32 static key(didA: string, didB: string): string { 33 return [didA, didB].sort().join(","); 34 } 35 36 set(didA: string, didB: string, block: BlockEntry): Blocks { 37 const key = Blocks.key(didA, didB); 38 this._blocks.set(key, block); 39 return this; 40 } 41 42 get(didA: string, didB: string): BlockEntry | null { 43 if (didA === didB) return null; // ignore self-blocks 44 const key = Blocks.key(didA, didB); 45 return this._blocks.get(key) ?? null; 46 } 47 48 merge(blocks: Blocks): Blocks { 49 blocks._blocks.forEach((block, key) => { 50 this._blocks.set(key, block); 51 }); 52 return this; 53 } 54} 55 56// No "blocking" vs. "blocked" directionality: only suitable for bidirectional block checks 57export type BlockEntry = { 58 blockUri: string | undefined; 59}; 60 61export class GraphHydrator { 62 constructor(public dataplane: DataPlane) {} 63 64 async getBidirectionalBlocks(pairs: RelationshipPair[]): Promise<Blocks> { 65 if (!pairs.length) return new Blocks(); 66 const deduped = dedupePairs(pairs).map(([a, b]) => ({ a, b })); 67 const res = await this.dataplane.relationships.getBlockExistence(deduped); 68 const blocks = new Blocks(); 69 for (let i = 0; i < deduped.length; i++) { 70 const pair = deduped[i]; 71 const block = res.blocks[i]; 72 blocks.set(pair.a, pair.b, { 73 blockUri: block.blockedBy || block.blocking || undefined, 74 }); 75 } 76 return blocks; 77 } 78 79 async getFollows(uris: string[], includeTakedowns = false): Promise<Follows> { 80 if (!uris.length) return new HydrationMap<Follow>(); 81 const res = await this.dataplane.records.getFollowRecords(uris); 82 return uris.reduce((acc, uri, i) => { 83 const record = parseRecord<FollowRecord>( 84 so.sprk.graph.follow.main, 85 res.records[i], 86 includeTakedowns, 87 ); 88 return acc.set(uri, record ?? null); 89 }, new HydrationMap<Follow>()); 90 } 91 92 async getBlocks( 93 uris: string[], 94 includeTakedowns = false, 95 ): Promise<HydrationMap<Block>> { 96 if (!uris.length) return new HydrationMap<Block>(); 97 const res = await this.dataplane.records.getBlockRecords(uris); 98 return uris.reduce((acc, uri, i) => { 99 const record = parseRecord<BlockRecord>( 100 so.sprk.graph.block.main, 101 res.records[i], 102 includeTakedowns, 103 ); 104 return acc.set(uri, record ?? null); 105 }, new HydrationMap<Block>()); 106 } 107 108 async getActorFollows(input: { 109 did: string; 110 cursor?: string; 111 limit?: number; 112 }): Promise<{ follows: FollowInfo[]; cursor: string | undefined }> { 113 const { did, cursor, limit } = input; 114 const res = await this.dataplane.follows.getFollows( 115 did, 116 limit, 117 cursor, 118 ); 119 return { follows: res.follows, cursor: res.cursor }; 120 } 121 122 async getActorFollowers(input: { 123 did: string; 124 cursor?: string; 125 limit?: number; 126 }): Promise<{ followers: FollowInfo[]; cursor: string | undefined }> { 127 const { did, cursor, limit } = input; 128 const res = await this.dataplane.follows.getFollowers( 129 did, 130 limit, 131 cursor, 132 ); 133 return { followers: res.followers, cursor: res.cursor }; 134 } 135}