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
60 changes: 4 additions & 56 deletions apps/roam/src/components/settings/DiscourseNodeIndex.tsx
Original file line number Diff line number Diff line change
@@ -1,61 +1,9 @@
import React, { useEffect } from "react";
import { Spinner } from "@blueprintjs/core";
import ExtensionApiContextProvider from "roamjs-components/components/ExtensionApiContext";
import type { OnloadArgs } from "roamjs-components/types/native";
import React from "react";
import type { DiscourseNode } from "~/utils/getDiscourseNodes";
import QueryBuilder from "~/components/QueryBuilder";
import parseQuery, { DEFAULT_RETURN_NODE } from "~/utils/parseQuery";
import createBlock from "roamjs-components/writes/createBlock";
import DiscourseNodeQueryBuilder from "./components/DiscourseNodeQueryBuilder";

const NodeIndex = ({
parentUid,
node,
onloadArgs,
}: {
parentUid: string;
node: DiscourseNode;
onloadArgs: OnloadArgs;
}) => {
const initialQueryArgs = React.useMemo(
() => parseQuery(parentUid),
[parentUid],
);
const [showQuery, setShowQuery] = React.useState(
!!initialQueryArgs.conditions.length,
);
useEffect(() => {
if (!showQuery) {
createBlock({
parentUid: initialQueryArgs.conditionsNodesUid,
node: {
text: "clause",
children: [
{
text: "source",
children: [{ text: DEFAULT_RETURN_NODE }],
},
{
text: "relation",
children: [{ text: "is a" }],
},
{
text: "target",
children: [
{
text: node.text,
},
],
},
],
},
}).then(() => setShowQuery(true));
}
}, [parentUid, initialQueryArgs, showQuery]);
return (
<ExtensionApiContextProvider {...onloadArgs}>
{showQuery ? <QueryBuilder pageUid={parentUid} /> : <Spinner />}
</ExtensionApiContextProvider>
);
const NodeIndex = ({ node }: { node: DiscourseNode }) => {
return <DiscourseNodeQueryBuilder nodeType={node.type} nodeText={node.text} />;
};

export default NodeIndex;
141 changes: 59 additions & 82 deletions apps/roam/src/components/settings/DiscourseNodeSpecification.tsx
Original file line number Diff line number Diff line change
@@ -1,108 +1,85 @@
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 getDiscourseNodes from "~/utils/getDiscourseNodes";
import getDiscourseNodeFormatExpression from "~/utils/getDiscourseNodeFormatExpression";
import QueryEditor from "~/components/QueryEditor";
import DiscourseNodeQueryEditor from "./components/DiscourseNodeQueryEditor";
import {
getDiscourseNodeSetting,
setDiscourseNodeSetting,
} from "./utils/accessors";
import type { Condition } from "~/utils/types";

const generateUID = (): string =>
window.roamAlphaAPI?.util?.generateUID?.() ??
Math.random().toString(36).substring(2, 11);

const NodeSpecification = ({
parentUid,
node,
}: {
parentUid: string;
node: ReturnType<typeof getDiscourseNodes>[number];
}) => {
const [migrated, setMigrated] = React.useState(false);
const [enabled, setEnabled] = React.useState(
() =>
getSubTree({ tree: getBasicTreeByParentUid(parentUid), key: "enabled" })
?.uid,
);
React.useEffect(() => {
if (enabled) {
const scratchNode = getSubTree({ parentUid, key: "scratch" });
if (
!scratchNode.children.length ||
!getSubTree({ tree: scratchNode.children, key: "conditions" }).children
.length
) {
const conditionsUid = getSubTree({
parentUid: scratchNode.uid,
key: "conditions",
}).uid;
const returnUid = getSubTree({
parentUid: scratchNode.uid,
key: "return",
}).uid;
createBlock({
parentUid: returnUid,
node: {
text: node.text,
},
})
.then(() =>
createBlock({
parentUid: conditionsUid,
node: {
text: "clause",
children: [
{ text: "source", children: [{ text: node.text }] },
{ text: "relation", children: [{ text: "has title" }] },
{
text: "target",
children: [
{
text: `/${
getDiscourseNodeFormatExpression(node.format).source
}/`,
},
],
},
],
},
}),
)
.then(() => setMigrated(true));
const nodeType = node.type;

const [enabled, setEnabled] = React.useState(() => {
const spec = getDiscourseNodeSetting<Condition[]>(nodeType, [
"specification",
]);
return spec !== null && spec !== undefined && spec.length > 0;
});

const createInitialCondition = React.useCallback((): Condition => {
return {
uid: generateUID(),
type: "clause",
source: node.text,
relation: "has title",
target: `/${getDiscourseNodeFormatExpression(node.format).source}/`,
};
}, [node.text, node.format]);

const handleEnabledChange = React.useCallback(
(e: React.FormEvent<HTMLInputElement>) => {
const flag = (e.target as HTMLInputElement).checked;
setEnabled(flag);

if (flag) {
// Create initial condition when enabling
const existingSpec = getDiscourseNodeSetting<Condition[]>(nodeType, [
"specification",
]);
if (!existingSpec || existingSpec.length === 0) {
const initialCondition = createInitialCondition();
setDiscourseNodeSetting(nodeType, ["specification"], [
initialCondition,
]);
}
} else {
// Clear specification when disabling
setDiscourseNodeSetting(nodeType, ["specification"], []);
}
} else {
const tree = getBasicTreeByParentUid(parentUid);
const scratchNode = getSubTree({ tree, key: "scratch" });
Promise.all(scratchNode.children.map((c) => deleteBlock(c.uid)));
}
}, [parentUid, setMigrated, enabled]);
},
[nodeType, createInitialCondition],
);

return (
<div className={"roamjs-node-specification"}>
<style>
{`.roamjs-node-specification .bp3-button.bp3-intent-primary { display: none; }`}
</style>
<p>
<Checkbox
checked={!!enabled}
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(setEnabled);
} else {
deleteBlock(enabled).then(() => setEnabled(""));
}
}}
onChange={handleEnabledChange}
/>
</p>
<div
className={`${enabled ? "" : "bg-gray-200 opacity-75"} overflow-auto`}
className={`${enabled ? "" : "bg-gray-200 opacity-75 pointer-events-none"} overflow-auto`}
>
<QueryEditor
parentUid={parentUid}
key={Number(migrated)}
hideCustomSwitch
<DiscourseNodeQueryEditor
nodeType={nodeType}
defaultConditions={enabled ? [createInitialCondition()] : []}
key={String(enabled)}
/>
</div>
</div>
Expand Down
15 changes: 3 additions & 12 deletions apps/roam/src/components/settings/NodeConfig.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -164,15 +164,13 @@ const NodeConfig = ({
onloadArgs: OnloadArgs;
}) => {
const suggestiveModeEnabled = useFeatureFlag("Suggestive Mode Enabled");
// UIDs still needed for deferred complex settings (template, specification, etc.)
// UIDs still needed for deferred complex settings (template)
const getUid = (key: string) =>
getSubTree({
parentUid: node.type,
key: key,
}).uid;
const templateUid = getUid("Template");
const specificationUid = getUid("Specification");
const indexUid = getUid("Index");

const [selectedTabId, setSelectedTabId] = useState<TabId>("general");
const [tagError, setTagError] = useState("");
Expand Down Expand Up @@ -301,11 +299,7 @@ const NodeConfig = ({
title="Index"
panel={
<div className="flex flex-col gap-4 p-1">
<DiscourseNodeIndex
node={node}
parentUid={indexUid}
onloadArgs={onloadArgs}
/>
<DiscourseNodeIndex node={node} />
</div>
}
/>
Expand All @@ -329,10 +323,7 @@ const NodeConfig = ({
"The conditions specified to identify a ${nodeText} node."
}
/>
<DiscourseNodeSpecification
node={node}
parentUid={specificationUid}
/>
<DiscourseNodeSpecification node={node} />
</Label>
</div>
}
Expand Down
Loading
Loading