[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.

feat(feed): getActorReposts

+131 -1
+127
api/so/sprk/feed/getActorReposts.ts
··· 1 + import { mapDefined } from "@atp/common"; 2 + import { InvalidRequestError } from "@atp/xrpc-server"; 3 + import { AppContext } from "../../../../context.ts"; 4 + import { DataPlane } from "../../../../data-plane/index.ts"; 5 + import { FeedItem } from "../../../../hydration/feed.ts"; 6 + import { 7 + HydrateCtx, 8 + HydrationState, 9 + Hydrator, 10 + } from "../../../../hydration/index.ts"; 11 + import { parseString } from "../../../../hydration/util.ts"; 12 + import { Server } from "../../../../lex/index.ts"; 13 + import { QueryParams } from "../../../../lex/types/so/sprk/feed/getActorLikes.ts"; 14 + import { createPipeline } from "../../../../pipeline.ts"; 15 + import { uriToDid as creatorFromUri } from "../../../../utils/uris.ts"; 16 + import { Views } from "../../../../views/index.ts"; 17 + import { clearlyBadCursor, resHeaders } from "../../../util.ts"; 18 + 19 + export default function (server: Server, ctx: AppContext) { 20 + const getActorReposts = createPipeline( 21 + skeleton, 22 + hydration, 23 + noPostBlocks, 24 + presentation, 25 + ); 26 + server.so.sprk.feed.getActorReposts({ 27 + auth: ctx.authVerifier.standardOptional, 28 + handler: async ({ params, auth, req }) => { 29 + const viewer = auth.credentials.iss; 30 + const labelers = ctx.reqLabelers(req); 31 + const hydrateCtx = await ctx.hydrator.createContext({ labelers, viewer }); 32 + 33 + const result = await getActorReposts({ ...params, hydrateCtx }, ctx); 34 + 35 + const repoRev = await ctx.hydrator.actor.getRepoRevSafe(viewer); 36 + 37 + return { 38 + encoding: "application/json", 39 + body: result, 40 + headers: resHeaders({ 41 + repoRev, 42 + labelers: hydrateCtx.labelers, 43 + }), 44 + }; 45 + }, 46 + }); 47 + } 48 + 49 + const skeleton = async (inputs: { 50 + ctx: Context; 51 + params: Params; 52 + }): Promise<Skeleton> => { 53 + const { ctx, params } = inputs; 54 + const { actor, limit, cursor } = params; 55 + const viewer = params.hydrateCtx.viewer; 56 + if (clearlyBadCursor(cursor)) { 57 + return { items: [] }; 58 + } 59 + const [actorDid] = await ctx.hydrator.actor.getDids([actor]); 60 + if (!actorDid || !viewer || viewer !== actorDid) { 61 + throw new InvalidRequestError("Profile not found"); 62 + } 63 + 64 + const repostsRes = await ctx.dataplane.reposts.getActor( 65 + actorDid, 66 + limit, 67 + cursor, 68 + ); 69 + 70 + const items = repostsRes.reposts.map((r) => ({ post: { uri: r.uri } })); 71 + 72 + return { 73 + items, 74 + cursor: parseString(repostsRes.cursor), 75 + }; 76 + }; 77 + 78 + const hydration = async (inputs: { 79 + ctx: Context; 80 + params: Params; 81 + skeleton: Skeleton; 82 + }) => { 83 + const { ctx, params, skeleton } = inputs; 84 + return await ctx.hydrator.hydrateFeedItems(skeleton.items, params.hydrateCtx); 85 + }; 86 + 87 + const noPostBlocks = (inputs: { 88 + ctx: Context; 89 + skeleton: Skeleton; 90 + hydration: HydrationState; 91 + }) => { 92 + const { ctx, skeleton, hydration } = inputs; 93 + skeleton.items = skeleton.items.filter((item) => { 94 + const creator = creatorFromUri(item.post.uri); 95 + return !ctx.views.viewerBlockExists(creator, hydration); 96 + }); 97 + return skeleton; 98 + }; 99 + 100 + const presentation = (inputs: { 101 + ctx: Context; 102 + skeleton: Skeleton; 103 + hydration: HydrationState; 104 + }) => { 105 + const { ctx, skeleton, hydration } = inputs; 106 + const feed = mapDefined( 107 + skeleton.items, 108 + (item) => ctx.views.feedViewPost(item, hydration), 109 + ); 110 + return { 111 + feed, 112 + cursor: skeleton.cursor, 113 + }; 114 + }; 115 + 116 + type Context = { 117 + hydrator: Hydrator; 118 + views: Views; 119 + dataplane: DataPlane; 120 + }; 121 + 122 + type Params = QueryParams & { hydrateCtx: HydrateCtx }; 123 + 124 + type Skeleton = { 125 + items: FeedItem[]; 126 + cursor?: string; 127 + };
+4 -1
data-plane/routes/reposts.ts
··· 95 95 } 96 96 97 97 return { 98 - uris: reposts.map((r) => r.uri), 98 + reposts: reposts.map((r) => ({ 99 + uri: r.uri, 100 + subject: r.subject, 101 + })), 99 102 cursor: nextCursor, 100 103 }; 101 104 }