From 07650633a3f8081d41f04a799546b348d98762f8 Mon Sep 17 00:00:00 2001 From: sid597 Date: Wed, 7 May 2025 18:38:51 +0530 Subject: [PATCH 1/4] ask user for confirmation, delete corresponding relations --- .../settings/DiscourseNodeConfigPanel.tsx | 87 +++++++++++++++++-- 1 file changed, 79 insertions(+), 8 deletions(-) diff --git a/apps/roam/src/components/settings/DiscourseNodeConfigPanel.tsx b/apps/roam/src/components/settings/DiscourseNodeConfigPanel.tsx index 5b649438c..2f60026f6 100644 --- a/apps/roam/src/components/settings/DiscourseNodeConfigPanel.tsx +++ b/apps/roam/src/components/settings/DiscourseNodeConfigPanel.tsx @@ -1,4 +1,5 @@ import { + Alert, Button, ControlGroup, InputGroup, @@ -12,6 +13,8 @@ import refreshConfigTree from "~/utils/refreshConfigTree"; import createPage from "roamjs-components/writes/createPage"; import type { CustomField } from "roamjs-components/components/ConfigPanels/types"; import posthog from "posthog-js"; +import getDiscourseRelations from "~/utils/getDiscourseRelations"; +import { deleteBlock } from "roamjs-components/writes"; type DiscourseNodeConfigPanelProps = React.ComponentProps< CustomField["options"]["component"] @@ -32,6 +35,12 @@ const DiscourseNodeConfigPanel: React.FC = ({ null, ); + const [isAlertOpen, setIsAlertOpen] = useState(false); + const [alertMessage, setAlertMessage] = useState(""); + const [alertConfirmAction, setAlertConfirmAction] = useState< + () => Promise + >(() => Promise.resolve()); + const navigateToNode = (uid: string) => { if (isPopup) { setSelectedTabId(uid); @@ -40,6 +49,47 @@ const DiscourseNodeConfigPanel: React.FC = ({ } }; + const handleDeleteNodeTypeWithConfirmation = ( + nodeTypeIdToDelete: string, + nodeLabel: string, + ) => { + const affectedRelations = getDiscourseRelations().filter( + (r) => + r.source === nodeTypeIdToDelete || r.destination === nodeTypeIdToDelete, + ); + + let dialogMessage = `Are you sure you want to delete the Node Type "${nodeLabel}"?`; + + if (affectedRelations.length > 0) { + dialogMessage = `The Node Type "${nodeLabel}" is used by the following relations, which will also be deleted:\n\n${affectedRelations + .map((r) => { + const sourceNodeDetails = nodes.find((s) => s.type === r.source); + const destinationNodeDetails = nodes.find( + (d) => d.type === r.destination, + ); + return `- ${sourceNodeDetails?.text || r.source} ${r.label} ${destinationNodeDetails?.text || r.destination}`; + }) + .join("\n")}\n\nProceed with deletion?`; + } + + setAlertMessage(dialogMessage); + setAlertConfirmAction(() => async () => { + for (const rel of affectedRelations) { + await deleteBlock(rel.id); + } + await window.roamAlphaAPI + .deletePage({ page: { uid: nodeTypeIdToDelete } }) + .then(() => { + setNodes((prevNodes) => + prevNodes.filter((nn) => nn.type !== nodeTypeIdToDelete), + ); + refreshConfigTree(); + }); + setDeleteConfirmation(null); + }); + setIsAlertOpen(true); + }; + return ( <> @@ -121,8 +171,11 @@ const DiscourseNodeConfigPanel: React.FC = ({ icon="trash" minimal onClick={() => { - if (deleteConfirmation) setDeleteConfirmation(null); - else setDeleteConfirmation(n.type); + if (deleteConfirmation === n.type) { + setDeleteConfirmation(null); + } else { + setDeleteConfirmation(n.type); + } }} /> @@ -130,12 +183,7 @@ const DiscourseNodeConfigPanel: React.FC = ({ children="Confirm" intent={Intent.DANGER} onClick={() => { - window.roamAlphaAPI - .deletePage({ page: { uid: n.type } }) - .then(() => { - setNodes(nodes.filter((nn) => nn.type !== n.type)); - refreshConfigTree(); - }); + handleDeleteNodeTypeWithConfirmation(n.type, n.text); }} className={`mx-1 ${ deleteConfirmation !== n.type ? "opacity-0" : "" @@ -153,6 +201,29 @@ const DiscourseNodeConfigPanel: React.FC = ({ ))} + + { + if (alertConfirmAction) { + await alertConfirmAction(); + } + setIsAlertOpen(false); + }} + onCancel={() => { + setIsAlertOpen(false); + setDeleteConfirmation(null); + }} + intent={Intent.DANGER} + confirmButtonText="Delete" + cancelButtonText="Cancel" + canEscapeKeyCancel={true} + canOutsideClickCancel={true} + > +
+ {alertMessage} +
+
); }; From 4782c5d45c8af8cc21b3348ee2beefa9bd697b96 Mon Sep 17 00:00:00 2001 From: sid597 Date: Wed, 7 May 2025 18:45:27 +0530 Subject: [PATCH 2/4] address review --- .../settings/DiscourseNodeConfigPanel.tsx | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/apps/roam/src/components/settings/DiscourseNodeConfigPanel.tsx b/apps/roam/src/components/settings/DiscourseNodeConfigPanel.tsx index 2f60026f6..2ca4af2e7 100644 --- a/apps/roam/src/components/settings/DiscourseNodeConfigPanel.tsx +++ b/apps/roam/src/components/settings/DiscourseNodeConfigPanel.tsx @@ -74,18 +74,31 @@ const DiscourseNodeConfigPanel: React.FC = ({ setAlertMessage(dialogMessage); setAlertConfirmAction(() => async () => { - for (const rel of affectedRelations) { - await deleteBlock(rel.id); - } - await window.roamAlphaAPI - .deletePage({ page: { uid: nodeTypeIdToDelete } }) - .then(() => { - setNodes((prevNodes) => - prevNodes.filter((nn) => nn.type !== nodeTypeIdToDelete), - ); - refreshConfigTree(); + try { + for (const rel of affectedRelations) { + await deleteBlock(rel.id).catch((error) => { + console.error( + `Failed to delete relation: ${rel.id}, ${error.message}`, + ); + throw error; + }); + } + await window.roamAlphaAPI.deletePage({ + page: { uid: nodeTypeIdToDelete }, }); - setDeleteConfirmation(null); + + setNodes((prevNodes) => + prevNodes.filter((nn) => nn.type !== nodeTypeIdToDelete), + ); + refreshConfigTree(); + setDeleteConfirmation(null); + } catch (error) { + console.error( + `Failed to complete deletion for Node Type ${nodeLabel} (UID: ${nodeTypeIdToDelete}): ${error instanceof Error ? error.message : String(error)}`, + ); + } finally { + setIsAlertOpen(false); + } }); setIsAlertOpen(true); }; @@ -208,7 +221,6 @@ const DiscourseNodeConfigPanel: React.FC = ({ if (alertConfirmAction) { await alertConfirmAction(); } - setIsAlertOpen(false); }} onCancel={() => { setIsAlertOpen(false); From f271a070d97d688f64c8aed4740d0a2bd0caa38b Mon Sep 17 00:00:00 2001 From: sid597 Date: Wed, 7 May 2025 18:49:52 +0530 Subject: [PATCH 3/4] address review --- .../components/settings/DiscourseNodeConfigPanel.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/roam/src/components/settings/DiscourseNodeConfigPanel.tsx b/apps/roam/src/components/settings/DiscourseNodeConfigPanel.tsx index 2ca4af2e7..f1953598d 100644 --- a/apps/roam/src/components/settings/DiscourseNodeConfigPanel.tsx +++ b/apps/roam/src/components/settings/DiscourseNodeConfigPanel.tsx @@ -193,7 +193,6 @@ const DiscourseNodeConfigPanel: React.FC = ({ /> ))} From 29eee50a84af1fa236fabff92b33a50b9f1c7064 Mon Sep 17 00:00:00 2001 From: sid597 Date: Thu, 8 May 2025 17:37:05 +0530 Subject: [PATCH 4/4] address comments --- .../settings/DiscourseNodeConfigPanel.tsx | 109 ++++++++---------- 1 file changed, 51 insertions(+), 58 deletions(-) diff --git a/apps/roam/src/components/settings/DiscourseNodeConfigPanel.tsx b/apps/roam/src/components/settings/DiscourseNodeConfigPanel.tsx index f1953598d..cb1d0295f 100644 --- a/apps/roam/src/components/settings/DiscourseNodeConfigPanel.tsx +++ b/apps/roam/src/components/settings/DiscourseNodeConfigPanel.tsx @@ -37,10 +37,8 @@ const DiscourseNodeConfigPanel: React.FC = ({ const [isAlertOpen, setIsAlertOpen] = useState(false); const [alertMessage, setAlertMessage] = useState(""); - const [alertConfirmAction, setAlertConfirmAction] = useState< - () => Promise - >(() => Promise.resolve()); - + const [affectedRelations, setAffectedRelations] = useState([]); + const [nodeTypeIdToDelete, setNodeTypeIdToDelete] = useState(""); const navigateToNode = (uid: string) => { if (isPopup) { setSelectedTabId(uid); @@ -49,58 +47,13 @@ const DiscourseNodeConfigPanel: React.FC = ({ } }; - const handleDeleteNodeTypeWithConfirmation = ( - nodeTypeIdToDelete: string, - nodeLabel: string, - ) => { - const affectedRelations = getDiscourseRelations().filter( - (r) => - r.source === nodeTypeIdToDelete || r.destination === nodeTypeIdToDelete, - ); - - let dialogMessage = `Are you sure you want to delete the Node Type "${nodeLabel}"?`; - - if (affectedRelations.length > 0) { - dialogMessage = `The Node Type "${nodeLabel}" is used by the following relations, which will also be deleted:\n\n${affectedRelations - .map((r) => { - const sourceNodeDetails = nodes.find((s) => s.type === r.source); - const destinationNodeDetails = nodes.find( - (d) => d.type === r.destination, - ); - return `- ${sourceNodeDetails?.text || r.source} ${r.label} ${destinationNodeDetails?.text || r.destination}`; - }) - .join("\n")}\n\nProceed with deletion?`; - } - - setAlertMessage(dialogMessage); - setAlertConfirmAction(() => async () => { - try { - for (const rel of affectedRelations) { - await deleteBlock(rel.id).catch((error) => { - console.error( - `Failed to delete relation: ${rel.id}, ${error.message}`, - ); - throw error; - }); - } - await window.roamAlphaAPI.deletePage({ - page: { uid: nodeTypeIdToDelete }, - }); - - setNodes((prevNodes) => - prevNodes.filter((nn) => nn.type !== nodeTypeIdToDelete), - ); - refreshConfigTree(); - setDeleteConfirmation(null); - } catch (error) { - console.error( - `Failed to complete deletion for Node Type ${nodeLabel} (UID: ${nodeTypeIdToDelete}): ${error instanceof Error ? error.message : String(error)}`, - ); - } finally { - setIsAlertOpen(false); - } + const deleteNodeType = async (uid: string) => { + await window.roamAlphaAPI.deletePage({ + page: { uid }, }); - setIsAlertOpen(true); + setNodes((prevNodes) => prevNodes.filter((nn) => nn.type !== uid)); + refreshConfigTree(); + setDeleteConfirmation(null); }; return ( @@ -195,7 +148,31 @@ const DiscourseNodeConfigPanel: React.FC = ({