a tool for shared writing and social publishing
at feature/footnotes 96 lines 2.7 kB view raw
1"use server"; 2 3import { getIdentityData } from "actions/getIdentityData"; 4import { supabaseServerClient } from "supabase/serverClient"; 5import type { 6 NormalizedDocument, 7 NormalizedPublication, 8} from "src/utils/normalizeRecords"; 9import { enrichDocumentToPost } from "./enrichPost"; 10 11export type Cursor = { 12 timestamp: string; 13 uri: string; 14}; 15 16export async function getReaderFeed( 17 cursor?: Cursor | null, 18): Promise<{ posts: Post[]; nextCursor: Cursor | null }> { 19 let auth_res = await getIdentityData(); 20 if (!auth_res?.atp_did) return { posts: [], nextCursor: null }; 21 22 const { data: rawFeed, error } = await supabaseServerClient.rpc( 23 "get_reader_feed", 24 { 25 p_identity: auth_res.atp_did, 26 p_cursor_timestamp: cursor?.timestamp, 27 p_cursor_uri: cursor?.uri, 28 p_limit: 25, 29 }, 30 ); 31 if (error) { 32 console.error("[getReaderFeed] rpc error:", error); 33 return { posts: [], nextCursor: null }; 34 } 35 36 if (rawFeed.length === 0) return { posts: [], nextCursor: null }; 37 38 // Reshape rows to match the structure enrichDocumentToPost expects 39 const feed = rawFeed.map((row: any) => ({ 40 uri: row.uri, 41 data: row.data, 42 sort_date: row.sort_date, 43 comments_on_documents: [{ count: Number(row.comments_count) }], 44 document_mentions_in_bsky: [{ count: Number(row.mentions_count) }], 45 recommends_on_documents: [{ count: Number(row.recommends_count) }], 46 documents_in_publications: row.publication_uri 47 ? [ 48 { 49 publications: { 50 uri: row.publication_uri, 51 record: row.publication_record, 52 name: row.publication_name, 53 }, 54 }, 55 ] 56 : [], 57 })); 58 59 let posts = ( 60 await Promise.all(feed.map((post) => enrichDocumentToPost(post as any))) 61 ).filter((post): post is Post => post !== null); 62 if (feed.length > 0 && posts.length !== feed.length) { 63 console.log(`[getReaderFeed] ${feed.length - posts.length}/${feed.length} posts dropped during enrichment`); 64 } 65 66 const nextCursor = 67 posts.length > 0 68 ? { 69 timestamp: posts[posts.length - 1].documents.sort_date, 70 uri: posts[posts.length - 1].documents.uri, 71 } 72 : null; 73 74 return { 75 posts, 76 nextCursor, 77 }; 78} 79 80export type Post = { 81 author: string | null; 82 publication?: { 83 href: string; 84 pubRecord: NormalizedPublication | null; 85 uri: string; 86 }; 87 documents: { 88 data: NormalizedDocument | null; 89 uri: string; 90 sort_date: string; 91 comments_on_documents: { count: number }[] | undefined; 92 document_mentions_in_bsky: { count: number }[] | undefined; 93 recommends_on_documents: { count: number }[] | undefined; 94 mentionsCount?: number; 95 }; 96};