a tool for shared writing and social publishing
1"use server"; 2 3import { getIdentityData } from "actions/getIdentityData"; 4import { getPublicationURL } from "app/lish/createPub/getPublicationURL"; 5import { supabaseServerClient } from "supabase/serverClient"; 6import { IdResolver } from "@atproto/identity"; 7import type { DidCache, CacheResult, DidDocument } from "@atproto/identity"; 8import Client from "ioredis"; 9import { AtUri } from "@atproto/api"; 10import { Json } from "supabase/database.types"; 11import { idResolver } from "./idResolver"; 12 13export type Cursor = { 14 timestamp: string; 15 uri: string; 16}; 17 18export async function getReaderFeed( 19 cursor?: Cursor | null, 20): Promise<{ posts: Post[]; nextCursor: Cursor | null }> { 21 let auth_res = await getIdentityData(); 22 if (!auth_res?.atp_did) return { posts: [], nextCursor: null }; 23 let query = supabaseServerClient 24 .from("documents") 25 .select( 26 `*, 27 comments_on_documents(count), 28 document_mentions_in_bsky(count), 29 documents_in_publications!inner(publications!inner(*, publication_subscriptions!inner(*)))`, 30 ) 31 .eq( 32 "documents_in_publications.publications.publication_subscriptions.identity", 33 auth_res.atp_did, 34 ) 35 .order("indexed_at", { ascending: false }) 36 .order("uri", { ascending: false }) 37 .limit(25); 38 if (cursor) { 39 query = query.or( 40 `indexed_at.lt.${cursor.timestamp},and(indexed_at.eq.${cursor.timestamp},uri.lt.${cursor.uri})`, 41 ); 42 } 43 let { data: feed, error } = await query; 44 45 let posts = await Promise.all( 46 feed?.map(async (post) => { 47 let pub = post.documents_in_publications[0].publications!; 48 let uri = new AtUri(post.uri); 49 let handle = await idResolver.did.resolve(uri.host); 50 let p: Post = { 51 publication: { 52 href: getPublicationURL(pub), 53 pubRecord: pub?.record || null, 54 uri: pub?.uri || "", 55 }, 56 author: handle?.alsoKnownAs?.[0] 57 ? `@${handle.alsoKnownAs[0].slice(5)}` 58 : null, 59 documents: { 60 comments_on_documents: post.comments_on_documents, 61 document_mentions_in_bsky: post.document_mentions_in_bsky, 62 data: post.data, 63 uri: post.uri, 64 indexed_at: post.indexed_at, 65 }, 66 }; 67 return p; 68 }) || [], 69 ); 70 const nextCursor = 71 posts.length > 0 72 ? { 73 timestamp: posts[posts.length - 1].documents.indexed_at, 74 uri: posts[posts.length - 1].documents.uri, 75 } 76 : null; 77 78 return { 79 posts, 80 nextCursor, 81 }; 82} 83 84export type Post = { 85 author: string | null; 86 publication: { 87 href: string; 88 pubRecord: Json; 89 uri: string; 90 }; 91 documents: { 92 data: Json; 93 uri: string; 94 indexed_at: string; 95 comments_on_documents: 96 | { 97 count: number; 98 }[] 99 | undefined; 100 document_mentions_in_bsky: 101 | { 102 count: number; 103 }[] 104 | undefined; 105 }; 106};