Skip to content

Commit 6a44f91

Browse files
authored
ENG-1281: Port discourse node block panel (#784)
* ENG-1281: Port discourse node block panel * fix bug * address review * address review * address review
1 parent 277aca6 commit 6a44f91

6 files changed

Lines changed: 122 additions & 16 deletions

File tree

apps/roam/src/components/settings/DiscourseNodeSuggestiveRules.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { useState, useMemo, useEffect, useRef } from "react";
22
import { Button, Intent } from "@blueprintjs/core";
3-
import BlocksPanel from "roamjs-components/components/ConfigPanels/BlocksPanel";
3+
import DualWriteBlocksPanel from "./components/EphemeralBlocksPanel";
44
import getSubTree from "roamjs-components/util/getSubTree";
55
import { DiscourseNode } from "~/utils/getDiscourseNodes";
66
import extractRef from "roamjs-components/util/extractRef";
@@ -12,6 +12,8 @@ import {
1212
DiscourseNodeTextPanel,
1313
} from "./components/BlockPropSettingPanels";
1414

15+
const TEMPLATE_SETTING_KEYS = ["template"];
16+
1517
const BlockRenderer = ({ uid }: { uid: string }) => {
1618
const containerRef = useRef<HTMLDivElement>(null);
1719

@@ -81,13 +83,12 @@ const DiscourseNodeSuggestiveRules = ({
8183

8284
return (
8385
<div className="flex flex-col gap-4 p-4">
84-
<BlocksPanel
86+
<DualWriteBlocksPanel
87+
nodeType={node.type}
8588
title="Template"
8689
description={`The template that auto fills ${node.text} page when generated.`}
87-
order={0}
88-
parentUid={nodeUid}
90+
settingKeys={TEMPLATE_SETTING_KEYS}
8991
uid={templateUid}
90-
defaultValue={node.template}
9192
/>
9293

9394
<DiscourseNodeTextPanel

apps/roam/src/components/settings/NodeConfig.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import React, {
77
} from "react";
88
import { DiscourseNode } from "~/utils/getDiscourseNodes";
99
import SelectPanel from "roamjs-components/components/ConfigPanels/SelectPanel";
10-
import BlocksPanel from "roamjs-components/components/ConfigPanels/BlocksPanel";
10+
import DualWriteBlocksPanel from "./components/EphemeralBlocksPanel";
1111
import { getSubTree } from "roamjs-components/util";
1212
import Description from "roamjs-components/components/Description";
1313
import { Label, Tabs, Tab, TabId, InputGroup } from "@blueprintjs/core";
@@ -27,6 +27,8 @@ import {
2727
DiscourseNodeFlagPanel,
2828
} from "./components/BlockPropSettingPanels";
2929

30+
const TEMPLATE_SETTING_KEYS = ["template"];
31+
3032
export const getCleanTagText = (tag: string): string => {
3133
return tag.replace(/^#+/, "").trim().toUpperCase();
3234
};
@@ -342,13 +344,12 @@ const NodeConfig = ({
342344
title="Template"
343345
panel={
344346
<div className="flex flex-col gap-4 p-1">
345-
<BlocksPanel
347+
<DualWriteBlocksPanel
348+
nodeType={node.type}
346349
title="Template"
347350
description={`The template that auto fills ${node.text} page when generated.`}
348-
order={0}
349-
parentUid={node.type}
351+
settingKeys={TEMPLATE_SETTING_KEYS}
350352
uid={templateUid}
351-
defaultValue={node.template}
352353
/>
353354
</div>
354355
}

apps/roam/src/components/settings/components/BlockPropSettingPanels.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -582,7 +582,7 @@ const createDiscourseNodeSetter =
582582
(keys: string[], value: json): void =>
583583
setDiscourseNodeSetting(nodeType, keys, value);
584584

585-
type DiscourseNodeBaseProps = {
585+
export type DiscourseNodeBaseProps = {
586586
nodeType: string;
587587
title: string;
588588
description: string;
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import React, { useRef, useEffect, useCallback } from "react";
2+
import { Label } from "@blueprintjs/core";
3+
import Description from "roamjs-components/components/Description";
4+
import createBlock from "roamjs-components/writes/createBlock";
5+
import getFullTreeByParentUid from "roamjs-components/queries/getFullTreeByParentUid";
6+
import getFirstChildUidByBlockUid from "roamjs-components/queries/getFirstChildUidByBlockUid";
7+
import type { TreeNode } from "roamjs-components/types";
8+
import type { RoamNodeType } from "~/components/settings/utils/zodSchema";
9+
import { setDiscourseNodeSetting } from "~/components/settings/utils/accessors";
10+
import type { DiscourseNodeBaseProps } from "./BlockPropSettingPanels";
11+
12+
const DEBOUNCE_MS = 250;
13+
14+
type DualWriteBlocksPanelProps = DiscourseNodeBaseProps & {
15+
uid: string;
16+
};
17+
18+
const serializeBlockTree = (children: TreeNode[]): RoamNodeType[] =>
19+
children
20+
.sort((a, b) => a.order - b.order)
21+
.map((child) => ({
22+
text: child.text,
23+
...(child.heading && { heading: child.heading as 0 | 1 | 2 | 3 }),
24+
...(child.open === false && { open: false }),
25+
...(child.children.length > 0 && {
26+
children: serializeBlockTree(child.children),
27+
}),
28+
}));
29+
30+
const DualWriteBlocksPanel = ({
31+
nodeType,
32+
settingKeys,
33+
title,
34+
description,
35+
uid,
36+
}: DualWriteBlocksPanelProps) => {
37+
const containerRef = useRef<HTMLDivElement>(null);
38+
const debounceRef = useRef(0);
39+
const pullWatchArgsRef = useRef<
40+
[string, string, (before: unknown, after: unknown) => void] | null
41+
>(null);
42+
43+
const handleChange = useCallback(() => {
44+
window.clearTimeout(debounceRef.current);
45+
debounceRef.current = window.setTimeout(() => {
46+
const tree = getFullTreeByParentUid(uid);
47+
const serialized = serializeBlockTree(tree.children);
48+
setDiscourseNodeSetting(nodeType, settingKeys, serialized);
49+
}, DEBOUNCE_MS);
50+
}, [uid, nodeType, settingKeys]);
51+
52+
useEffect(() => {
53+
const el = containerRef.current;
54+
if (!el || !uid) return;
55+
56+
const pattern = "[:block/string :block/order {:block/children ...}]";
57+
const entityId = `[:block/uid "${uid}"]`;
58+
const callback = () => handleChange();
59+
60+
const registerPullWatch = () => {
61+
pullWatchArgsRef.current = [pattern, entityId, callback];
62+
window.roamAlphaAPI.data.addPullWatch(pattern, entityId, callback);
63+
};
64+
65+
if (!getFirstChildUidByBlockUid(uid)) {
66+
void createBlock({ node: { text: " " }, parentUid: uid }).then(() => {
67+
el.innerHTML = "";
68+
void window.roamAlphaAPI.ui.components.renderBlock({ uid, el });
69+
registerPullWatch();
70+
});
71+
} else {
72+
el.innerHTML = "";
73+
void window.roamAlphaAPI.ui.components.renderBlock({ uid, el });
74+
registerPullWatch();
75+
}
76+
77+
return () => {
78+
window.clearTimeout(debounceRef.current);
79+
if (pullWatchArgsRef.current) {
80+
window.roamAlphaAPI.data.removePullWatch(...pullWatchArgsRef.current);
81+
pullWatchArgsRef.current = null;
82+
}
83+
};
84+
}, [uid, handleChange]);
85+
86+
return (
87+
<>
88+
<Label>
89+
{title}
90+
<Description description={description} />
91+
</Label>
92+
<style>{`.dg-dualwrite-blocks > div > .rm-block-main {
93+
display: none;
94+
}
95+
.dg-dualwrite-blocks > div > .rm-block-children > .rm-multibar {
96+
display: none;
97+
}
98+
.dg-dualwrite-blocks > div > .rm-block-children {
99+
margin-left: -4px;
100+
}`}</style>
101+
<div
102+
ref={containerRef}
103+
className="dg-dualwrite-blocks rounded border border-gray-200 py-2"
104+
/>
105+
</>
106+
);
107+
};
108+
109+
export default DualWriteBlocksPanel;

apps/roam/src/components/settings/utils/zodSchema.example.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,6 @@ const canvasSettings: CanvasSettings = {
3030
};
3131

3232
const suggestiveRules: SuggestiveRules = {
33-
template: [
34-
{ text: "Summary::", heading: 2 },
35-
{ text: "Key Points::", heading: 2, children: [{ text: "" }] },
36-
],
3733
embeddingRef: "((block-uid-123))",
3834
isFirstChild: true,
3935
};

apps/roam/src/components/settings/utils/zodSchema.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,6 @@ export const RoamNodeSchema: z.ZodType<RoamNode> = z.lazy(() =>
7575
);
7676

7777
export const SuggestiveRulesSchema = z.object({
78-
template: z.array(RoamNodeSchema).default([]),
7978
embeddingRef: z.string().default(""),
8079
isFirstChild: z.boolean().default(false),
8180
});

0 commit comments

Comments
 (0)