Skip to content
Merged
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
118 changes: 107 additions & 11 deletions apps/roam/src/components/settings/components/EphemeralBlocksPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import React, { useRef, useEffect, useCallback } from "react";
import React, { useRef, useEffect, useCallback, useState } from "react";
import { Label } from "@blueprintjs/core";
import Description from "roamjs-components/components/Description";
import createBlock from "roamjs-components/writes/createBlock";
import deleteBlock from "roamjs-components/writes/deleteBlock";
import getFullTreeByParentUid from "roamjs-components/queries/getFullTreeByParentUid";
import getFirstChildUidByBlockUid from "roamjs-components/queries/getFirstChildUidByBlockUid";
import type { InputTextNode, TreeNode } from "roamjs-components/types";
import type { RoamNodeType } from "~/components/settings/utils/zodSchema";
import { setDiscourseNodeSetting } from "~/components/settings/utils/accessors";
import {
isNewSettingsStoreEnabled,
setDiscourseNodeSetting,
} from "~/components/settings/utils/accessors";
import type { DiscourseNodeBaseProps } from "./BlockPropSettingPanels";

const DEBOUNCE_MS = 250;
const TEMPLATE_BUFFER_TEXT = "Template-Block-props";

type DualWriteBlocksPanelProps = DiscourseNodeBaseProps & {
uid: string;
Expand All @@ -28,6 +33,62 @@ const serializeBlockTree = (children: TreeNode[]): RoamNodeType[] =>
}),
}));

const treeNodeToInputTextNode = (node: TreeNode): InputTextNode => ({

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be removed when we move from legacy, used to sync block prop template to block template

text: node.text,
...(node.heading && { heading: node.heading as 0 | 1 | 2 | 3 }),
...(node.open === false && { open: false }),
...(node.children.length > 0 && {
children: [...node.children]
.sort((a, b) => a.order - b.order)
.map(treeNodeToInputTextNode),
}),
});

const mirrorBufferToLegacyChildren = (

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be removed when we move from legacy, used to sync block prop template to block template

bufferChildren: TreeNode[],
legacyChildren: TreeNode[],
legacyParentUid: string,
): void => {
const sortedBuffer = [...bufferChildren].sort((a, b) => a.order - b.order);
const sortedLegacy = [...legacyChildren].sort((a, b) => a.order - b.order);
const minLen = Math.min(sortedBuffer.length, sortedLegacy.length);

for (let i = 0; i < minLen; i++) {
const bufferNode = sortedBuffer[i];
const legacyNode = sortedLegacy[i];
if (
bufferNode.text !== legacyNode.text ||
bufferNode.heading !== legacyNode.heading ||
bufferNode.open !== legacyNode.open
) {
void window.roamAlphaAPI.data.block.update({
block: {
uid: legacyNode.uid,
string: bufferNode.text,
...(bufferNode.heading !== undefined && {
heading: bufferNode.heading,
}),
...(bufferNode.open !== undefined && { open: bufferNode.open }),
},
});
}
mirrorBufferToLegacyChildren(
bufferNode.children,
legacyNode.children,
legacyNode.uid,
);
}

for (let i = minLen; i < sortedBuffer.length; i++) {
const node = treeNodeToInputTextNode(sortedBuffer[i]);
void createBlock({ node, parentUid: legacyParentUid, order: i });
}

for (let i = minLen; i < sortedLegacy.length; i++) {
void deleteBlock(sortedLegacy[i].uid);
}
};
Comment thread
sid597 marked this conversation as resolved.

const DualWriteBlocksPanel = ({
nodeType,
settingKeys,
Expand All @@ -44,21 +105,51 @@ const DualWriteBlocksPanel = ({
[string, string, (before: unknown, after: unknown) => void] | null
>(null);

const isNewStore = isNewSettingsStoreEnabled();
const [bufferUid, setBufferUid] = useState<string | null>(null);
const renderUid = isNewStore ? bufferUid : uid;

useEffect(() => {
if (!isNewStore || !nodeType) return;
let cancelled = false;
const newUid = window.roamAlphaAPI.util.generateUID();
const dv = defaultValueRef.current;
const seed: InputTextNode[] = dv && dv.length > 0 ? dv : [{ text: " " }];
void createBlock({
node: { text: TEMPLATE_BUFFER_TEXT, uid: newUid, children: seed },
parentUid: nodeType,
order: "last",
}).then(() => {
if (!cancelled) setBufferUid(newUid);
});
return () => {
cancelled = true;
setBufferUid(null);
void deleteBlock(newUid);
};
}, [isNewStore, nodeType]);

const handleChange = useCallback(() => {
if (!renderUid) return;
window.clearTimeout(debounceRef.current);
debounceRef.current = window.setTimeout(() => {
const tree = getFullTreeByParentUid(uid);
const tree = getFullTreeByParentUid(renderUid);
const serialized = serializeBlockTree(tree.children);
setDiscourseNodeSetting(nodeType, settingKeys, serialized);
if (isNewStore && renderUid !== uid) {
const legacyTree = getFullTreeByParentUid(uid);
mirrorBufferToLegacyChildren(tree.children, legacyTree.children, uid);
}
}, DEBOUNCE_MS);
}, [uid, nodeType, settingKeys]);
}, [renderUid, uid, isNewStore, nodeType, settingKeys]);

useEffect(() => {
const el = containerRef.current;
if (!el || !uid) return;
if (!el || !renderUid) return;

let cancelled = false;
const pattern = "[:block/string :block/order {:block/children ...}]";
const entityId = `[:block/uid "${uid}"]`;
const entityId = `[:block/uid "${renderUid}"]`;
const callback = () => handleChange();

const registerPullWatch = () => {
Expand All @@ -67,31 +158,36 @@ const DualWriteBlocksPanel = ({
};

const dv = defaultValueRef.current;
const ensureChildren = getFirstChildUidByBlockUid(uid)
const ensureChildren = getFirstChildUidByBlockUid(renderUid)
? Promise.resolve()
: (dv && dv.length > 0
? Promise.all(
dv.map((node, i) =>
createBlock({ node, parentUid: uid, order: i }),
createBlock({ node, parentUid: renderUid, order: i }),
),
)
: createBlock({ node: { text: " " }, parentUid: uid })
: createBlock({ node: { text: " " }, parentUid: renderUid })
).then(() => {});

void ensureChildren.then(() => {
if (cancelled) return;
el.innerHTML = "";
void window.roamAlphaAPI.ui.components.renderBlock({ uid, el });
void window.roamAlphaAPI.ui.components.renderBlock({
uid: renderUid,
el,
});
registerPullWatch();
});
Comment thread
sid597 marked this conversation as resolved.

return () => {
cancelled = true;
window.clearTimeout(debounceRef.current);
if (pullWatchArgsRef.current) {
window.roamAlphaAPI.data.removePullWatch(...pullWatchArgsRef.current);
pullWatchArgsRef.current = null;
}
};
}, [uid, handleChange]);
}, [renderUid, handleChange]);
Comment thread
sid597 marked this conversation as resolved.

return (
<>
Expand Down
Loading