Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions apps/roam/src/components/QueryBuilder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ type QueryPageComponent = (props: {
isEditBlock?: boolean;
showAlias?: boolean;
discourseNodeType?: string;
settingKey?: "index" | "specification";
returnNode?: string;
}) => JSX.Element;

type Props = Parameters<QueryPageComponent>[0];
Expand All @@ -41,6 +43,8 @@ const QueryBuilder = ({
isEditBlock,
showAlias,
discourseNodeType,
settingKey,
returnNode,
}: Props) => {
const extensionAPI = useExtensionAPI();
const hideMetadata = useMemo(
Expand Down Expand Up @@ -165,6 +169,8 @@ const QueryBuilder = ({
<QueryEditor
parentUid={pageUid}
discourseNodeType={discourseNodeType}
settingKey={settingKey}
returnNode={returnNode}
onQuery={() => {
setHasResults(true);
setIsEdit(false);
Expand Down
27 changes: 21 additions & 6 deletions apps/roam/src/components/QueryEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,8 @@ type QueryEditorComponent = (props: {
hideCustomSwitch?: boolean;
showAlias?: boolean;
discourseNodeType?: string;
settingKey?: "index" | "specification";
returnNode?: string;
}) => JSX.Element;

const QueryEditor: QueryEditorComponent = ({
Expand All @@ -446,6 +448,8 @@ const QueryEditor: QueryEditorComponent = ({
hideCustomSwitch,
showAlias,
discourseNodeType,
settingKey,
returnNode,
}) => {
useEffect(() => {
const previewQuery = ((e: CustomEvent) => {
Expand Down Expand Up @@ -487,11 +491,12 @@ const QueryEditor: QueryEditorComponent = ({
return () => window.clearTimeout(blockPropSyncTimeoutRef.current);
}, []);
useEffect(() => {
if (!discourseNodeType) return;
if (!discourseNodeType || !settingKey) return;

const stripped: unknown = JSON.parse(
JSON.stringify({ conditions, selections, custom }, (key, value: unknown) =>
key === "uid" ? undefined : value,
JSON.stringify(
{ conditions, selections, custom, returnNode },
(key, value: unknown) => (key === "uid" ? undefined : value),
),
);

Expand All @@ -500,18 +505,28 @@ const QueryEditor: QueryEditorComponent = ({

const result = IndexSchema.safeParse(stripped);
if (!result.success) {
console.error("Index blockprop sync failed validation:", result.error);
console.error(`${settingKey} blockprop sync failed validation:`, result.error);
return;
}

const path =
settingKey === "index" ? ["index"] : ["specification", "query"];

window.clearTimeout(blockPropSyncTimeoutRef.current);
blockPropSyncTimeoutRef.current = window.setTimeout(() => {
setDiscourseNodeSetting(discourseNodeType, ["index"], result.data);
setDiscourseNodeSetting(discourseNodeType, path, result.data);
lastSyncedIndexRef.current = serialized;
}, 250);

return () => window.clearTimeout(blockPropSyncTimeoutRef.current);
}, [conditions, selections, custom, discourseNodeType]);
}, [
conditions,
selections,
custom,
discourseNodeType,
settingKey,
returnNode,
]);

const customNodeOnChange = (value: string) => {
window.clearTimeout(debounceRef.current);
Expand Down
9 changes: 8 additions & 1 deletion apps/roam/src/components/settings/DiscourseNodeIndex.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ const NodeIndex = ({
},
],
selections: [],
custom: "",
returnNode: DEFAULT_RETURN_NODE,
});

setShowQuery(true);
Expand All @@ -69,7 +71,12 @@ const NodeIndex = ({
return (
<ExtensionApiContextProvider {...onloadArgs}>
{showQuery ? (
<QueryBuilder pageUid={parentUid} discourseNodeType={node.type} />
<QueryBuilder
pageUid={parentUid}
discourseNodeType={node.type}
settingKey="index"
returnNode={DEFAULT_RETURN_NODE}
/>
) : (
<Spinner />
)}
Expand Down
93 changes: 53 additions & 40 deletions apps/roam/src/components/settings/DiscourseNodeSpecification.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import React from "react";
import getSubTree from "roamjs-components/util/getSubTree";
import createBlock from "roamjs-components/writes/createBlock";
import { Checkbox } from "@blueprintjs/core";
import getBasicTreeByParentUid from "roamjs-components/queries/getBasicTreeByParentUid";
import deleteBlock from "roamjs-components/writes/deleteBlock";
import refreshConfigTree from "~/utils/refreshConfigTree";
import getDiscourseNodes from "~/utils/getDiscourseNodes";
import getDiscourseNodeFormatExpression from "~/utils/getDiscourseNodeFormatExpression";
import QueryEditor from "~/components/QueryEditor";
import internalError from "~/utils/internalError";
import { setDiscourseNodeSetting } from "~/components/settings/utils/accessors";
import { DiscourseNodeFlagPanel } from "~/components/settings/components/BlockPropSettingPanels";

const NodeSpecification = ({
parentUid,
Expand All @@ -20,11 +21,14 @@ const NodeSpecification = ({
parentSetEnabled?: (enabled: boolean) => void;
}) => {
const [migrated, setMigrated] = React.useState(false);
const [enabled, setEnabled] = React.useState<string>(
const enabledBlockUid = React.useMemo(
() =>
getSubTree({ tree: getBasicTreeByParentUid(parentUid), key: "enabled" })
?.uid,
[parentUid],
);
const [enabled, setEnabled] = React.useState(!!enabledBlockUid);

React.useEffect(() => {
if (enabled) {
const scratchNode = getSubTree({ parentUid, key: "scratch" });
Expand Down Expand Up @@ -69,19 +73,41 @@ const NodeSpecification = ({
},
}),
)
.then(() => setMigrated(true))
.then(() => {
setDiscourseNodeSetting(node.type, ["specification", "query"], {
conditions: [
{
type: "clause" as const,
source: node.text,
relation: "has title",
target: `/${getDiscourseNodeFormatExpression(node.format).source}/`,
},
],
selections: [],
custom: "",
returnNode: node.text,
});
setMigrated(true);
})
.catch((error) => {
internalError({ error });
});
}
} else {
const tree = getBasicTreeByParentUid(parentUid);
const scratchNode = getSubTree({ tree, key: "scratch" });
Promise.all(scratchNode.children.map((c) => deleteBlock(c.uid))).catch(
(error) => {
Promise.all(scratchNode.children.map((c) => deleteBlock(c.uid)))
.then(() => {
setDiscourseNodeSetting(node.type, ["specification", "query"], {
conditions: [],
selections: [],
custom: "",
returnNode: "",
});
})
.catch((error) => {
internalError({ error });
},
);
});
}
return () => {
refreshConfigTree();
Expand All @@ -90,47 +116,34 @@ const NodeSpecification = ({
return (
<div className={"roamjs-node-specification"}>
<style>
{`.roamjs-node-specification .bp3-button.bp3-intent-primary { display: none; }`}
{`.roamjs-node-specification .bp3-button.bp3-intent-primary { display: none; }
.roamjs-node-specification .bp3-checkbox { visibility: hidden; }
.roamjs-node-specification .bp3-checkbox .bp3-control-indicator { visibility: visible; }`}
</style>
<p>
<Checkbox
checked={!!enabled}
className={"ml-8 inline-block"}
onChange={(e) => {
const flag = (e.target as HTMLInputElement).checked;
if (flag) {
createBlock({
parentUid,
order: 2,
node: { text: "enabled" },
})
.then((uid: string) => {
setEnabled(uid);
if (parentSetEnabled) parentSetEnabled(true);
})
.catch((error) => {
internalError({ error });
});
} else {
deleteBlock(enabled)
.then(() => {
setEnabled("");
if (parentSetEnabled) parentSetEnabled(false);
})
.catch((error) => {
internalError({ error });
});
}
}}
/>
</p>
<DiscourseNodeFlagPanel
nodeType={node.type}
title="enabled"
description=""
settingKeys={["specification", "enabled"]}
defaultValue={enabled}
parentUid={parentUid}
uid={enabledBlockUid}
order={2}
onChange={(checked) => {
setEnabled(checked);
parentSetEnabled?.(checked);
}}
/>
<div
className={`${enabled ? "" : "bg-gray-200 opacity-75"} overflow-auto`}
>
<QueryEditor
parentUid={parentUid}
key={Number(migrated)}
hideCustomSwitch
discourseNodeType={node.type}
settingKey="specification"
returnNode={node.text}
/>
</div>
</div>
Expand Down
23 changes: 16 additions & 7 deletions apps/roam/src/components/settings/utils/zodSchema.example.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,22 @@ const discourseNodeSettings: DiscourseNodeSettings = {
shortcut: "C",
tag: "#claim",
description: "A statement or assertion that can be supported or refuted",
specification: [
{
type: "clause",
source: "Claim",
relation: "has title",
target: "/^\\[\\[CLM\\]\\]/",
specification: {
enabled: true,
query: {
conditions: [
{
type: "clause",
source: "Claim",
relation: "has title",
target: "/^\\[\\[CLM\\]\\]/",
},
],
selections: [],
custom: "",
returnNode: "Claim",
},
],
},
template: [
{ text: "Summary::", heading: 2 },
{ text: "Evidence::", heading: 2, children: [{ text: "" }] },
Expand All @@ -76,6 +84,7 @@ const discourseNodeSettings: DiscourseNodeSettings = {
],
selections: [],
custom: "",
returnNode: "node",
},
suggestiveRules,
backedBy: "user",
Expand Down
8 changes: 6 additions & 2 deletions apps/roam/src/components/settings/utils/zodSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export const IndexSchema = z.object({
conditions: z.array(ConditionSchema).default([]),
selections: z.array(SelectionSchema).default([]),
custom: z.string().default(""),
returnNode: z.string().default("node"),
});

type RoamNode = {
Expand Down Expand Up @@ -103,10 +104,13 @@ export const DiscourseNodeSchema = z.object({
tag: stringWithDefault(""),
description: stringWithDefault(""),
specification: z
.array(ConditionSchema)
.object({
enabled: z.boolean().default(false),
query: IndexSchema.default({}),
})
.nullable()
.optional()
.transform((val) => val ?? []),
.transform((val) => val ?? { enabled: false, query: { conditions: [], selections: [], custom: "", returnNode: "" } }),
template: z
.array(RoamNodeSchema)
.nullable()
Expand Down