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

Configure Feed

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

add getBlocks

+90
+2
api/index.ts
··· 11 11 import getAudioPosts from "./so/sprk/sound/getAudioPosts.ts"; 12 12 import getFollows from "./so/sprk/graph/getFollows.ts"; 13 13 import getFollowers from "./so/sprk/graph/getFollowers.ts"; 14 + import getBlocks from "./so/sprk/graph/getBlocks.ts"; 14 15 import putPreferences from "./so/sprk/actor/putPreferences.ts"; 15 16 import getPreferences from "./so/sprk/actor/getPreferences.ts"; 16 17 import searchActors from "./so/sprk/actor/searchActors.ts"; ··· 42 43 getAudioPosts(server, ctx); 43 44 getFollows(server, ctx); 44 45 getFollowers(server, ctx); 46 + getBlocks(server, ctx); 45 47 putPreferences(server, ctx); 46 48 getPreferences(server, ctx); 47 49 searchActors(server, ctx);
+88
api/so/sprk/graph/getBlocks.ts
··· 1 + import { mapDefined } from "@atp/common"; 2 + import { AppContext } from "../../../../context.ts"; 3 + import { HydrateCtx, Hydrator } from "../../../../hydration/index.ts"; 4 + import { Server } from "../../../../lex/index.ts"; 5 + import { QueryParams } from "../../../../lex/types/so/sprk/graph/getBlocks.ts"; 6 + import { 7 + createPipeline, 8 + HydrationFnInput, 9 + noRules, 10 + PresentationFnInput, 11 + SkeletonFnInput, 12 + } from "../../../../pipeline.ts"; 13 + import { Views } from "../../../../views/index.ts"; 14 + import { clearlyBadCursor, resHeaders } from "../../../util.ts"; 15 + 16 + export default function (server: Server, ctx: AppContext) { 17 + const getBlocks = createPipeline(skeleton, hydration, noRules, presentation); 18 + server.so.sprk.graph.getBlocks({ 19 + auth: ctx.authVerifier.standard, 20 + handler: async ({ params, auth, req }) => { 21 + const viewer = auth.credentials.iss; 22 + const labelers = ctx.reqLabelers(req); 23 + const hydrateCtx = await ctx.hydrator.createContext({ labelers, viewer }); 24 + const result = await getBlocks( 25 + { ...params, hydrateCtx: hydrateCtx.copy({ viewer }) }, 26 + ctx, 27 + ); 28 + return { 29 + encoding: "application/json", 30 + body: result, 31 + headers: resHeaders({ labelers: hydrateCtx.labelers }), 32 + }; 33 + }, 34 + }); 35 + } 36 + 37 + const skeleton = async (input: SkeletonFnInput<Context, Params>) => { 38 + const { params, ctx } = input; 39 + if (clearlyBadCursor(params.cursor)) { 40 + return { blockedDids: [] }; 41 + } 42 + const { blockUris, cursor } = await ctx.hydrator.dataplane.blocks.getBlocks( 43 + params.hydrateCtx.viewer, 44 + params.limit, 45 + params.cursor, 46 + ); 47 + const blocks = await ctx.hydrator.graph.getBlocks(blockUris); 48 + const blockedDids = mapDefined( 49 + blockUris, 50 + (uri) => blocks.get(uri)?.record.subject, 51 + ); 52 + return { 53 + blockedDids, 54 + cursor: cursor || undefined, 55 + }; 56 + }; 57 + 58 + const hydration = ( 59 + input: HydrationFnInput<Context, Params, SkeletonState>, 60 + ) => { 61 + const { ctx, params, skeleton } = input; 62 + return ctx.hydrator.hydrateProfiles(skeleton.blockedDids, params.hydrateCtx); 63 + }; 64 + 65 + const presentation = ( 66 + input: PresentationFnInput<Context, Params, SkeletonState>, 67 + ) => { 68 + const { ctx, hydration, skeleton } = input; 69 + const { blockedDids, cursor } = skeleton; 70 + const blocks = mapDefined(blockedDids, (did) => { 71 + return ctx.views.profile(did, hydration); 72 + }); 73 + return { blocks, cursor }; 74 + }; 75 + 76 + type Context = { 77 + hydrator: Hydrator; 78 + views: Views; 79 + }; 80 + 81 + type Params = QueryParams & { 82 + hydrateCtx: HydrateCtx & { viewer: string }; 83 + }; 84 + 85 + type SkeletonState = { 86 + blockedDids: string[]; 87 + cursor?: string; 88 + };