a tool for shared writing and social publishing
at update/thread-viewer 90 lines 2.8 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(`*, publications(*, documents_in_publications(*, documents(*)))`) 33 .order(`created_at`, { ascending: false }) 34 .order(`uri`, { ascending: false }) 35 .order("documents(sort_date)", { 36 ascending: false, 37 referencedTable: "publications.documents_in_publications", 38 }) 39 .limit(1, { referencedTable: "publications.documents_in_publications" }) 40 .limit(25) 41 .eq("identity", identity); 42 43 if (cursor) { 44 query = query.or( 45 `created_at.lt.${cursor.timestamp},and(created_at.eq.${cursor.timestamp},uri.lt.${cursor.uri})`, 46 ); 47 } 48 let { data: pubs, error } = await query; 49 50 const hydratedSubscriptions = ( 51 await Promise.all( 52 pubs?.map(async (pub) => { 53 const normalizedRecord = normalizePublicationRecord( 54 pub.publications?.record 55 ); 56 if (!normalizedRecord) return null; 57 let id = await idResolver.did.resolve(pub.publications?.identity_did!); 58 return { 59 ...pub.publications!, 60 record: normalizedRecord, 61 authorProfile: id?.alsoKnownAs?.[0] 62 ? { handle: `@${id.alsoKnownAs[0].slice(5)}` } 63 : undefined, 64 } as PublicationSubscription; 65 }) || [] 66 ) 67 ).filter((sub): sub is PublicationSubscription => sub !== null); 68 69 const nextCursor = 70 pubs && pubs.length > 0 71 ? { 72 timestamp: pubs[pubs.length - 1].created_at, 73 uri: pubs[pubs.length - 1].uri, 74 } 75 : null; 76 77 return { 78 subscriptions: hydratedSubscriptions, 79 nextCursor, 80 }; 81} 82 83export type PublicationSubscription = { 84 authorProfile?: { handle: string }; 85 record: NormalizedPublication; 86 uri: string; 87 documents_in_publications: { 88 documents: { data?: Json; sort_date: string } | null; 89 }[]; 90};