[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 125 lines 3.3 kB view raw
1import { Database } from "../db/index.ts"; 2import { TimeCidKeyset } from "../db/pagination.ts"; 3 4// Helper function to format feed items 5function feedItemFromRow( 6 item: { uri: string; cid: string }, 7): { uri: string; cid: string } { 8 return { 9 uri: item.uri, 10 cid: item.cid, 11 }; 12} 13 14interface FeedItem { 15 uri: string; 16 cid: string; 17 authorDid: string; 18 createdAt: string; 19 indexedAt: string; 20} 21 22export class Feeds { 23 private db: Database; 24 private timeCidKeyset: TimeCidKeyset; 25 26 constructor(db: Database) { 27 this.db = db; 28 this.timeCidKeyset = new TimeCidKeyset(); 29 } 30 31 async getFeedGenerators(uris: string[]) { 32 if (!uris.length) return { generators: [] }; 33 34 const generators = await this.db.models.Generator.find({ 35 uri: { $in: uris }, 36 }).populate("actor"); 37 38 return { 39 generators: generators.map((generator) => ({ 40 uri: generator.uri, 41 cid: generator.cid, 42 authorDid: generator.authorDid, 43 displayName: generator.displayName, 44 description: generator.description, 45 descriptionFacets: generator.descriptionFacets, 46 avatar: generator.avatar, 47 acceptsInteractions: generator.acceptsInteractions, 48 likeCount: generator.likeCount || 0, 49 createdAt: generator.createdAt, 50 indexedAt: generator.indexedAt, 51 actor: generator.actor, 52 })), 53 }; 54 } 55 56 async getAuthorFeed( 57 actorDid: string, 58 limit = 50, 59 cursor?: string, 60 ) { 61 // Get posts by this author - Post collection doesn't have replies (they're in Reply collection) 62 const postsQuery = this.db.models.Post.find({ 63 authorDid: actorDid, 64 }); 65 66 // Apply pagination to posts query 67 const paginatedPostsQuery = this.timeCidKeyset.paginate(postsQuery, { 68 limit, 69 cursor, 70 direction: "desc", 71 }); 72 73 const posts = await paginatedPostsQuery.exec(); 74 75 // Transform posts 76 const transformedPosts: FeedItem[] = posts.map((p) => ({ 77 uri: p.uri, 78 cid: p.cid, 79 authorDid: p.authorDid, 80 createdAt: p.createdAt, 81 indexedAt: p.indexedAt, 82 })); 83 84 return { 85 items: transformedPosts.map(feedItemFromRow), 86 cursor: this.timeCidKeyset.packFromResult(transformedPosts), 87 }; 88 } 89 90 async getTimeline(actorDid: string, limit = 50, cursor?: string) { 91 // Get people this actor follows 92 const follows = await this.db.models.Follow.find({ authorDid: actorDid }); 93 94 const followedDids = follows.map((f) => f.subject); 95 const timelineDids = [...followedDids, actorDid]; 96 97 // Get timeline posts 98 const postsQuery = this.db.models.Post.find({ 99 authorDid: { $in: timelineDids }, 100 }); 101 102 // Apply pagination using createdAt + cid (which matches DB schema and indexes) 103 const paginatedPostsQuery = this.timeCidKeyset.paginate(postsQuery, { 104 limit, 105 cursor, 106 direction: "desc", 107 }); 108 109 const posts = await paginatedPostsQuery.exec(); 110 111 // Transform posts 112 const transformedPosts: FeedItem[] = posts.map((p) => ({ 113 uri: p.uri, 114 cid: p.cid, 115 authorDid: p.authorDid, 116 createdAt: p.createdAt, 117 indexedAt: p.indexedAt, 118 })); 119 120 return { 121 items: transformedPosts.map(feedItemFromRow), 122 cursor: this.timeCidKeyset.packFromResult(transformedPosts), 123 }; 124 } 125}