|
1 | 1 | import React from "react"; |
2 | | -import getSubTree from "roamjs-components/util/getSubTree"; |
3 | | -import createBlock from "roamjs-components/writes/createBlock"; |
4 | 2 | import { Checkbox } from "@blueprintjs/core"; |
5 | | -import getBasicTreeByParentUid from "roamjs-components/queries/getBasicTreeByParentUid"; |
6 | | -import deleteBlock from "roamjs-components/writes/deleteBlock"; |
7 | 3 | import getDiscourseNodes from "~/utils/getDiscourseNodes"; |
8 | 4 | import getDiscourseNodeFormatExpression from "~/utils/getDiscourseNodeFormatExpression"; |
9 | | -import QueryEditor from "~/components/QueryEditor"; |
| 5 | +import DiscourseNodeQueryEditor from "./components/DiscourseNodeQueryEditor"; |
| 6 | +import { |
| 7 | + getDiscourseNodeSetting, |
| 8 | + setDiscourseNodeSetting, |
| 9 | +} from "./utils/accessors"; |
| 10 | +import type { Condition } from "~/utils/types"; |
| 11 | + |
| 12 | +const generateUID = (): string => |
| 13 | + window.roamAlphaAPI?.util?.generateUID?.() ?? |
| 14 | + Math.random().toString(36).substring(2, 11); |
10 | 15 |
|
11 | 16 | const NodeSpecification = ({ |
12 | | - parentUid, |
13 | 17 | node, |
14 | 18 | }: { |
15 | | - parentUid: string; |
16 | 19 | node: ReturnType<typeof getDiscourseNodes>[number]; |
17 | 20 | }) => { |
18 | | - const [migrated, setMigrated] = React.useState(false); |
19 | | - const [enabled, setEnabled] = React.useState( |
20 | | - () => |
21 | | - getSubTree({ tree: getBasicTreeByParentUid(parentUid), key: "enabled" }) |
22 | | - ?.uid, |
23 | | - ); |
24 | | - React.useEffect(() => { |
25 | | - if (enabled) { |
26 | | - const scratchNode = getSubTree({ parentUid, key: "scratch" }); |
27 | | - if ( |
28 | | - !scratchNode.children.length || |
29 | | - !getSubTree({ tree: scratchNode.children, key: "conditions" }).children |
30 | | - .length |
31 | | - ) { |
32 | | - const conditionsUid = getSubTree({ |
33 | | - parentUid: scratchNode.uid, |
34 | | - key: "conditions", |
35 | | - }).uid; |
36 | | - const returnUid = getSubTree({ |
37 | | - parentUid: scratchNode.uid, |
38 | | - key: "return", |
39 | | - }).uid; |
40 | | - createBlock({ |
41 | | - parentUid: returnUid, |
42 | | - node: { |
43 | | - text: node.text, |
44 | | - }, |
45 | | - }) |
46 | | - .then(() => |
47 | | - createBlock({ |
48 | | - parentUid: conditionsUid, |
49 | | - node: { |
50 | | - text: "clause", |
51 | | - children: [ |
52 | | - { text: "source", children: [{ text: node.text }] }, |
53 | | - { text: "relation", children: [{ text: "has title" }] }, |
54 | | - { |
55 | | - text: "target", |
56 | | - children: [ |
57 | | - { |
58 | | - text: `/${ |
59 | | - getDiscourseNodeFormatExpression(node.format).source |
60 | | - }/`, |
61 | | - }, |
62 | | - ], |
63 | | - }, |
64 | | - ], |
65 | | - }, |
66 | | - }), |
67 | | - ) |
68 | | - .then(() => setMigrated(true)); |
| 21 | + const nodeType = node.type; |
| 22 | + |
| 23 | + const [enabled, setEnabled] = React.useState(() => { |
| 24 | + const spec = getDiscourseNodeSetting<Condition[]>(nodeType, [ |
| 25 | + "specification", |
| 26 | + ]); |
| 27 | + return spec !== null && spec !== undefined && spec.length > 0; |
| 28 | + }); |
| 29 | + |
| 30 | + const createInitialCondition = React.useCallback((): Condition => { |
| 31 | + return { |
| 32 | + uid: generateUID(), |
| 33 | + type: "clause", |
| 34 | + source: node.text, |
| 35 | + relation: "has title", |
| 36 | + target: `/${getDiscourseNodeFormatExpression(node.format).source}/`, |
| 37 | + }; |
| 38 | + }, [node.text, node.format]); |
| 39 | + |
| 40 | + const handleEnabledChange = React.useCallback( |
| 41 | + (e: React.FormEvent<HTMLInputElement>) => { |
| 42 | + const flag = (e.target as HTMLInputElement).checked; |
| 43 | + setEnabled(flag); |
| 44 | + |
| 45 | + if (flag) { |
| 46 | + // Create initial condition when enabling |
| 47 | + const existingSpec = getDiscourseNodeSetting<Condition[]>(nodeType, [ |
| 48 | + "specification", |
| 49 | + ]); |
| 50 | + if (!existingSpec || existingSpec.length === 0) { |
| 51 | + const initialCondition = createInitialCondition(); |
| 52 | + setDiscourseNodeSetting(nodeType, ["specification"], [ |
| 53 | + initialCondition, |
| 54 | + ]); |
| 55 | + } |
| 56 | + } else { |
| 57 | + // Clear specification when disabling |
| 58 | + setDiscourseNodeSetting(nodeType, ["specification"], []); |
69 | 59 | } |
70 | | - } else { |
71 | | - const tree = getBasicTreeByParentUid(parentUid); |
72 | | - const scratchNode = getSubTree({ tree, key: "scratch" }); |
73 | | - Promise.all(scratchNode.children.map((c) => deleteBlock(c.uid))); |
74 | | - } |
75 | | - }, [parentUid, setMigrated, enabled]); |
| 60 | + }, |
| 61 | + [nodeType, createInitialCondition], |
| 62 | + ); |
| 63 | + |
76 | 64 | return ( |
77 | 65 | <div className={"roamjs-node-specification"}> |
78 | 66 | <style> |
79 | 67 | {`.roamjs-node-specification .bp3-button.bp3-intent-primary { display: none; }`} |
80 | 68 | </style> |
81 | 69 | <p> |
82 | 70 | <Checkbox |
83 | | - checked={!!enabled} |
| 71 | + checked={enabled} |
84 | 72 | className={"ml-8 inline-block"} |
85 | | - onChange={(e) => { |
86 | | - const flag = (e.target as HTMLInputElement).checked; |
87 | | - if (flag) { |
88 | | - createBlock({ |
89 | | - parentUid, |
90 | | - order: 2, |
91 | | - node: { text: "enabled" }, |
92 | | - }).then(setEnabled); |
93 | | - } else { |
94 | | - deleteBlock(enabled).then(() => setEnabled("")); |
95 | | - } |
96 | | - }} |
| 73 | + onChange={handleEnabledChange} |
97 | 74 | /> |
98 | 75 | </p> |
99 | 76 | <div |
100 | | - className={`${enabled ? "" : "bg-gray-200 opacity-75"} overflow-auto`} |
| 77 | + className={`${enabled ? "" : "bg-gray-200 opacity-75 pointer-events-none"} overflow-auto`} |
101 | 78 | > |
102 | | - <QueryEditor |
103 | | - parentUid={parentUid} |
104 | | - key={Number(migrated)} |
105 | | - hideCustomSwitch |
| 79 | + <DiscourseNodeQueryEditor |
| 80 | + nodeType={nodeType} |
| 81 | + defaultConditions={enabled ? [createInitialCondition()] : []} |
| 82 | + key={String(enabled)} |
106 | 83 | /> |
107 | 84 | </div> |
108 | 85 | </div> |
|
0 commit comments