diff --git a/actions/domains/assignDomainToDocument.ts b/actions/domains/assignDomainToDocument.ts index 100cf842..6eaa1d10 100644 --- a/actions/domains/assignDomainToDocument.ts +++ b/actions/domains/assignDomainToDocument.ts @@ -24,11 +24,13 @@ export async function assignDomainToDocument({ if (!identity || !identity.custom_domains.find((d) => d.domain === domain)) return null; - await supabase.from("publication_domains").delete().eq("domain", domain); - await supabase - .from("custom_domain_routes") - .delete() - .eq("edit_permission_token", edit_permission_token); + await Promise.all([ + supabase.from("publication_domains").delete().eq("domain", domain), + supabase + .from("custom_domain_routes") + .delete() + .eq("edit_permission_token", edit_permission_token), + ]); await supabase.from("custom_domain_routes").insert({ domain, diff --git a/actions/domains/assignDomainToPublication.ts b/actions/domains/assignDomainToPublication.ts index 6829f74a..9f35e46c 100644 --- a/actions/domains/assignDomainToPublication.ts +++ b/actions/domains/assignDomainToPublication.ts @@ -27,8 +27,10 @@ export async function assignDomainToPublication({ .single(); if (publication?.identity_did !== identity.atp_did) return null; - await supabase.from("custom_domain_routes").delete().eq("domain", domain); - await supabase.from("publication_domains").delete().eq("domain", domain); + await Promise.all([ + supabase.from("custom_domain_routes").delete().eq("domain", domain), + supabase.from("publication_domains").delete().eq("domain", domain), + ]); await supabase.from("publication_domains").insert({ publication: publication_uri, diff --git a/actions/domains/removeDomainAssignment.ts b/actions/domains/removeDomainAssignment.ts index 4f3ffa48..f35970d7 100644 --- a/actions/domains/removeDomainAssignment.ts +++ b/actions/domains/removeDomainAssignment.ts @@ -18,8 +18,10 @@ export async function removeDomainAssignment({ if (!identity || !identity.custom_domains.find((d) => d.domain === domain)) return null; - await supabase.from("custom_domain_routes").delete().eq("domain", domain); - await supabase.from("publication_domains").delete().eq("domain", domain); + await Promise.all([ + supabase.from("custom_domain_routes").delete().eq("domain", domain), + supabase.from("publication_domains").delete().eq("domain", domain), + ]); return true; } diff --git a/actions/domains/removeDomainRoute.ts b/actions/domains/removeDomainRoute.ts new file mode 100644 index 00000000..6a40a5fb --- /dev/null +++ b/actions/domains/removeDomainRoute.ts @@ -0,0 +1,25 @@ +"use server"; +import { Database } from "supabase/database.types"; +import { createServerClient } from "@supabase/ssr"; +import { getIdentityData } from "actions/getIdentityData"; + +let supabase = createServerClient( + process.env.NEXT_PUBLIC_SUPABASE_API_URL as string, + process.env.SUPABASE_SERVICE_ROLE_KEY as string, + { cookies: {} }, +); + +export async function removeDomainRoute({ routeId }: { routeId: string }) { + let identity = await getIdentityData(); + if (!identity) return null; + + // Verify the route belongs to one of the user's domains + let allRoutes = identity.custom_domains.flatMap( + (d) => d.custom_domain_routes, + ); + if (!allRoutes.find((r) => r.id === routeId)) return null; + + await supabase.from("custom_domain_routes").delete().eq("id", routeId); + + return true; +} diff --git a/app/[leaflet_id]/actions/ShareOptions/DomainOptions.tsx b/app/[leaflet_id]/actions/ShareOptions/DomainOptions.tsx index 5aedc86d..0338edde 100644 --- a/app/[leaflet_id]/actions/ShareOptions/DomainOptions.tsx +++ b/app/[leaflet_id]/actions/ShareOptions/DomainOptions.tsx @@ -2,7 +2,10 @@ import { useState } from "react"; import { ButtonPrimary } from "components/Buttons"; import { useToaster } from "components/Toast"; import { Input } from "components/Input"; -import { useIdentityData } from "components/IdentityProvider"; +import { + useIdentityData, + mutateIdentityData, +} from "components/IdentityProvider"; import { useDomainStatus } from "components/Domains/useDomainStatus"; import { CustomDomain } from "components/Domains/DomainList"; import { @@ -85,25 +88,36 @@ const DomainOptions = (props: { let toaster = useToaster(); let publishLink = useReadOnlyShareLink(); - // Find the CustomDomain object for the selected domain to check its assignment - let selectedDomainData = identity?.custom_domains.find( - (d: CustomDomain) => d.domain === selectedDomain, + // Filter out domains assigned to publications — they can't be used for leaflets + let availableDomains = (identity?.custom_domains || []).filter( + (d: CustomDomain) => d.publication_domains.length === 0, ); - let selectedAssignment = selectedDomainData - ? getDomainAssignment(selectedDomainData) - : null; - let willReassign = - selectedAssignment && selectedAssignment.type !== "unassigned"; - - let [confirmingReassign, setConfirmingReassign] = useState(false); async function doPublish() { if (!selectedDomain || !publishLink) return; - await assignDomainToDocument({ - domain: selectedDomain, - route: "/" + selectedRoute, - view_permission_token: publishLink, - edit_permission_token: permission_token.id, + let route = "/" + selectedRoute; + + // Optimistic update + mutateIdentityData(mutateIdentity, (draft) => { + let domain = draft.custom_domains.find( + (d) => d.domain === selectedDomain, + ); + if (domain) { + // Remove existing route for this leaflet, add new one + domain.custom_domain_routes = [ + ...domain.custom_domain_routes.filter( + (r) => r.edit_permission_token !== permission_token.id, + ), + { + id: crypto.randomUUID(), + domain: selectedDomain, + route, + view_permission_token: publishLink, + edit_permission_token: permission_token.id, + created_at: new Date().toISOString(), + }, + ]; + } }); toaster({ @@ -121,17 +135,22 @@ const DomainOptions = (props: { ), type: "success", }); - mutateIdentity(); mutateDomains(); - setConfirmingReassign(false); props.setShareMenuState("default"); + + await assignDomainToDocument({ + domain: selectedDomain, + route, + view_permission_token: publishLink, + edit_permission_token: permission_token.id, + }); } return (

Choose a Domain

- {identity?.custom_domains.map((domain: CustomDomain) => { + {availableDomains.map((domain: CustomDomain) => { return ( { - setSelectedDomain(d); - setConfirmingReassign(false); - }} + setChecked={(d) => setSelectedDomain(d)} setDomainMenuState={props.setDomainMenuState} /> ); @@ -157,67 +173,38 @@ const DomainOptions = (props: {
- {confirmingReassign && selectedAssignment && ( -
-

- {selectedDomain} is currently assigned to{" "} - {describeAssignment(selectedAssignment)}. Publishing here will - remove that assignment. -

-
- - - Reassign & Publish - -
-
- )} - - {!confirmingReassign && ( -
- {props.domainConnected && ( - - )} - - { - if (willReassign) { - setConfirmingReassign(true); - return; - } - await doPublish(); +
+ {props.domainConnected && ( +
- )} + Unpublish + + )} + + + Publish! + +
); }; @@ -233,7 +220,6 @@ const DomainOption = (props: { let [value, setValue] = useState(""); let { pending } = useDomainStatus(props.domain.domain); let assignment = getDomainAssignment(props.domain); - let assignedElsewhere = assignment.type !== "unassigned"; return (