From 3bbb4ddccefef4ced1b3f2e4447481a501888179 Mon Sep 17 00:00:00 2001 From: Jared Pereira Date: Thu, 5 Mar 2026 16:50:42 -0500 Subject: [PATCH] add get profile posts rpc --- .../p/[didOrHandle]/getProfilePosts.ts | 92 ++++++++----------- supabase/database.types.ts | 19 ++++ ...5000000_add_get_profile_posts_function.sql | 46 ++++++++++ 3 files changed, 101 insertions(+), 56 deletions(-) create mode 100644 supabase/migrations/20260305000000_add_get_profile_posts_function.sql diff --git a/app/(home-pages)/p/[didOrHandle]/getProfilePosts.ts b/app/(home-pages)/p/[didOrHandle]/getProfilePosts.ts index 92ca1bf6..07ab25b7 100644 --- a/app/(home-pages)/p/[didOrHandle]/getProfilePosts.ts +++ b/app/(home-pages)/p/[didOrHandle]/getProfilePosts.ts @@ -20,79 +20,59 @@ export async function getProfilePosts( ): Promise<{ posts: Post[]; nextCursor: Cursor | null }> { const limit = 20; - let query = supabaseServerClient - .from("documents") - .select( - `*, - comments_on_documents(count), - document_mentions_in_bsky(count), - recommends_on_documents(count), - documents_in_publications(publications(*))`, - ) - .like("uri", `at://${did}/%`) - .order("sort_date", { ascending: false }) - .order("uri", { ascending: false }) - .limit(limit); - - if (cursor) { - query = query.or( - `sort_date.lt.${cursor.sort_date},and(sort_date.eq.${cursor.sort_date},uri.lt.${cursor.uri})`, - ); + let [{ data: rawFeed, error }, { data: profile }] = await Promise.all([ + supabaseServerClient.rpc("get_profile_posts", { + p_did: did, + p_cursor_sort_date: cursor?.sort_date ?? null, + p_cursor_uri: cursor?.uri ?? null, + p_limit: limit, + }), + supabaseServerClient + .from("bsky_profiles") + .select("handle") + .eq("did", did) + .single(), + ]); + + if (error) { + console.error("[getProfilePosts] rpc error:", error); + return { posts: [], nextCursor: null }; } - let [{ data: rawDocs }, { data: rawPubs }, { data: profile }] = - await Promise.all([ - query, - supabaseServerClient - .from("publications") - .select("*") - .eq("identity_did", did), - supabaseServerClient - .from("bsky_profiles") - .select("handle") - .eq("did", did) - .single(), - ]); - - // Deduplicate records that may exist under both pub.leaflet and site.standard namespaces - const docs = deduplicateByUriOrdered(rawDocs || []); - const pubs = deduplicateByUriOrdered(rawPubs || []); + let feed = deduplicateByUriOrdered(rawFeed || []); + if (feed.length === 0) return { posts: [], nextCursor: null }; - // Build a map of publications for quick lookup - let pubMap = new Map[number]>(); - for (let pub of pubs || []) { - pubMap.set(pub.uri, pub); - } - - // Transform data to Post[] format let handle = profile?.handle ? `@${profile.handle}` : null; let posts: Post[] = []; - for (let doc of docs || []) { - // Normalize records - filter out unrecognized formats - const normalizedData = normalizeDocumentRecord(doc.data, doc.uri); + for (let row of feed) { + const normalizedData = normalizeDocumentRecord(row.data, row.uri); if (!normalizedData) continue; - let pubFromDoc = doc.documents_in_publications?.[0]?.publications; - let pub = pubFromDoc ? pubMap.get(pubFromDoc.uri) || pubFromDoc : null; + const normalizedPubRecord = row.publication_record + ? normalizePublicationRecord(row.publication_record) + : null; let post: Post = { author: handle, documents: { data: normalizedData, - uri: doc.uri, - sort_date: doc.sort_date, - comments_on_documents: doc.comments_on_documents, - document_mentions_in_bsky: doc.document_mentions_in_bsky, - recommends_on_documents: doc.recommends_on_documents, + uri: row.uri, + sort_date: row.sort_date, + comments_on_documents: [{ count: Number(row.comments_count) }], + document_mentions_in_bsky: [{ count: Number(row.mentions_count) }], + recommends_on_documents: [{ count: Number(row.recommends_count) }], }, }; - if (pub) { + if (row.publication_uri) { post.publication = { - href: getPublicationURL(pub), - pubRecord: normalizePublicationRecord(pub.record), - uri: pub.uri, + href: getPublicationURL({ + uri: row.publication_uri, + record: row.publication_record, + }), + pubRecord: normalizedPubRecord, + uri: row.publication_uri, }; } diff --git a/supabase/database.types.ts b/supabase/database.types.ts index dfd01d43..78767dc4 100644 --- a/supabase/database.types.ts +++ b/supabase/database.types.ts @@ -1360,6 +1360,25 @@ export type Database = { like: unknown }[] } + get_profile_posts: { + Args: { + p_did: string + p_cursor_sort_date?: string | null + p_cursor_uri?: string | null + p_limit?: number + } + Returns: { + uri: string + data: Json + sort_date: string + comments_count: number + mentions_count: number + recommends_count: number + publication_uri: string + publication_record: Json + publication_name: string + }[] + } get_reader_feed: { Args: { p_identity: string diff --git a/supabase/migrations/20260305000000_add_get_profile_posts_function.sql b/supabase/migrations/20260305000000_add_get_profile_posts_function.sql new file mode 100644 index 00000000..71e5f1bf --- /dev/null +++ b/supabase/migrations/20260305000000_add_get_profile_posts_function.sql @@ -0,0 +1,46 @@ +CREATE OR REPLACE FUNCTION get_profile_posts( + p_did text, + p_cursor_sort_date timestamptz DEFAULT NULL, + p_cursor_uri text DEFAULT NULL, + p_limit int DEFAULT 20 +) +RETURNS TABLE ( + uri text, + data jsonb, + sort_date timestamptz, + comments_count bigint, + mentions_count bigint, + recommends_count bigint, + publication_uri text, + publication_record jsonb, + publication_name text +) +LANGUAGE sql STABLE +AS $$ + SELECT + d.uri, + d.data, + d.sort_date, + (SELECT count(*) FROM comments_on_documents c WHERE c.document = d.uri), + (SELECT count(*) FROM document_mentions_in_bsky m WHERE m.document = d.uri), + (SELECT count(*) FROM recommends_on_documents r WHERE r.document = d.uri), + pub.uri, + pub.record, + pub.name + FROM documents d + LEFT JOIN LATERAL ( + SELECT p.uri, p.record, p.name + FROM documents_in_publications dip + JOIN publications p ON p.uri = dip.publication + WHERE dip.document = d.uri + LIMIT 1 + ) pub ON true + WHERE d.uri LIKE 'at://' || p_did || '/%' + AND ( + p_cursor_sort_date IS NULL + OR d.sort_date < p_cursor_sort_date + OR (d.sort_date = p_cursor_sort_date AND d.uri < p_cursor_uri) + ) + ORDER BY d.sort_date DESC, d.uri DESC + LIMIT p_limit; +$$; -- 2.51.2