diff --git a/actions/createPublicationDraft.ts b/actions/createPublicationDraft.ts index 12d67938..164a91cb 100644 --- a/actions/createPublicationDraft.ts +++ b/actions/createPublicationDraft.ts @@ -11,6 +11,12 @@ export async function createPublicationDraft(publication_uri: string) { redirectUser: false, firstBlockType: "text", }); + let { data: publication } = await supabaseServerClient + .from("publications") + .select("*") + .eq("uri", publication_uri) + .single(); + if (publication?.identity_did !== identity.atp_did) return; await supabaseServerClient .from("leaflets_in_publications") diff --git a/actions/publications/moveLeafletToPublication.ts b/actions/publications/moveLeafletToPublication.ts new file mode 100644 index 00000000..a3ee8c62 --- /dev/null +++ b/actions/publications/moveLeafletToPublication.ts @@ -0,0 +1,33 @@ +"use server"; + +import { getIdentityData } from "actions/getIdentityData"; +import { supabaseServerClient } from "supabase/serverClient"; + +export async function moveLeafletToPublication( + leaflet_id: string, + publication_uri: string, + metadata: { title: string; description: string }, + entitiesToDelete: string[], +) { + let identity = await getIdentityData(); + if (!identity || !identity.atp_did) return null; + let { data: publication } = await supabaseServerClient + .from("publications") + .select("*") + .eq("uri", publication_uri) + .single(); + if (publication?.identity_did !== identity.atp_did) return; + + await supabaseServerClient.from("leaflets_in_publications").insert({ + publication: publication_uri, + leaflet: leaflet_id, + doc: null, + title: metadata.title, + description: metadata.description, + }); + + await supabaseServerClient + .from("entities") + .delete() + .in("id", entitiesToDelete); +} diff --git a/actions/publications/updateLeafletDraftMetadata.ts b/actions/publications/updateLeafletDraftMetadata.ts deleted file mode 100644 index c15fb1eb..00000000 --- a/actions/publications/updateLeafletDraftMetadata.ts +++ /dev/null @@ -1,26 +0,0 @@ -"use server"; - -import { getIdentityData } from "actions/getIdentityData"; -import { supabaseServerClient } from "supabase/serverClient"; - -export async function updateLeafletDraftMetadata( - leafletID: string, - publication_uri: string, - title: string, - description: string, -) { - let identity = await getIdentityData(); - if (!identity?.atp_did) return null; - let { data: publication } = await supabaseServerClient - .from("publications") - .select() - .eq("uri", publication_uri) - .single(); - if (!publication || publication.identity_did !== identity.atp_did) - return null; - await supabaseServerClient - .from("leaflets_in_publications") - .update({ title, description }) - .eq("leaflet", leafletID) - .eq("publication", publication_uri); -} diff --git a/app/[leaflet_id]/Footer.tsx b/app/[leaflet_id]/Footer.tsx index 82e67bef..94a5a9a3 100644 --- a/app/[leaflet_id]/Footer.tsx +++ b/app/[leaflet_id]/Footer.tsx @@ -46,7 +46,7 @@ export function LeafletFooter(props: { entityID: string }) { )} - + diff --git a/app/[leaflet_id]/actions/PublishButton.tsx b/app/[leaflet_id]/actions/PublishButton.tsx index a645f2be..295a2bc5 100644 --- a/app/[leaflet_id]/actions/PublishButton.tsx +++ b/app/[leaflet_id]/actions/PublishButton.tsx @@ -1,9 +1,11 @@ +"use client"; import { publishToPublication } from "actions/publishToPublication"; import { getPublicationURL } from "app/lish/createPub/getPublicationURL"; import { ActionButton } from "components/ActionBar/ActionButton"; import { PubIcon, PubListEmptyContent, + PubListEmptyIllo, } from "components/ActionBar/Publications"; import { ButtonPrimary, ButtonTertiary } from "components/Buttons"; import { AddSmall } from "components/Icons/AddSmall"; @@ -12,13 +14,16 @@ import { PublishSmall } from "components/Icons/PublishSmall"; import { useIdentityData } from "components/IdentityProvider"; import { InputWithLabel } from "components/Input"; import { Menu, MenuItem } from "components/Layout"; -import { useLeafletPublicationData } from "components/PageSWRDataProvider"; +import { + useLeafletDomains, + useLeafletPublicationData, +} from "components/PageSWRDataProvider"; import { Popover } from "components/Popover"; import { SpeedyLink } from "components/SpeedyLink"; import { useToaster } from "components/Toast"; import { DotLoader } from "components/utils/DotLoader"; import { PubLeafletPublication } from "lexicons/api"; -import { useParams, useRouter } from "next/navigation"; +import { useParams, useRouter, useSearchParams } from "next/navigation"; import { useState, useMemo } from "react"; import { useIsMobile } from "src/hooks/isMobile"; import { useReplicache, useEntity } from "src/replicache"; @@ -27,6 +32,8 @@ import { useBlocks } from "src/hooks/queries/useBlocks"; import * as Y from "yjs"; 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"; export const PublishButton = (props: { entityID: string }) => { let { data: pub } = useLeafletPublicationData(); @@ -92,14 +99,23 @@ const UpdateButton = () => { const PublishToPublicationButton = (props: { entityID: string }) => { let { identity } = useIdentityData(); + let { permission_token } = useReplicache(); + let query = useSearchParams(); + console.log(query.get("publish")); + let [open, setOpen] = useState(query.get("publish") !== null); let isMobile = useIsMobile(); identity && identity.atp_did && identity.publications.length > 0; let [selectedPub, setSelectedPub] = useState(undefined); + let router = useRouter(); + let { title, entitiesToDelete } = useTitle(props.entityID); + let [description, setDescription] = useState(""); return ( setOpen(o)} side={isMobile ? "top" : "right"} align={isMobile ? "center" : "start"} className="sm:max-w-sm w-[1000px]" @@ -112,14 +128,36 @@ const PublishToPublicationButton = (props: { entityID: string }) => { } > {!identity || !identity.atp_did ? ( - // this component is also used on Home to populate the sidebar when PubList is empty - // when user doesn't have an AT Proto account, and redirects back to the doc (hopefully with publish open?
- +
+
+ +
+
Publish on AT Proto
+ { + <> +
+ Link a Bluesky account to start
a publishing on AT + Proto +
+ + + + } +
) : (
- +
{
- Save as Draft - + + { + if (!selectedPub) return; + e.preventDefault(); + if (selectedPub === "create") return; + router.push( + `${permission_token.id}/publish?publication_uri=${encodeURIComponent(selectedPub)}&title=${encodeURIComponent(title)}&description=${encodeURIComponent(description)}`, + ); + }} + > Next{selectedPub === "create" && ": Create Pub!"}
@@ -142,33 +195,54 @@ const PublishToPublicationButton = (props: { entityID: string }) => { ); }; -const PostDetailsForm = (props: { entityID: string }) => { - let [description, setDescription] = useState(""); - - let rootPage = useEntity(props.entityID, "root/page")[0].data.value; - let firstBlock = useBlocks(rootPage)[0]; - let firstBlockText = useEntity(firstBlock?.value, "block/text")?.data.value; +const SaveAsDraftButton = (props: { + selectedPub: string | undefined; + leafletId: string; + metadata: { title: string; description: string }; + entitiesToDelete: string[]; +}) => { + let { mutate } = useLeafletPublicationData(); + let { rep } = useReplicache(); + let [isLoading, setIsLoading] = useState(false); - const leafletTitle = useMemo(() => { - if (!firstBlockText) return "Untitled"; - let doc = new Y.Doc(); - const update = base64.toByteArray(firstBlockText); - Y.applyUpdate(doc, update); - let nodes = doc.getXmlElement("prosemirror").toArray(); - return YJSFragmentToString(nodes[0]) || "Untitled"; - }, [firstBlockText]); + return ( + { + if (!props.selectedPub) return; + if (props.selectedPub === "create") return; + e.preventDefault(); + setIsLoading(true); + await moveLeafletToPublication( + props.leafletId, + props.selectedPub, + props.metadata, + props.entitiesToDelete, + ); + await Promise.all([rep?.pull(), mutate()]); + setIsLoading(false); + }} + > + {isLoading ? : "Save as Draft"} + + ); +}; +const PostDetailsForm = (props: { + title: string; + description: string; + setDescription: (d: string) => void; +}) => { return (
Post Details
- + setDescription(e.currentTarget.value)} + onChange={(e) => props.setDescription(e.currentTarget.value)} />
@@ -271,3 +345,40 @@ const PubOption = (props: { ); }; + +let useTitle = (entityID: string) => { + let rootPage = useEntity(entityID, "root/page")[0].data.value; + let firstBlock = useBlocks(rootPage)[0]?.value; + + let firstBlockText = useEntity(firstBlock, "block/text")?.data.value; + + const leafletTitle = useMemo(() => { + if (!firstBlockText) return "Untitled"; + let doc = new Y.Doc(); + const update = base64.toByteArray(firstBlockText); + Y.applyUpdate(doc, update); + let nodes = doc.getXmlElement("prosemirror").toArray(); + return YJSFragmentToString(nodes[0]) || "Untitled"; + }, [firstBlockText]); + + let secondBlock = useBlocks(rootPage)[1]; + let secondBlockTextValue = useEntity(secondBlock.value, "block/text")?.data + .value; + const secondBlockText = useMemo(() => { + if (!secondBlockTextValue) return ""; + let doc = new Y.Doc(); + const update = base64.toByteArray(secondBlockTextValue); + Y.applyUpdate(doc, update); + let nodes = doc.getXmlElement("prosemirror").toArray(); + return YJSFragmentToString(nodes[0]) || ""; + }, [firstBlockText]); + + let entitiesToDelete = useMemo(() => { + let etod = [firstBlock]; + if (secondBlockText.trim() === "" && secondBlock.type === "text") + etod.push(secondBlock.value); + return etod; + }, [firstBlock, secondBlockText, secondBlock]); + + return { title: leafletTitle, entitiesToDelete }; +}; diff --git a/app/[leaflet_id]/publish/page.tsx b/app/[leaflet_id]/publish/page.tsx index 578afb69..592990ff 100644 --- a/app/[leaflet_id]/publish/page.tsx +++ b/app/[leaflet_id]/publish/page.tsx @@ -13,6 +13,11 @@ export const fetchCache = "force-no-store"; type Props = { // this is now a token id not leaflet! Should probs rename params: Promise<{ leaflet_id: string }>; + searchParams: Promise<{ + publication_uri: string; + title: string; + description: string; + }>; }; export default async function PublishLeafletPage(props: Props) { let leaflet_id = (await props.params).leaflet_id; @@ -32,7 +37,20 @@ export default async function PublishLeafletPage(props: Props) { .eq("id", leaflet_id) .single(); let rootEntity = data?.root_entity; - if (!data || !rootEntity || !data.leaflets_in_publications[0]) + let publication = data?.leaflets_in_publications[0]?.publications; + if (!publication) { + let pub_uri = (await props.searchParams).publication_uri; + if (!pub_uri) return; + console.log(decodeURIComponent(pub_uri)); + let { data, error } = await supabaseServerClient + .from("publications") + .select("*, documents_in_publications(count)") + .eq("uri", decodeURIComponent(pub_uri)) + .single(); + console.log(error); + publication = data; + } + if (!data || !rootEntity || !publication) return (
missin something @@ -42,7 +60,12 @@ export default async function PublishLeafletPage(props: Props) { let identity = await getIdentityData(); if (!identity || !identity.atp_did) return null; - let pub = data.leaflets_in_publications[0]; + let title = + data.leaflets_in_publications[0]?.title || + decodeURIComponent((await props.searchParams).title); + let description = + data.leaflets_in_publications[0]?.description || + decodeURIComponent((await props.searchParams).description); let agent = new AtpAgent({ service: "https://public.api.bsky.app" }); let profile = await agent.getProfile({ actor: identity.atp_did }); @@ -57,11 +80,11 @@ export default async function PublishLeafletPage(props: Props) { leaflet_id={leaflet_id} root_entity={rootEntity} profile={profile.data} - title={pub.title} - publication_uri={pub.publication} - description={pub.description} - record={pub.publications?.record as PubLeafletPublication.Record} - posts_in_pub={pub.publications?.documents_in_publications[0].count} + title={title} + description={description} + publication_uri={publication.uri} + record={publication.record as PubLeafletPublication.Record} + posts_in_pub={publication.documents_in_publications[0].count} /> );