a tool for shared writing and social publishing
0

Configure Feed

Select the types of activity you want to include in your feed.

fix comment action import

Jared Pereira (Dec 12, 2025, 4:51 PM +0400) be08f13f 216c2870

+140 -136
-133
app/p/[didOrHandle]/getProfileComments.ts
··· 1 - "use server"; 2 - 3 - import { supabaseServerClient } from "supabase/serverClient"; 4 - import { Json } from "supabase/database.types"; 5 - import { PubLeafletComment } from "lexicons/api"; 6 - 7 - export type Cursor = { 8 - indexed_at: string; 9 - uri: string; 10 - }; 11 - 12 - export type ProfileComment = { 13 - uri: string; 14 - record: Json; 15 - indexed_at: string; 16 - bsky_profiles: { record: Json; handle: string | null } | null; 17 - document: { 18 - uri: string; 19 - data: Json; 20 - } | null; 21 - publication: { 22 - uri: string; 23 - record: Json; 24 - } | null; 25 - // For replies, include the parent comment info 26 - parentComment: { 27 - uri: string; 28 - record: Json; 29 - bsky_profiles: { record: Json; handle: string | null } | null; 30 - } | null; 31 - }; 32 - 33 - export async function getProfileComments( 34 - did: string, 35 - cursor?: Cursor | null, 36 - ): Promise<{ comments: ProfileComment[]; nextCursor: Cursor | null }> { 37 - const limit = 20; 38 - 39 - let query = supabaseServerClient 40 - .from("comments_on_documents") 41 - .select( 42 - `*, 43 - bsky_profiles(record, handle), 44 - documents(uri, data, documents_in_publications(publications(*)))`, 45 - ) 46 - .eq("profile", did) 47 - .order("indexed_at", { ascending: false }) 48 - .order("uri", { ascending: false }) 49 - .limit(limit); 50 - 51 - if (cursor) { 52 - query = query.or( 53 - `indexed_at.lt.${cursor.indexed_at},and(indexed_at.eq.${cursor.indexed_at},uri.lt.${cursor.uri})`, 54 - ); 55 - } 56 - 57 - const { data: rawComments } = await query; 58 - 59 - if (!rawComments || rawComments.length === 0) { 60 - return { comments: [], nextCursor: null }; 61 - } 62 - 63 - // Collect parent comment URIs for replies 64 - const parentUris = rawComments 65 - .map((c) => (c.record as PubLeafletComment.Record).reply?.parent) 66 - .filter((uri): uri is string => !!uri); 67 - 68 - // Fetch parent comments if there are any replies 69 - let parentCommentsMap = new Map< 70 - string, 71 - { 72 - uri: string; 73 - record: Json; 74 - bsky_profiles: { record: Json; handle: string | null } | null; 75 - } 76 - >(); 77 - 78 - if (parentUris.length > 0) { 79 - const { data: parentComments } = await supabaseServerClient 80 - .from("comments_on_documents") 81 - .select(`uri, record, bsky_profiles(record, handle)`) 82 - .in("uri", parentUris); 83 - 84 - if (parentComments) { 85 - for (const pc of parentComments) { 86 - parentCommentsMap.set(pc.uri, { 87 - uri: pc.uri, 88 - record: pc.record, 89 - bsky_profiles: pc.bsky_profiles, 90 - }); 91 - } 92 - } 93 - } 94 - 95 - // Transform to ProfileComment format 96 - const comments: ProfileComment[] = rawComments.map((comment) => { 97 - const record = comment.record as PubLeafletComment.Record; 98 - const doc = comment.documents; 99 - const pub = doc?.documents_in_publications?.[0]?.publications; 100 - 101 - return { 102 - uri: comment.uri, 103 - record: comment.record, 104 - indexed_at: comment.indexed_at, 105 - bsky_profiles: comment.bsky_profiles, 106 - document: doc 107 - ? { 108 - uri: doc.uri, 109 - data: doc.data, 110 - } 111 - : null, 112 - publication: pub 113 - ? { 114 - uri: pub.uri, 115 - record: pub.record, 116 - } 117 - : null, 118 - parentComment: record.reply?.parent 119 - ? parentCommentsMap.get(record.reply.parent) || null 120 - : null, 121 - }; 122 - }); 123 - 124 - const nextCursor = 125 - comments.length === limit 126 - ? { 127 - indexed_at: comments[comments.length - 1].indexed_at, 128 - uri: comments[comments.length - 1].uri, 129 - } 130 - : null; 131 - 132 - return { comments, nextCursor }; 133 - }
+1 -1
app/p/[didOrHandle]/(profile)/comments/CommentsContent.tsx
··· 12 12 getProfileComments, 13 13 type ProfileComment, 14 14 type Cursor, 15 - } from "../getProfileComments"; 15 + } from "./getProfileComments"; 16 16 import { timeAgo } from "src/utils/timeAgo"; 17 17 import { getPublicationURL } from "app/lish/createPub/getPublicationURL"; 18 18
+133
app/p/[didOrHandle]/(profile)/comments/getProfileComments.ts
··· 1 + "use server"; 2 + 3 + import { supabaseServerClient } from "supabase/serverClient"; 4 + import { Json } from "supabase/database.types"; 5 + import { PubLeafletComment } from "lexicons/api"; 6 + 7 + export type Cursor = { 8 + indexed_at: string; 9 + uri: string; 10 + }; 11 + 12 + export type ProfileComment = { 13 + uri: string; 14 + record: Json; 15 + indexed_at: string; 16 + bsky_profiles: { record: Json; handle: string | null } | null; 17 + document: { 18 + uri: string; 19 + data: Json; 20 + } | null; 21 + publication: { 22 + uri: string; 23 + record: Json; 24 + } | null; 25 + // For replies, include the parent comment info 26 + parentComment: { 27 + uri: string; 28 + record: Json; 29 + bsky_profiles: { record: Json; handle: string | null } | null; 30 + } | null; 31 + }; 32 + 33 + export async function getProfileComments( 34 + did: string, 35 + cursor?: Cursor | null, 36 + ): Promise<{ comments: ProfileComment[]; nextCursor: Cursor | null }> { 37 + const limit = 20; 38 + 39 + let query = supabaseServerClient 40 + .from("comments_on_documents") 41 + .select( 42 + `*, 43 + bsky_profiles(record, handle), 44 + documents(uri, data, documents_in_publications(publications(*)))`, 45 + ) 46 + .eq("profile", did) 47 + .order("indexed_at", { ascending: false }) 48 + .order("uri", { ascending: false }) 49 + .limit(limit); 50 + 51 + if (cursor) { 52 + query = query.or( 53 + `indexed_at.lt.${cursor.indexed_at},and(indexed_at.eq.${cursor.indexed_at},uri.lt.${cursor.uri})`, 54 + ); 55 + } 56 + 57 + const { data: rawComments } = await query; 58 + 59 + if (!rawComments || rawComments.length === 0) { 60 + return { comments: [], nextCursor: null }; 61 + } 62 + 63 + // Collect parent comment URIs for replies 64 + const parentUris = rawComments 65 + .map((c) => (c.record as PubLeafletComment.Record).reply?.parent) 66 + .filter((uri): uri is string => !!uri); 67 + 68 + // Fetch parent comments if there are any replies 69 + let parentCommentsMap = new Map< 70 + string, 71 + { 72 + uri: string; 73 + record: Json; 74 + bsky_profiles: { record: Json; handle: string | null } | null; 75 + } 76 + >(); 77 + 78 + if (parentUris.length > 0) { 79 + const { data: parentComments } = await supabaseServerClient 80 + .from("comments_on_documents") 81 + .select(`uri, record, bsky_profiles(record, handle)`) 82 + .in("uri", parentUris); 83 + 84 + if (parentComments) { 85 + for (const pc of parentComments) { 86 + parentCommentsMap.set(pc.uri, { 87 + uri: pc.uri, 88 + record: pc.record, 89 + bsky_profiles: pc.bsky_profiles, 90 + }); 91 + } 92 + } 93 + } 94 + 95 + // Transform to ProfileComment format 96 + const comments: ProfileComment[] = rawComments.map((comment) => { 97 + const record = comment.record as PubLeafletComment.Record; 98 + const doc = comment.documents; 99 + const pub = doc?.documents_in_publications?.[0]?.publications; 100 + 101 + return { 102 + uri: comment.uri, 103 + record: comment.record, 104 + indexed_at: comment.indexed_at, 105 + bsky_profiles: comment.bsky_profiles, 106 + document: doc 107 + ? { 108 + uri: doc.uri, 109 + data: doc.data, 110 + } 111 + : null, 112 + publication: pub 113 + ? { 114 + uri: pub.uri, 115 + record: pub.record, 116 + } 117 + : null, 118 + parentComment: record.reply?.parent 119 + ? parentCommentsMap.get(record.reply.parent) || null 120 + : null, 121 + }; 122 + }); 123 + 124 + const nextCursor = 125 + comments.length === limit 126 + ? { 127 + indexed_at: comments[comments.length - 1].indexed_at, 128 + uri: comments[comments.length - 1].uri, 129 + } 130 + : null; 131 + 132 + return { comments, nextCursor }; 133 + }
+6 -2
app/p/[didOrHandle]/(profile)/comments/page.tsx
··· 1 1 import { idResolver } from "app/(home-pages)/reader/idResolver"; 2 - import { getProfileComments } from "../../getProfileComments"; 2 + import { getProfileComments } from "./getProfileComments"; 3 3 import { ProfileCommentsContent } from "./CommentsContent"; 4 4 5 5 export default async function ProfileCommentsPage(props: { ··· 19 19 const { comments, nextCursor } = await getProfileComments(did); 20 20 21 21 return ( 22 - <ProfileCommentsContent did={did} comments={comments} nextCursor={nextCursor} /> 22 + <ProfileCommentsContent 23 + did={did} 24 + comments={comments} 25 + nextCursor={nextCursor} 26 + /> 23 27 ); 24 28 }