a tool for shared writing and social publishing
at feature/footnotes 92 lines 2.9 kB view raw
1"use server"; 2 3import { AtpAgent } from "@atproto/api"; 4import { ProfileViewDetailed } from "@atproto/api/dist/client/types/app/bsky/actor/defs"; 5import { getIdentityData } from "actions/getIdentityData"; 6import { Json } from "supabase/database.types"; 7import { supabaseServerClient } from "supabase/serverClient"; 8import { idResolver } from "./idResolver"; 9import { Cursor } from "./getReaderFeed"; 10import { 11 normalizePublicationRecord, 12 type NormalizedPublication, 13} from "src/utils/normalizeRecords"; 14 15export async function getSubscriptions( 16 did?: string | null, 17 cursor?: Cursor | null, 18): Promise<{ 19 nextCursor: null | Cursor; 20 subscriptions: PublicationSubscription[]; 21}> { 22 // If no DID provided, use logged-in user's DID 23 let identity = did; 24 if (!identity) { 25 const auth_res = await getIdentityData(); 26 if (!auth_res?.atp_did) return { subscriptions: [], nextCursor: null }; 27 identity = auth_res.atp_did; 28 } 29 30 let query = supabaseServerClient 31 .from("publication_subscriptions") 32 .select( 33 `*, publications(*, publication_subscriptions(*), documents_in_publications(*, documents(*)))`, 34 ) 35 .order(`created_at`, { ascending: false }) 36 .order(`uri`, { ascending: false }) 37 .order("documents(sort_date)", { 38 ascending: false, 39 referencedTable: "publications.documents_in_publications", 40 }) 41 .limit(1, { referencedTable: "publications.documents_in_publications" }) 42 .limit(25) 43 .eq("identity", identity); 44 45 if (cursor) { 46 query = query.or( 47 `created_at.lt.${cursor.timestamp},and(created_at.eq.${cursor.timestamp},uri.lt.${cursor.uri})`, 48 ); 49 } 50 let { data: pubs, error } = await query; 51 52 const hydratedSubscriptions = ( 53 await Promise.all( 54 pubs?.map(async (pub) => { 55 const normalizedRecord = normalizePublicationRecord( 56 pub.publications?.record, 57 ); 58 if (!normalizedRecord) return null; 59 let id = await idResolver.did.resolve(pub.publications?.identity_did!); 60 return { 61 ...pub.publications!, 62 record: normalizedRecord, 63 authorProfile: id?.alsoKnownAs?.[0] 64 ? { handle: `@${id.alsoKnownAs[0].slice(5)}` } 65 : undefined, 66 } as PublicationSubscription; 67 }) || [], 68 ) 69 ).filter((sub): sub is PublicationSubscription => sub !== null); 70 const nextCursor = 71 pubs && pubs.length > 0 72 ? { 73 timestamp: pubs[pubs.length - 1].created_at, 74 uri: pubs[pubs.length - 1].uri, 75 } 76 : null; 77 78 return { 79 subscriptions: hydratedSubscriptions, 80 nextCursor, 81 }; 82} 83 84export type PublicationSubscription = { 85 authorProfile?: { handle: string }; 86 record: NormalizedPublication; 87 publication_subscriptions: { identity: string }[]; 88 uri: string; 89 documents_in_publications: { 90 documents: { data?: Json; sort_date: string } | null; 91 }[]; 92};