diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 8f66d8d3..457d562b 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -3,7 +3,12 @@ "allow": [ "mcp__acp__Edit", "mcp__acp__Write", - "mcp__acp__Bash" + "mcp__acp__Bash", + "mcp__primitive__say_hello", + "mcp__primitive__pending_delegations", + "mcp__primitive__claim_delegation", + "mcp__primitive__tasks_update", + "mcp__primitive__contexts_update" ] } } diff --git a/actions/getIdentityData.ts b/actions/getIdentityData.ts index 6ed8e690..c83a287c 100644 --- a/actions/getIdentityData.ts +++ b/actions/getIdentityData.ts @@ -42,6 +42,7 @@ export async function uncachedGetIdentityData() { .eq("confirmed", true) .single() : null; + console.log(auth_res); if (!auth_res?.data?.identities) return null; if (auth_res.data.identities.atp_did) { //I should create a relationship table so I can do this in the above query diff --git a/app/(home-pages)/p/[didOrHandle]/getProfilePosts.ts b/app/(home-pages)/p/[didOrHandle]/getProfilePosts.ts index 131b821f..92ca1bf6 100644 --- a/app/(home-pages)/p/[didOrHandle]/getProfilePosts.ts +++ b/app/(home-pages)/p/[didOrHandle]/getProfilePosts.ts @@ -26,6 +26,7 @@ export async function getProfilePosts( `*, comments_on_documents(count), document_mentions_in_bsky(count), + recommends_on_documents(count), documents_in_publications(publications(*))`, ) .like("uri", `at://${did}/%`) @@ -39,18 +40,19 @@ export async function getProfilePosts( ); } - 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(), - ]); + 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 || []); @@ -82,6 +84,7 @@ export async function getProfilePosts( 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, }, }; diff --git a/app/(home-pages)/reader/getReaderFeed.ts b/app/(home-pages)/reader/getReaderFeed.ts index d3996eb6..4ff16591 100644 --- a/app/(home-pages)/reader/getReaderFeed.ts +++ b/app/(home-pages)/reader/getReaderFeed.ts @@ -32,6 +32,7 @@ export async function getReaderFeed( `*, comments_on_documents(count), document_mentions_in_bsky(count), + recommends_on_documents(count), documents_in_publications!inner(publications!inner(*, publication_subscriptions!inner(*)))`, ) .eq( @@ -76,6 +77,7 @@ export async function getReaderFeed( documents: { comments_on_documents: post.comments_on_documents, document_mentions_in_bsky: post.document_mentions_in_bsky, + recommends_on_documents: post.recommends_on_documents, data: normalizedData, uri: post.uri, sort_date: post.sort_date, @@ -112,5 +114,6 @@ export type Post = { sort_date: string; comments_on_documents: { count: number }[] | undefined; document_mentions_in_bsky: { count: number }[] | undefined; + recommends_on_documents: { count: number }[] | undefined; }; }; diff --git a/app/(home-pages)/tag/[tag]/getDocumentsByTag.ts b/app/(home-pages)/tag/[tag]/getDocumentsByTag.ts index ebaffbe0..bb6394c9 100644 --- a/app/(home-pages)/tag/[tag]/getDocumentsByTag.ts +++ b/app/(home-pages)/tag/[tag]/getDocumentsByTag.ts @@ -21,6 +21,7 @@ export async function getDocumentsByTag( `*, comments_on_documents(count), document_mentions_in_bsky(count), + recommends_on_documents(count), documents_in_publications(publications(*))`, ) .contains("data->tags", `["${tag}"]`) @@ -67,6 +68,7 @@ export async function getDocumentsByTag( documents: { comments_on_documents: doc.comments_on_documents, document_mentions_in_bsky: doc.document_mentions_in_bsky, + recommends_on_documents: doc.recommends_on_documents, data: normalizedData, uri: doc.uri, sort_date: doc.sort_date, diff --git a/app/api/oauth/[route]/route.ts b/app/api/oauth/[route]/route.ts index 903ce627..db2ab8f4 100644 --- a/app/api/oauth/[route]/route.ts +++ b/app/api/oauth/[route]/route.ts @@ -89,10 +89,11 @@ export async function GET( // Trigger migration if identity needs it const metadata = identity?.metadata as Record | null; if (metadata?.needsStandardSiteMigration) { - await inngest.send({ - name: "user/migrate-to-standard", - data: { did: session.did }, - }); + if (process.env.NODE_ENV === "production") + await inngest.send({ + name: "user/migrate-to-standard", + data: { did: session.did }, + }); } let { data: token } = await supabaseServerClient @@ -104,7 +105,7 @@ export async function GET( }) .select() .single(); - + console.log({ token }); if (token) await setAuthToken(token.id); // Process successful authentication here @@ -113,6 +114,7 @@ export async function GET( console.log("User authenticated as:", session.did); return handleAction(s.action, redirectPath); } catch (e) { + console.log(e); redirect(redirectPath); } } diff --git a/app/api/rpc/[command]/get_publication_data.ts b/app/api/rpc/[command]/get_publication_data.ts index b33b9203..09cc91f4 100644 --- a/app/api/rpc/[command]/get_publication_data.ts +++ b/app/api/rpc/[command]/get_publication_data.ts @@ -40,7 +40,8 @@ export const get_publication_data = makeRoute({ documents_in_publications(documents( *, comments_on_documents(count), - document_mentions_in_bsky(count) + document_mentions_in_bsky(count), + recommends_on_documents(count) )), publication_subscriptions(*, identities(bsky_profiles(*))), publication_domains(*), @@ -87,6 +88,8 @@ export const get_publication_data = makeRoute({ data: dip.documents.data, commentsCount: dip.documents.comments_on_documents[0]?.count || 0, mentionsCount: dip.documents.document_mentions_in_bsky[0]?.count || 0, + recommendsCount: + dip.documents.recommends_on_documents?.[0]?.count || 0, }; }) .filter((d): d is NonNullable => d !== null); diff --git a/app/lish/[did]/[publication]/[rkey]/CanvasPage.tsx b/app/lish/[did]/[publication]/[rkey]/CanvasPage.tsx index 9b3a1058..03fb8cab 100644 --- a/app/lish/[did]/[publication]/[rkey]/CanvasPage.tsx +++ b/app/lish/[did]/[publication]/[rkey]/CanvasPage.tsx @@ -71,6 +71,8 @@ export function CanvasPage({ preferences={preferences} commentsCount={getCommentCount(document.comments_on_documents, pageId)} quotesCount={getQuoteCount(document.quotesAndMentions, pageId)} + recommendsCount={document.recommendsCount} + hasRecommended={document.hasRecommended} /> { let isMobile = useIsMobile(); return ( @@ -216,6 +220,8 @@ const CanvasMetadata = (props: { { - const { uri: document_uri, quotesAndMentions, normalizedDocument } = useDocument(); + const { + uri: document_uri, + quotesAndMentions, + normalizedDocument, + } = useDocument(); let { identity } = useIdentityData(); let { drawerOpen, drawer, pageId } = useInteractionState(document_uri); @@ -128,6 +135,12 @@ export const Interactions = (props: {
{tagCount > 0 && } + + {props.quotesCount === 0 || props.showMentions === false ? null : (
); }; -export function getQuoteCount(quotesAndMentions: { uri: string; link?: string }[], pageId?: string) { +export function getQuoteCount( + quotesAndMentions: { uri: string; link?: string }[], + pageId?: string, +) { return getQuoteCountFromArray(quotesAndMentions, pageId); } @@ -338,7 +365,10 @@ export function getQuoteCountFromArray( } } -export function getCommentCount(comments: CommentOnDocument[], pageId?: string) { +export function getCommentCount( + comments: CommentOnDocument[], + pageId?: string, +) { if (pageId) return comments.filter( (c) => (c.record as PubLeafletComment.Record)?.onPage === pageId, diff --git a/app/lish/[did]/[publication]/[rkey]/LinearDocumentPage.tsx b/app/lish/[did]/[publication]/[rkey]/LinearDocumentPage.tsx index 1046025a..c628e044 100644 --- a/app/lish/[did]/[publication]/[rkey]/LinearDocumentPage.tsx +++ b/app/lish/[did]/[publication]/[rkey]/LinearDocumentPage.tsx @@ -87,8 +87,12 @@ export function LinearDocumentPage({ pageId={pageId} showComments={preferences.showComments !== false} showMentions={preferences.showMentions !== false} - commentsCount={getCommentCount(document.comments_on_documents, pageId) || 0} + commentsCount={ + getCommentCount(document.comments_on_documents, pageId) || 0 + } quotesCount={getQuoteCount(document.quotesAndMentions, pageId) || 0} + recommendsCount={document.recommendsCount} + hasRecommended={document.hasRecommended} /> {!hasPageBackground &&
} diff --git a/app/lish/[did]/[publication]/[rkey]/PostHeader/PostHeader.tsx b/app/lish/[did]/[publication]/[rkey]/PostHeader/PostHeader.tsx index 86216159..f8e5de13 100644 --- a/app/lish/[did]/[publication]/[rkey]/PostHeader/PostHeader.tsx +++ b/app/lish/[did]/[publication]/[rkey]/PostHeader/PostHeader.tsx @@ -88,7 +88,11 @@ export function PostHeader(props: { showComments={props.preferences.showComments !== false} showMentions={props.preferences.showMentions !== false} quotesCount={getQuoteCount(document?.quotesAndMentions || []) || 0} - commentsCount={getCommentCount(document?.comments_on_documents || []) || 0} + commentsCount={ + getCommentCount(document?.comments_on_documents || []) || 0 + } + recommendsCount={document?.recommendsCount || 0} + hasRecommended={document?.hasRecommended || false} /> } diff --git a/app/lish/[did]/[publication]/[rkey]/getPostPageData.ts b/app/lish/[did]/[publication]/[rkey]/getPostPageData.ts index 9f7f44a2..4d18cae5 100644 --- a/app/lish/[did]/[publication]/[rkey]/getPostPageData.ts +++ b/app/lish/[did]/[publication]/[rkey]/getPostPageData.ts @@ -8,8 +8,12 @@ import { } from "src/utils/normalizeRecords"; import { PubLeafletPublication, SiteStandardPublication } from "lexicons/api"; import { documentUriFilter } from "src/utils/uriHelpers"; +import { getIdentityData } from "actions/getIdentityData"; export async function getPostPageData(did: string, rkey: string) { + const identity = await getIdentityData(); + const currentUserDid = identity?.atp_did; + let { data: documents } = await supabaseServerClient .from("documents") .select( @@ -22,7 +26,8 @@ export async function getPostPageData(did: string, rkey: string) { publication_subscriptions(*)) ), document_mentions_in_bsky(*), - leaflets_in_publications(*) + leaflets_in_publications(*), + recommends_on_documents(count) `, ) .or(documentUriFilter(did, rkey)) @@ -32,13 +37,28 @@ export async function getPostPageData(did: string, rkey: string) { if (!document) return null; + // Check if current user has recommended this document + let hasRecommended = false; + if (currentUserDid) { + const { data: userRecommend } = await supabaseServerClient + .from("recommends_on_documents") + .select("uri") + .eq("document", document.uri) + .eq("recommender_did", currentUserDid) + .limit(1); + hasRecommended = (userRecommend?.length ?? 0) > 0; + } + // Normalize the document record - this is the primary way consumers should access document data - const normalizedDocument = normalizeDocumentRecord(document.data, document.uri); + const normalizedDocument = normalizeDocumentRecord( + document.data, + document.uri, + ); if (!normalizedDocument) return null; // Normalize the publication record - this is the primary way consumers should access publication data const normalizedPublication = normalizePublicationRecord( - document.documents_in_publications[0]?.publications?.record + document.documents_in_publications[0]?.publications?.record, ); // Fetch constellation backlinks for mentions @@ -83,7 +103,10 @@ export async function getPostPageData(did: string, rkey: string) { // Filter and sort documents by publishedAt const sortedDocs = allDocs .map((dip) => { - const normalizedData = normalizeDocumentRecord(dip?.documents?.data, dip?.documents?.uri); + const normalizedData = normalizeDocumentRecord( + dip?.documents?.data, + dip?.documents?.uri, + ); return { uri: dip?.documents?.uri, title: normalizedData?.title, @@ -98,7 +121,9 @@ export async function getPostPageData(did: string, rkey: string) { ); // Find current document index - const currentIndex = sortedDocs.findIndex((doc) => doc.uri === document.uri); + const currentIndex = sortedDocs.findIndex( + (doc) => doc.uri === document.uri, + ); if (currentIndex !== -1) { prevNext = { @@ -122,13 +147,21 @@ export async function getPostPageData(did: string, rkey: string) { // Build explicit publication context for consumers const rawPub = document.documents_in_publications[0]?.publications; - const publication = rawPub ? { - uri: rawPub.uri, - name: rawPub.name, - identity_did: rawPub.identity_did, - record: rawPub.record as PubLeafletPublication.Record | SiteStandardPublication.Record | null, - publication_subscriptions: rawPub.publication_subscriptions || [], - } : null; + const publication = rawPub + ? { + uri: rawPub.uri, + name: rawPub.name, + identity_did: rawPub.identity_did, + record: rawPub.record as + | PubLeafletPublication.Record + | SiteStandardPublication.Record + | null, + publication_subscriptions: rawPub.publication_subscriptions || [], + } + : null; + + // Get recommends count from the aggregated query result + const recommendsCount = document.recommends_on_documents?.[0]?.count ?? 0; return { ...document, @@ -143,6 +176,9 @@ export async function getPostPageData(did: string, rkey: string) { comments: document.comments_on_documents, mentions: document.document_mentions_in_bsky, leafletId: document.leaflets_in_publications[0]?.leaflet || null, + // Recommends data + recommendsCount, + hasRecommended, }; } diff --git a/app/lish/[did]/[publication]/dashboard/PublishedPostsLists.tsx b/app/lish/[did]/[publication]/dashboard/PublishedPostsLists.tsx index b8a24406..ae0e12bf 100644 --- a/app/lish/[did]/[publication]/dashboard/PublishedPostsLists.tsx +++ b/app/lish/[did]/[publication]/dashboard/PublishedPostsLists.tsx @@ -60,7 +60,9 @@ export function PublishedPostsList(props: { function PublishedPostItem(props: { doc: PublishedDocument; - publication: NonNullable["data"]>["publication"]>; + publication: NonNullable< + NonNullable["data"]>["publication"] + >; pubRecord: ReturnType; showPageBackground: boolean; }) { @@ -94,10 +96,7 @@ function PublishedPostItem(props: {
{leaflet && leaflet.permission_tokens && ( <> - + @@ -129,9 +128,7 @@ function PublishedPostItem(props: {
{doc.record.description ? ( -

- {doc.record.description} -

+

{doc.record.description}

) : null}
{doc.record.publishedAt ? ( @@ -140,6 +137,9 @@ function PublishedPostItem(props: { { if (!doc.documents) return null; - const doc_record = normalizeDocumentRecord(doc.documents.data); + const doc_record = normalizeDocumentRecord( + doc.documents.data, + ); if (!doc_record) return null; let uri = new AtUri(doc.documents.uri); let quotes = @@ -128,6 +131,8 @@ export default async function Publication(props: { record?.preferences?.showComments === false ? 0 : doc.documents.comments_on_documents[0].count || 0; + let recommends = + doc.documents.recommends_on_documents?.[0]?.count || 0; let tags = doc_record.tags || []; return ( @@ -164,6 +169,9 @@ export default async function Publication(props: { +
{tagsCount === 0 ? null : ( <> @@ -38,6 +40,12 @@ export const InteractionPreview = (props: { )} + + {!props.showMentions || props.quotesCount === 0 ? null : ( { pubRecord?.preferences?.showComments === false ? 0 : props.documents.comments_on_documents?.[0]?.count || 0; + let recommends = props.documents.recommends_on_documents?.[0]?.count || 0; let tags = (postRecord?.tags as string[] | undefined) || []; // For standalone posts, link directly to the document @@ -103,6 +104,9 @@ export const PostListing = (props: Post) => { postUrl={postHref} quotesCount={quotes} commentsCount={comments} + recommendsCount={recommends} + hasRecommended={false} + documentUri={props.documents.uri} tags={tags} showComments={pubRecord?.preferences?.showComments !== false} showMentions={pubRecord?.preferences?.showMentions !== false} diff --git a/contexts/DocumentContext.tsx b/contexts/DocumentContext.tsx index 467d9ad9..fdd54954 100644 --- a/contexts/DocumentContext.tsx +++ b/contexts/DocumentContext.tsx @@ -21,6 +21,8 @@ export type DocumentContextValue = Pick< | "comments" | "mentions" | "leafletId" + | "recommendsCount" + | "hasRecommended" >; const DocumentContext = createContext(null);