From 7537500721d796f581af7a855a5137dac7409b92 Mon Sep 17 00:00:00 2001 From: sid597 Date: Thu, 6 Nov 2025 00:15:17 +0530 Subject: [PATCH 1/2] Add duplicate node alert during nod creation via modal --- apps/roam/src/components/CreateNodeDialog.tsx | 94 ++++++++++- apps/roam/src/utils/hyde.ts | 151 ++++++++++++++++++ 2 files changed, 239 insertions(+), 6 deletions(-) diff --git a/apps/roam/src/components/CreateNodeDialog.tsx b/apps/roam/src/components/CreateNodeDialog.tsx index 4211f7c56..9e64d2e46 100644 --- a/apps/roam/src/components/CreateNodeDialog.tsx +++ b/apps/roam/src/components/CreateNodeDialog.tsx @@ -12,6 +12,8 @@ import getDiscourseNodes, { import { getNewDiscourseNodeText } from "~/utils/formatUtils"; import MenuItemSelect from "roamjs-components/components/MenuItemSelect"; import createBlock from "roamjs-components/writes/createBlock"; +import { findSimilarNodes, SuggestedNode } from "~/utils/hyde"; +import { Spinner } from "@blueprintjs/core"; export type CreateNodeDialogProps = { onClose: () => void; @@ -37,6 +39,9 @@ const CreateNodeDialog = ({ const [selectedType, setSelectedType] = useState(defaultNodeType); const [loading, setLoading] = useState(false); + const [suggestions, setSuggestions] = useState([]); + const [suggestionsLoading, setSuggestionsLoading] = useState(false); + const [formattedTitle, setFormattedTitle] = useState(""); const inputRef = useRef(null); useEffect(() => { @@ -45,16 +50,53 @@ const CreateNodeDialog = ({ } }, []); + useEffect(() => { + let isCancelled = false; + const compute = async () => { + const base = title.trim(); + if (!base) { + setFormattedTitle(""); + return; + } + const ft = await getNewDiscourseNodeText({ + text: base, + nodeType: selectedType.type, + blockUid: sourceBlockUid, + }); + if (!isCancelled) setFormattedTitle(ft || ""); + }; + void compute(); + return () => { + isCancelled = true; + }; + }, [title, selectedType.type, sourceBlockUid]); + + useEffect(() => { + const fetchSuggestions = async () => { + if (formattedTitle.trim()) { + setSuggestionsLoading(true); + console.log( + "fetching suggestions for", + formattedTitle, + selectedType.type, + ); + const similarNodes = await findSimilarNodes({ + text: formattedTitle, + nodeType: selectedType.type, + }); + setSuggestions(similarNodes); + setSuggestionsLoading(false); + } else { + setSuggestions([]); + } + }; + void fetchSuggestions(); + }, [formattedTitle, selectedType.type]); + const onCreate = async () => { if (!title.trim()) return; setLoading(true); - const formattedTitle = await getNewDiscourseNodeText({ - text: title.trim(), - nodeType: selectedType.type, - blockUid: sourceBlockUid, - }); - if (!formattedTitle) { setLoading(false); return; @@ -138,6 +180,46 @@ const CreateNodeDialog = ({ /> + {suggestionsLoading && ( +
+ + Fetching possible duplicates... +
+ )} + {suggestions.length > 0 && ( +
+

Possible duplicates

+ +
+ )} +