-
Notifications
You must be signed in to change notification settings - Fork 4
[ENG-1545] Switch positions of node type and node title fields #907
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -109,13 +109,16 @@ const ModifyNodeDialog = ({ | |
| : allNodes.filter(excludeDefaultNodes); | ||
| }, [includeDefaultNodes]); | ||
|
|
||
| const [selectedNodeType, setSelectedNodeType] = useState(() => { | ||
| const [selectedNodeType, setSelectedNodeType] = useState< | ||
| (typeof discourseNodes)[number] | null | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| >(() => { | ||
| if (!nodeType) return null; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's make |
||
| const node = discourseNodes.find((n) => n.type === nodeType); | ||
| return node || discourseNodes[0]; | ||
| return node || null; | ||
| }); | ||
|
|
||
| const nodeFormat = useMemo(() => { | ||
| return selectedNodeType.format || ""; | ||
| return selectedNodeType?.format || ""; | ||
| }, [selectedNodeType]); | ||
|
|
||
| const referencedNode = useMemo(() => { | ||
|
|
@@ -160,6 +163,39 @@ const ModifyNodeDialog = ({ | |
| if (contentRequestIdRef.current === req && alive) { | ||
| setOptions((prev) => ({ ...prev, content: results })); | ||
| } | ||
| } else { | ||
| // Query all discourse node types in parallel | ||
| const allResults = await Promise.all( | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| discourseNodes.map(async (node) => { | ||
| const conditionUid = window.roamAlphaAPI.util.generateUID(); | ||
| const results = await fireQuery({ | ||
| returnNode: "node", | ||
| selections: [], | ||
| conditions: [ | ||
| { | ||
| source: "node", | ||
| relation: "is a", | ||
| target: node.type, | ||
| uid: conditionUid, | ||
| type: "clause", | ||
| }, | ||
| ], | ||
| }); | ||
| return results.map((r) => ({ | ||
| ...r, | ||
| discourseNodeType: node.type, | ||
| })); | ||
| }), | ||
| ); | ||
| const seen = new Set<string>(); | ||
| const deduped = allResults.flat().filter((r) => { | ||
| if (seen.has(r.uid)) return false; | ||
| seen.add(r.uid); | ||
| return true; | ||
| }); | ||
| if (contentRequestIdRef.current === req && alive) { | ||
| setOptions((prev) => ({ ...prev, content: deduped })); | ||
| } | ||
| } | ||
| } catch (error) { | ||
| if (contentRequestIdRef.current === req && alive) { | ||
|
|
@@ -224,11 +260,25 @@ const ModifyNodeDialog = ({ | |
| alive = false; | ||
| refAlive = false; | ||
| }; | ||
| }, [selectedNodeType, referencedNode]); | ||
|
|
||
| const setValue = useCallback((r: Result) => { | ||
| setContent(r); | ||
| }, []); | ||
| }, [selectedNodeType, referencedNode, discourseNodes]); | ||
|
|
||
| const setValue = useCallback( | ||
| (r: Result) => { | ||
| setContent(r); | ||
| if (!selectedNodeType && r.uid) { | ||
| const detectedType = (r as Record<string, unknown>) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this assertion necessary? It's already a |
||
| .discourseNodeType as string | undefined; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this assertion necessary? |
||
| if (detectedType) { | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| const nt = discourseNodes.find((n) => n.type === detectedType); | ||
| if (nt) { | ||
| setSelectedNodeType(nt); | ||
| setError(""); | ||
| } | ||
| } | ||
| } | ||
| }, | ||
| [selectedNodeType, discourseNodes], | ||
| ); | ||
|
|
||
| const setReferencedNodeValueCallback = useCallback((r: Result) => { | ||
| setReferencedNodeValue(r); | ||
|
|
@@ -304,9 +354,13 @@ const ModifyNodeDialog = ({ | |
|
|
||
| const onSubmit = async () => { | ||
| if (!content.text.trim()) return; | ||
| if (!selectedNodeType && !isContentLocked) { | ||
| setError("Please select a node type"); | ||
| return; | ||
| } | ||
| posthog.capture("Modify Node Dialog: Submit Triggered", { | ||
| mode, | ||
| nodeType: selectedNodeType.type, | ||
| nodeType: selectedNodeType?.type, | ||
| }); | ||
| try { | ||
| if (mode === "create") { | ||
|
|
@@ -326,7 +380,7 @@ const ModifyNodeDialog = ({ | |
| await addImageToPage({ | ||
| pageUid, | ||
| imageUrl, | ||
| configPageUid: selectedNodeType.type, | ||
| configPageUid: selectedNodeType?.type || "", | ||
| extensionAPI, | ||
| }); | ||
| } | ||
|
|
@@ -373,7 +427,7 @@ const ModifyNodeDialog = ({ | |
| } else { | ||
| formattedTitle = await getNewDiscourseNodeText({ | ||
| text: content.text.trim(), | ||
| nodeType: selectedNodeType.type, | ||
| nodeType: selectedNodeType!.type, | ||
| blockUid: sourceBlockUid, | ||
| }); | ||
| } | ||
|
|
@@ -384,7 +438,7 @@ const ModifyNodeDialog = ({ | |
| // Create new discourse node | ||
| const newPageUid = await createDiscourseNode({ | ||
| text: formattedTitle, | ||
| configPageUid: selectedNodeType.type, | ||
| configPageUid: selectedNodeType!.type, | ||
| extensionAPI, | ||
| imageUrl, | ||
| }); | ||
|
|
@@ -505,6 +559,26 @@ const ModifyNodeDialog = ({ | |
| style={{ pointerEvents: "all" }} | ||
| > | ||
| <div className={`${Classes.DIALOG_BODY} flex flex-col gap-4`}> | ||
| {/* Content Input */} | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| <div className="w-full"> | ||
| <Label>Content</Label> | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was pre-exisitng, but let's wrap the |
||
| <FuzzySelectInput | ||
| value={content} | ||
| setValue={setValue} | ||
| options={options.content} | ||
| placeholder={ | ||
| loading | ||
| ? "..." | ||
| : selectedNodeType | ||
| ? `Enter a ${selectedNodeType.text.toLowerCase()} ...` | ||
| : "Search all nodes..." | ||
| } | ||
| mode={mode} | ||
| isLocked={isContentLocked} | ||
| autoFocus={!isContentLocked} | ||
| /> | ||
| </div> | ||
|
|
||
| {/* Node Type Selector */} | ||
| <div className="flex w-full"> | ||
| <Label autoFocus={false}> | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
@@ -514,43 +588,28 @@ const ModifyNodeDialog = ({ | |
| transformItem={(t) => | ||
| discourseNodes.find((n) => n.type === t)?.text || t | ||
| } | ||
| activeItem={selectedNodeType.type} | ||
| activeItem={selectedNodeType?.type ?? null} | ||
| onItemSelect={(t) => { | ||
| const nt = discourseNodes.find((n) => n.type === t); | ||
| if (nt) { | ||
| setSelectedNodeType(nt); | ||
| setReferencedNodeValue({ text: "", uid: "" }); | ||
| setError(""); | ||
| } | ||
| }} | ||
| disabled={mode === "edit" || disableNodeTypeChange} | ||
| disabled={ | ||
| mode === "edit" || disableNodeTypeChange || isContentLocked | ||
| } | ||
| popoverProps={{ openOnTargetFocus: false }} | ||
| className={ | ||
| mode === "edit" || disableNodeTypeChange | ||
| mode === "edit" || disableNodeTypeChange || isContentLocked | ||
| ? "cursor-not-allowed opacity-50" | ||
| : "" | ||
| } | ||
| /> | ||
| </Label> | ||
| </div> | ||
|
|
||
| {/* Content Input */} | ||
| <div className="w-full"> | ||
| <Label>Content</Label> | ||
| <FuzzySelectInput | ||
| value={content} | ||
| setValue={setValue} | ||
| options={options.content} | ||
| placeholder={ | ||
| loading | ||
| ? "..." | ||
| : `Enter a ${selectedNodeType.text.toLowerCase()} ...` | ||
| } | ||
| mode={mode} | ||
| isLocked={isContentLocked} | ||
| autoFocus={!isContentLocked} | ||
| /> | ||
| </div> | ||
|
|
||
| {/* Referenced Node Input */} | ||
| {referencedNode && !isContentLocked && mode === "create" && ( | ||
| <div className="w-full"> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -189,19 +189,9 @@ export const registerCommandPaletteCommands = (onloadArgs: OnloadArgs) => { | |
|
|
||
| const selectionStart = uid ? getSelectionStartForBlock(uid) : 0; | ||
|
|
||
| const defaultNodeType = | ||
| getDiscourseNodes().filter(excludeDefaultNodes)[0]?.type; | ||
| if (!defaultNodeType) { | ||
| renderToast({ | ||
| id: "create-discourse-node-command-no-types", | ||
| content: "No discourse node types found in settings.", | ||
| }); | ||
| return; | ||
| } | ||
|
|
||
| renderModifyNodeDialog({ | ||
| mode: "create", | ||
| nodeType: defaultNodeType, | ||
| nodeType: "", | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| initialValue: { text: "", uid: "" }, | ||
| extensionAPI, | ||
| onSuccess: async (result) => { | ||
|
|
||

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have an explicit type for this:
DiscourseNode. Let's use that to be clear and direct.