diff --git a/actions/deleteLeaflet.ts b/actions/deleteLeaflet.ts index d6108def..a25acb45 100644 --- a/actions/deleteLeaflet.ts +++ b/actions/deleteLeaflet.ts @@ -23,11 +23,13 @@ export async function deleteLeaflet(permission_token: PermissionToken) { // Check publication and document ownership in one query let { data: tokenData } = await supabaseServerClient .from("permission_tokens") - .select(` + .select( + ` id, leaflets_in_publications(publication, publications!inner(identity_did)), leaflets_to_documents(document, documents!inner(uri)) - `) + `, + ) .eq("id", permission_token.id) .single(); @@ -36,28 +38,34 @@ export async function deleteLeaflet(permission_token: PermissionToken) { const leafletInPubs = tokenData.leaflets_in_publications || []; if (leafletInPubs.length > 0) { if (!identity) { - throw new Error("Unauthorized: You must be logged in to delete a leaflet in a publication"); + throw new Error( + "Unauthorized: You must be logged in to delete a leaflet in a publication", + ); } const isOwner = leafletInPubs.some( - (pub: any) => pub.publications.identity_did === identity.atp_did + (pub: any) => pub.publications.identity_did === identity.atp_did, ); if (!isOwner) { - throw new Error("Unauthorized: You must own the publication to delete this leaflet"); + throw new Error( + "Unauthorized: You must own the publication to delete this leaflet", + ); } } // Check if there's a standalone published document - const leafletDocs = tokenData.leaflets_to_documents || []; - if (leafletDocs.length > 0) { - if (!identity) { - throw new Error("Unauthorized: You must be logged in to delete a published leaflet"); + const leafletDoc = tokenData.leaflets_to_documents; + if (leafletDoc && leafletDoc.document) { + if (!identity || !identity.atp_did) { + throw new Error( + "Unauthorized: You must be logged in to delete a published leaflet", + ); } - for (let leafletDoc of leafletDocs) { - const docUri = leafletDoc.documents?.uri; - // Extract the DID from the document URI (format: at://did:plc:xxx/...) - if (docUri && !docUri.includes(identity.atp_did)) { - throw new Error("Unauthorized: You must own the published document to delete this leaflet"); - } + const docUri = leafletDoc.documents?.uri; + // Extract the DID from the document URI (format: at://did:plc:xxx/...) + if (docUri && !docUri.includes(identity.atp_did)) { + throw new Error( + "Unauthorized: You must own the published document to delete this leaflet", + ); } } } @@ -73,9 +81,9 @@ export async function deleteLeaflet(permission_token: PermissionToken) { .where(eq(permission_tokens.id, permission_token.id)); if (!token?.permission_token_rights?.write) return; - await tx - .delete(entities) - .where(eq(entities.set, token.permission_token_rights.entity_set)); + const entitySet = token.permission_token_rights.entity_set; + if (!entitySet) return; + await tx.delete(entities).where(eq(entities.set, entitySet)); await tx .delete(permission_tokens) .where(eq(permission_tokens.id, permission_token.id)); diff --git a/actions/publications/moveLeafletToPublication.ts b/actions/publications/moveLeafletToPublication.ts index a3ee8c62..160ddc4a 100644 --- a/actions/publications/moveLeafletToPublication.ts +++ b/actions/publications/moveLeafletToPublication.ts @@ -11,6 +11,8 @@ export async function moveLeafletToPublication( ) { let identity = await getIdentityData(); if (!identity || !identity.atp_did) return null; + + // Verify publication ownership let { data: publication } = await supabaseServerClient .from("publications") .select("*") @@ -18,6 +20,7 @@ export async function moveLeafletToPublication( .single(); if (publication?.identity_did !== identity.atp_did) return; + // Save as a publication draft await supabaseServerClient.from("leaflets_in_publications").insert({ publication: publication_uri, leaflet: leaflet_id, diff --git a/actions/publications/saveLeafletDraft.ts b/actions/publications/saveLeafletDraft.ts new file mode 100644 index 00000000..c6650672 --- /dev/null +++ b/actions/publications/saveLeafletDraft.ts @@ -0,0 +1,26 @@ +"use server"; + +import { getIdentityData } from "actions/getIdentityData"; +import { supabaseServerClient } from "supabase/serverClient"; + +export async function saveLeafletDraft( + leaflet_id: string, + metadata: { title: string; description: string }, + entitiesToDelete: string[], +) { + let identity = await getIdentityData(); + if (!identity || !identity.atp_did) return null; + + // Save as a looseleaf draft in leaflets_to_documents with null document + await supabaseServerClient.from("leaflets_to_documents").upsert({ + leaflet: leaflet_id, + document: null, + title: metadata.title, + description: metadata.description, + }); + + await supabaseServerClient + .from("entities") + .delete() + .in("id", entitiesToDelete); +} diff --git a/app/(home-pages)/home/HomeLayout.tsx b/app/(home-pages)/home/HomeLayout.tsx index 568a6e7e..744b4d5d 100644 --- a/app/(home-pages)/home/HomeLayout.tsx +++ b/app/(home-pages)/home/HomeLayout.tsx @@ -136,7 +136,7 @@ export function HomeLeafletList(props: { (acc, tok) => { let title = tok.permission_tokens.leaflets_in_publications[0]?.title || - tok.permission_tokens.leaflets_to_documents[0]?.title; + tok.permission_tokens.leaflets_to_documents?.title; if (title) acc[tok.permission_tokens.root_entity] = title; return acc; }, @@ -233,7 +233,7 @@ export function LeafletList(props: { value={{ ...leaflet, leaflets_in_publications: leaflet.leaflets_in_publications || [], - leaflets_to_documents: leaflet.leaflets_to_documents || [], + leaflets_to_documents: leaflet.leaflets_to_documents || null, blocked_by_admin: null, custom_domain_routes: [], }} @@ -292,7 +292,7 @@ function useSearchedLeaflets( ({ token: leaflet, archived: archived }) => { let published = !!leaflet.leaflets_in_publications?.find((l) => l.doc) || - !!leaflet.leaflets_to_documents?.find((l) => l.document); + !!leaflet.leaflets_to_documents?.document; let drafts = !!leaflet.leaflets_in_publications?.length && !published; let docs = !leaflet.leaflets_in_publications?.length && !archived; // If no filters are active, show all diff --git a/app/(home-pages)/home/page.tsx b/app/(home-pages)/home/page.tsx index 5408ab37..9b783167 100644 --- a/app/(home-pages)/home/page.tsx +++ b/app/(home-pages)/home/page.tsx @@ -30,7 +30,7 @@ export default async function Home() { (acc, tok) => { let title = tok.permission_tokens.leaflets_in_publications[0]?.title || - tok.permission_tokens.leaflets_to_documents[0]?.title; + tok.permission_tokens.leaflets_to_documents?.title; if (title) acc[tok.permission_tokens.root_entity] = title; return acc; }, diff --git a/app/(home-pages)/looseleafs/LooseleafsLayout.tsx b/app/(home-pages)/looseleafs/LooseleafsLayout.tsx index fd1ba95b..c96140ac 100644 --- a/app/(home-pages)/looseleafs/LooseleafsLayout.tsx +++ b/app/(home-pages)/looseleafs/LooseleafsLayout.tsx @@ -111,7 +111,7 @@ export const LooseleafList = (props: { (acc, tok) => { let title = tok.permission_tokens.leaflets_in_publications[0]?.title || - tok.permission_tokens.leaflets_to_documents[0]?.title; + tok.permission_tokens.leaflets_to_documents?.title; if (title) acc[tok.permission_tokens.root_entity] = title; return acc; }, @@ -127,7 +127,9 @@ export const LooseleafList = (props: { let leaflets: Leaflet[] = identity ? identity.permission_token_on_homepage .filter( - (ptoh) => ptoh.permission_tokens.leaflets_to_documents.length > 0, + (ptoh) => + ptoh.permission_tokens.leaflets_to_documents && + ptoh.permission_tokens.leaflets_to_documents.document, ) .map((ptoh) => ({ added_at: ptoh.created_at, diff --git a/app/(home-pages)/looseleafs/page.tsx b/app/(home-pages)/looseleafs/page.tsx index 9ae9f7b3..1b2fc02c 100644 --- a/app/(home-pages)/looseleafs/page.tsx +++ b/app/(home-pages)/looseleafs/page.tsx @@ -34,7 +34,7 @@ export default async function Home() { (acc, tok) => { let title = tok.permission_tokens.leaflets_in_publications[0]?.title || - tok.permission_tokens.leaflets_to_documents[0]?.title; + tok.permission_tokens.leaflets_to_documents?.title; if (title) acc[tok.permission_tokens.root_entity] = title; return acc; }, diff --git a/app/[leaflet_id]/actions/HomeButton.tsx b/app/[leaflet_id]/actions/HomeButton.tsx index 54d54753..14cae86f 100644 --- a/app/[leaflet_id]/actions/HomeButton.tsx +++ b/app/[leaflet_id]/actions/HomeButton.tsx @@ -53,7 +53,7 @@ const AddToHomeButton = (props: {}) => { archived: null, permission_tokens: { ...permission_token, - leaflets_to_documents: [], + leaflets_to_documents: null, leaflets_in_publications: [], }, }); diff --git a/app/[leaflet_id]/actions/PublishButton.tsx b/app/[leaflet_id]/actions/PublishButton.tsx index 4098d1ff..2cde46a8 100644 --- a/app/[leaflet_id]/actions/PublishButton.tsx +++ b/app/[leaflet_id]/actions/PublishButton.tsx @@ -37,6 +37,7 @@ import * as base64 from "base64-js"; import { YJSFragmentToString } from "components/Blocks/TextBlock/RenderYJSFragment"; import { BlueskyLogin } from "app/login/LoginForm"; import { moveLeafletToPublication } from "actions/publications/moveLeafletToPublication"; +import { saveLeafletDraft } from "actions/publications/saveLeafletDraft"; import { AddTiny } from "components/Icons/AddTiny"; export const PublishButton = (props: { entityID: string }) => { @@ -176,7 +177,7 @@ const PublishToPublicationButton = (props: { entityID: string }) => {