a tool for shared writing and social publishing

Merge branch 'main' into update/reader

+71 -51
+1
actions/publishToPublication.ts
··· 300 300 await supabaseServerClient.from("documents").upsert({ 301 301 uri: result.uri, 302 302 data: record as unknown as Json, 303 + indexed: true, 303 304 }); 304 305 305 306 if (publication_uri) {
+15 -9
app/api/inngest/functions/sync_document_metadata.ts
··· 21 21 const handle = doc?.alsoKnownAs 22 22 ?.find((a) => a.startsWith("at://")) 23 23 ?.replace("at://", ""); 24 - return { handle: handle ?? null, isBridgy: handle?.includes("brid.gy") ?? false }; 24 + if (!doc) return null; 25 + const isBridgy = !!doc?.service?.find( 26 + (s) => 27 + typeof s.serviceEndpoint === "string" && 28 + s.serviceEndpoint.includes("atproto.brid.gy"), 29 + ); 30 + return { handle: handle ?? null, isBridgy, doc }; 25 31 }); 32 + if (!handleResult) return { error: "No Handle" }; 26 33 27 - if (handleResult.isBridgy) { 28 - await step.run("set-unindexed", async () => { 29 - await supabaseServerClient 30 - .from("documents") 31 - .update({ indexed: false }) 32 - .eq("uri", document_uri); 33 - }); 34 - } 34 + await step.run("set-indexed", async () => { 35 + return await supabaseServerClient 36 + .from("documents") 37 + .update({ indexed: !handleResult.isBridgy }) 38 + .eq("uri", document_uri) 39 + .select(); 40 + }); 35 41 36 42 if (!bsky_post_uri || handleResult.isBridgy) { 37 43 return { handle: handleResult.handle };
+45 -38
app/lish/[did]/[publication]/generateFeed.ts
··· 53 53 }, 54 54 }); 55 55 56 - await Promise.all( 57 - publication.documents_in_publications.map(async (doc) => { 58 - if (!doc.documents) return; 59 - const record = normalizeDocumentRecord( 60 - doc.documents?.data, 61 - doc.documents?.uri, 62 - ); 63 - const uri = new AtUri(doc.documents?.uri); 64 - const rkey = uri.rkey; 65 - if (!record) return; 56 + let docs = publication.documents_in_publications.sort((a, b) => { 57 + const dateA = a.documents?.sort_date 58 + ? new Date(a.documents.sort_date).getTime() 59 + : 0; 60 + const dateB = b.documents?.sort_date 61 + ? new Date(b.documents.sort_date).getTime() 62 + : 0; 63 + return dateB - dateA; // Sort in descending order (newest first) 64 + }); 65 + for (const doc of docs) { 66 + if (!doc.documents) continue; 67 + const record = normalizeDocumentRecord( 68 + doc.documents?.data, 69 + doc.documents?.uri, 70 + ); 71 + const uri = new AtUri(doc.documents?.uri); 72 + const rkey = uri.rkey; 73 + if (!record) continue; 66 74 67 - let blocks: PubLeafletPagesLinearDocument.Block[] = []; 68 - if (hasLeafletContent(record) && record.content.pages[0]) { 69 - const firstPage = record.content.pages[0]; 70 - if (PubLeafletPagesLinearDocument.isMain(firstPage)) { 71 - blocks = firstPage.blocks || []; 72 - } 75 + let blocks: PubLeafletPagesLinearDocument.Block[] = []; 76 + if (hasLeafletContent(record) && record.content.pages[0]) { 77 + const firstPage = record.content.pages[0]; 78 + if (PubLeafletPagesLinearDocument.isMain(firstPage)) { 79 + blocks = firstPage.blocks || []; 73 80 } 74 - const stream = await renderToReadableStream( 75 - createElement(StaticPostContent, { blocks, did: uri.host }), 76 - ); 77 - const reader = stream.getReader(); 78 - const chunks = []; 81 + } 82 + const stream = await renderToReadableStream( 83 + createElement(StaticPostContent, { blocks, did: uri.host }), 84 + ); 85 + const reader = stream.getReader(); 86 + const chunks = []; 79 87 80 - let done, value; 81 - while (!done) { 82 - ({ done, value } = await reader.read()); 83 - if (value) { 84 - chunks.push(new TextDecoder().decode(value)); 85 - } 88 + let done, value; 89 + while (!done) { 90 + ({ done, value } = await reader.read()); 91 + if (value) { 92 + chunks.push(new TextDecoder().decode(value)); 86 93 } 94 + } 87 95 88 - const docUrl = getDocumentURL(record, doc.documents.uri, pubRecord); 89 - feed.addItem({ 90 - title: record.title, 91 - description: record.description, 92 - date: record.publishedAt ? new Date(record.publishedAt) : new Date(), 93 - id: docUrl, 94 - link: docUrl, 95 - content: chunks.join(""), 96 - }); 97 - }), 98 - ); 96 + const docUrl = getDocumentURL(record, doc.documents.uri, pubRecord); 97 + feed.addItem({ 98 + title: record.title, 99 + description: record.description, 100 + date: record.publishedAt ? new Date(record.publishedAt) : new Date(), 101 + id: docUrl, 102 + link: docUrl, 103 + content: chunks.join(""), 104 + }); 105 + } 99 106 100 107 return feed; 101 108 }
+7 -3
app/lish/createPub/getPublicationURL.ts
··· 58 58 docUri: string, 59 59 publication?: PublicationInput | NormalizedPublication | null, 60 60 ): string { 61 - const path = doc.path || "/" + new AtUri(docUri).rkey; 61 + let path = doc.path || "/" + new AtUri(docUri).rkey; 62 + if (path[0] !== "/") path = "/" + path; 62 63 const aturi = new AtUri(docUri); 63 64 64 65 const isNormalized = 65 66 !!publication && 66 - (publication as NormalizedPublication).$type === "site.standard.publication"; 67 + (publication as NormalizedPublication).$type === 68 + "site.standard.publication"; 67 69 const normPub = isNormalized 68 70 ? (publication as NormalizedPublication) 69 71 : publication 70 72 ? normalizePublicationRecord((publication as PublicationInput).record) 71 73 : null; 72 - const pubInput = isNormalized ? null : (publication as PublicationInput | null); 74 + const pubInput = isNormalized 75 + ? null 76 + : (publication as PublicationInput | null); 73 77 74 78 // Non-leaflet documents always use the full publication site URL 75 79 if (doc.content && !hasLeafletContent(doc) && normPub?.url) {
+1 -1
app/lish/createPub/updatePublication.ts
··· 275 275 276 276 return buildRecord(normalizedPub, existingBasePath, publicationType, { 277 277 name, 278 - description, 278 + ...(description !== undefined && { description }), 279 279 icon: iconBlob, 280 280 preferences, 281 281 });
+1
feeds/index.ts
··· 115 115 ); 116 116 } 117 117 query = query 118 + .eq("indexed", true) 118 119 .or("data->postRef.not.is.null,data->bskyPostRef.not.is.null") 119 120 .order("sort_date", { ascending: false }) 120 121 .order("uri", { ascending: false })
+1
supabase/migrations/20260211000000_indexed_default_false.sql
··· 1 + ALTER TABLE documents ALTER COLUMN indexed SET DEFAULT false;