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

getActorLikes

+125
+2
api/index.ts
··· 7 7 import getProfile from "./so/sprk/actor/getProfile.ts"; 8 8 import getAuthorFeed from "./so/sprk/feed/getAuthorFeed.ts"; 9 9 import getPostThread from "./so/sprk/feed/getPostThread.ts"; 10 + import getActorLikes from "./so/sprk/feed/getActorLikes.ts"; 10 11 import getAudios from "./so/sprk/sound/getAudios.ts"; 11 12 import getAudioPosts from "./so/sprk/sound/getAudioPosts.ts"; 12 13 import getFollows from "./so/sprk/graph/getFollows.ts"; ··· 39 40 getProfiles(server, ctx); 40 41 getAuthorFeed(server, ctx); 41 42 getPostThread(server, ctx); 43 + getActorLikes(server, ctx); 42 44 getAudios(server, ctx); 43 45 getAudioPosts(server, ctx); 44 46 getFollows(server, ctx);
+123
api/so/sprk/feed/getActorLikes.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 getActorLikes = createPipeline( 21 + skeleton, 22 + hydration, 23 + noPostBlocks, 24 + presentation, 25 + ); 26 + server.so.sprk.feed.getActorLikes({ 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 getActorLikes({ ...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 likesRes = await ctx.dataplane.likes.getActor(actorDid, limit, cursor); 65 + 66 + const items = likesRes.likes.map((l) => ({ post: { uri: l.uri } })); 67 + 68 + return { 69 + items, 70 + cursor: parseString(likesRes.cursor), 71 + }; 72 + }; 73 + 74 + const hydration = async (inputs: { 75 + ctx: Context; 76 + params: Params; 77 + skeleton: Skeleton; 78 + }) => { 79 + const { ctx, params, skeleton } = inputs; 80 + return await ctx.hydrator.hydrateFeedItems(skeleton.items, params.hydrateCtx); 81 + }; 82 + 83 + const noPostBlocks = (inputs: { 84 + ctx: Context; 85 + skeleton: Skeleton; 86 + hydration: HydrationState; 87 + }) => { 88 + const { ctx, skeleton, hydration } = inputs; 89 + skeleton.items = skeleton.items.filter((item) => { 90 + const creator = creatorFromUri(item.post.uri); 91 + return !ctx.views.viewerBlockExists(creator, hydration); 92 + }); 93 + return skeleton; 94 + }; 95 + 96 + const presentation = (inputs: { 97 + ctx: Context; 98 + skeleton: Skeleton; 99 + hydration: HydrationState; 100 + }) => { 101 + const { ctx, skeleton, hydration } = inputs; 102 + const feed = mapDefined( 103 + skeleton.items, 104 + (item) => ctx.views.feedViewPost(item, hydration), 105 + ); 106 + return { 107 + feed, 108 + cursor: skeleton.cursor, 109 + }; 110 + }; 111 + 112 + type Context = { 113 + hydrator: Hydrator; 114 + views: Views; 115 + dataplane: DataPlane; 116 + }; 117 + 118 + type Params = QueryParams & { hydrateCtx: HydrateCtx }; 119 + 120 + type Skeleton = { 121 + items: FeedItem[]; 122 + cursor?: string; 123 + };