diff --git a/app/[leaflet_id]/publish/BskyPostEditorProsemirror.tsx b/app/[leaflet_id]/publish/BskyPostEditorProsemirror.tsx index 379208f5..6496c57b 100644 --- a/app/[leaflet_id]/publish/BskyPostEditorProsemirror.tsx +++ b/app/[leaflet_id]/publish/BskyPostEditorProsemirror.tsx @@ -170,7 +170,6 @@ export function BlueskyPostEditorProsemirror(props: { const handleMentionSelect = useCallback( (mention: Mention) => { - if (mention.type !== "did") return; if (!viewRef.current || mentionInsertPos === null) return; const view = viewRef.current; const from = mentionInsertPos - 1; @@ -180,19 +179,37 @@ export function BlueskyPostEditorProsemirror(props: { // Delete the @ symbol tr.delete(from, to); - // Insert @handle - const mentionText = "@" + mention.handle; - tr.insertText(mentionText, from); - - // Apply mention mark - tr.addMark( - from, - from + mentionText.length, - bskyPostSchema.marks.mention.create({ did: mention.did }), - ); - - // Add a space after the mention - tr.insertText(" ", from + mentionText.length); + if (mention.type === "did") { + // Insert @handle with mention mark + const mentionText = "@" + mention.handle; + tr.insertText(mentionText, from); + tr.addMark( + from, + from + mentionText.length, + bskyPostSchema.marks.mention.create({ did: mention.did }), + ); + tr.insertText(" ", from + mentionText.length); + } else if (mention.type === "publication") { + // Insert publication name as a link + const linkText = mention.name; + tr.insertText(linkText, from); + tr.addMark( + from, + from + linkText.length, + bskyPostSchema.marks.link.create({ href: mention.url }), + ); + tr.insertText(" ", from + linkText.length); + } else if (mention.type === "post") { + // Insert post title as a link + const linkText = mention.title; + tr.insertText(linkText, from); + tr.addMark( + from, + from + linkText.length, + bskyPostSchema.marks.link.create({ href: mention.url }), + ); + tr.insertText(" ", from + linkText.length); + } view.dispatch(tr); view.focus(); diff --git a/app/api/rpc/[command]/search_publication_documents.ts b/app/api/rpc/[command]/search_publication_documents.ts index 382820f7..a58f6867 100644 --- a/app/api/rpc/[command]/search_publication_documents.ts +++ b/app/api/rpc/[command]/search_publication_documents.ts @@ -1,6 +1,8 @@ +import { AtUri } from "@atproto/api"; import { z } from "zod"; import { makeRoute } from "../lib"; import type { Env } from "./route"; +import { getPublicationURL } from "app/lish/createPub/getPublicationURL"; export type SearchPublicationDocumentsReturnType = Awaited< ReturnType<(typeof search_publication_documents)["handler"]> @@ -18,9 +20,12 @@ export const search_publication_documents = makeRoute({ { supabase }: Pick, ) => { // Get documents in the publication, filtering by title using JSON operator + // Also join with publications to get the record for URL construction const { data: documents, error } = await supabase .from("documents_in_publications") - .select("document, documents!inner(uri, data)") + .select( + "document, documents!inner(uri, data), publications!inner(uri, record)", + ) .eq("publication", publication_uri) .ilike("documents.data->>title", `%${query}%`) .limit(limit); @@ -31,10 +36,16 @@ export const search_publication_documents = makeRoute({ ); } - const result = documents.map((d) => ({ - uri: d.documents.uri, - title: (d.documents.data as { title?: string })?.title || "Untitled", - })); + const result = documents.map((d) => { + const docUri = new AtUri(d.documents.uri); + const pubUrl = getPublicationURL(d.publications); + + return { + uri: d.documents.uri, + title: (d.documents.data as { title?: string })?.title || "Untitled", + url: `${pubUrl}/${docUri.rkey}`, + }; + }); return { result: { documents: result } }; }, diff --git a/app/api/rpc/[command]/search_publication_names.ts b/app/api/rpc/[command]/search_publication_names.ts index cd042311..72636497 100644 --- a/app/api/rpc/[command]/search_publication_names.ts +++ b/app/api/rpc/[command]/search_publication_names.ts @@ -1,6 +1,7 @@ import { z } from "zod"; import { makeRoute } from "../lib"; import type { Env } from "./route"; +import { getPublicationURL } from "app/lish/createPub/getPublicationURL"; export type SearchPublicationNamesReturnType = Awaited< ReturnType<(typeof search_publication_names)["handler"]> @@ -12,10 +13,7 @@ export const search_publication_names = makeRoute({ query: z.string(), limit: z.number().optional().default(10), }), - handler: async ( - { query, limit }, - { supabase }: Pick, - ) => { + handler: async ({ query, limit }, { supabase }: Pick) => { // Search publications by name in record (case-insensitive partial match) const { data: publications, error } = await supabase .from("publications") @@ -27,10 +25,14 @@ export const search_publication_names = makeRoute({ throw new Error(`Failed to search publications: ${error.message}`); } - const result = publications.map((p) => ({ - uri: p.uri, - name: (p.record as { name?: string })?.name || "Untitled", - })); + const result = publications.map((p) => { + const record = p.record as { name?: string }; + return { + uri: p.uri, + name: record.name || "Untitled", + url: getPublicationURL(p), + }; + }); return { result: { publications: result } }; }, diff --git a/components/Mention.tsx b/components/Mention.tsx index ee709f21..32e0075a 100644 --- a/components/Mention.tsx +++ b/components/Mention.tsx @@ -457,8 +457,8 @@ export type Mention = displayName?: string; avatar?: string; } - | { type: "publication"; uri: string; name: string } - | { type: "post"; uri: string; title: string }; + | { type: "publication"; uri: string; name: string; url: string } + | { type: "post"; uri: string; title: string; url: string }; export type MentionScope = | { type: "default" } @@ -493,6 +493,7 @@ function useMentionSuggestions(query: string | null) { type: "post" as const, uri: d.uri, title: d.title, + url: d.url, })), ); } else { @@ -517,6 +518,7 @@ function useMentionSuggestions(query: string | null) { type: "publication" as const, uri: p.uri, name: p.name, + url: p.url, })), ]); }