Skip to content

Commit 4f5eb59

Browse files
authored
Land orphaned Graphite stack PRs (#764, #765, #769, #793, #794, #805) (#826)
* Land orphaned Graphite stack PRs (#764, #765, #769, #793, #794, #805) These PRs were squash-merged into parent branches instead of main due to a Graphite stack merge race condition. Their changes never landed on main despite showing as merged in Graphite. Includes: - #764 ENG-1280: Port query builder/editor for block props - #765 ENG-1291: Port discourse node specification (dual-write) - #769 ENG-1290: Port discourse node config panel - #793 ENG-1440: Port page groups settings in suggestive mode - #794 ENG-1438: Port keyboard shortcut keys/triggers - #805 ENG-1328: Port small blueprint misc settings * Add #804 ENG-1217: Port section component in left sidebar * Add #764, #765, #769 (Chain 1 orphaned PRs: query builder, specification, config panel) The initial recovery commit claimed to include these but only contained Chain 2 (#793, #794, #805, #804). Chain 1 lives on separate branches that weren't in the merge source. Applied using `gh pr diff` for each PR in order (#764#765#769). init.ts required manual merge — #765 and #769 both modified it from the same base. Kept #765's safeParse checks inside #769's restructured initializeSettingsBlockProps. - #764 ENG-1280: Port query builder and editor component for block props - #765 ENG-1291: Port discourse node specification - #769 ENG-1290: Port discourse node config panel * fix lint * prettier
1 parent facc68c commit 4f5eb59

23 files changed

Lines changed: 934 additions & 567 deletions

apps/roam/src/components/DiscourseNodeMenu.tsx

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { getNewDiscourseNodeText } from "~/utils/formatUtils";
2727
import { OnloadArgs } from "roamjs-components/types";
2828
import { formatHexColor } from "./settings/DiscourseNodeCanvasSettings";
2929
import posthog from "posthog-js";
30+
import { setPersonalSetting } from "~/components/settings/utils/accessors";
3031

3132
type Props = {
3233
textarea?: HTMLTextAreaElement;
@@ -406,6 +407,13 @@ export const getModifiersFromCombo = (comboKey: IKeyCombo) => {
406407
].filter(Boolean);
407408
};
408409

410+
export const comboToString = (combo: IKeyCombo): string => {
411+
if (!combo.key) return "";
412+
const modifiers = getModifiersFromCombo(combo);
413+
const comboString = [...modifiers, combo.key].join("+");
414+
return normalizeKeyCombo(comboString).join("+");
415+
};
416+
409417
export const NodeMenuTriggerComponent = ({
410418
extensionAPI,
411419
}: {
@@ -427,19 +435,15 @@ export const NodeMenuTriggerComponent = ({
427435
const comboObj = getKeyCombo(e.nativeEvent);
428436
if (!comboObj.key) return;
429437

430-
setComboKey({ key: comboObj.key, modifiers: comboObj.modifiers });
431-
extensionAPI.settings.set("personal-node-menu-trigger", comboObj);
438+
const combo = { key: comboObj.key, modifiers: comboObj.modifiers };
439+
setComboKey(combo);
440+
void extensionAPI.settings.set("personal-node-menu-trigger", combo);
441+
setPersonalSetting(["Personal node menu trigger"], combo);
432442
},
433443
[extensionAPI],
434444
);
435445

436-
const shortcut = useMemo(() => {
437-
if (!comboKey.key) return "";
438-
439-
const modifiers = getModifiersFromCombo(comboKey);
440-
const comboString = [...modifiers, comboKey.key].join("+");
441-
return normalizeKeyCombo(comboString).join("+");
442-
}, [comboKey]);
446+
const shortcut = useMemo(() => comboToString(comboKey), [comboKey]);
443447

444448
return (
445449
<InputGroup
@@ -455,7 +459,8 @@ export const NodeMenuTriggerComponent = ({
455459
icon={"remove"}
456460
onClick={() => {
457461
setComboKey({ modifiers: 0, key: "" });
458-
extensionAPI.settings.set("personal-node-menu-trigger", "");
462+
void extensionAPI.settings.set("personal-node-menu-trigger", "");
463+
setPersonalSetting(["Personal node menu trigger"], "");
459464
}}
460465
minimal
461466
/>

apps/roam/src/components/DiscourseNodeSearchMenu.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import getDiscourseNodeFormatExpression from "~/utils/getDiscourseNodeFormatExpr
2626
import { Result } from "~/utils/types";
2727
import { getSetting } from "~/utils/extensionSettings";
2828
import MiniSearch from "minisearch";
29+
import { setPersonalSetting } from "~/components/settings/utils/accessors";
2930

3031
type Props = {
3132
textarea: HTMLTextAreaElement;
@@ -724,7 +725,8 @@ export const NodeSearchMenuTriggerSetting = ({
724725
.trim();
725726

726727
setNodeSearchTrigger(trigger);
727-
extensionAPI.settings.set("node-search-trigger", trigger);
728+
void extensionAPI.settings.set("node-search-trigger", trigger);
729+
setPersonalSetting(["Node search menu trigger"], trigger);
728730
};
729731
return (
730732
<InputGroup

apps/roam/src/components/QueryBuilder.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,21 @@ type QueryPageComponent = (props: {
3131
pageUid: string;
3232
isEditBlock?: boolean;
3333
showAlias?: boolean;
34+
discourseNodeType?: string;
35+
settingKey?: "index" | "specification";
36+
returnNode?: string;
3437
}) => JSX.Element;
3538

3639
type Props = Parameters<QueryPageComponent>[0];
3740

38-
const QueryBuilder = ({ pageUid, isEditBlock, showAlias }: Props) => {
41+
const QueryBuilder = ({
42+
pageUid,
43+
isEditBlock,
44+
showAlias,
45+
discourseNodeType,
46+
settingKey,
47+
returnNode,
48+
}: Props) => {
3949
const extensionAPI = useExtensionAPI();
4050
const hideMetadata = useMemo(
4151
() =>
@@ -158,6 +168,9 @@ const QueryBuilder = ({ pageUid, isEditBlock, showAlias }: Props) => {
158168
<>
159169
<QueryEditor
160170
parentUid={pageUid}
171+
discourseNodeType={discourseNodeType}
172+
settingKey={settingKey}
173+
returnNode={returnNode}
161174
onQuery={() => {
162175
setHasResults(true);
163176
setIsEdit(false);

apps/roam/src/components/QueryEditor.tsx

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ import {
4444
import getShallowTreeByParentUid from "roamjs-components/queries/getShallowTreeByParentUid";
4545
import { ALL_SELECTION_SUGGESTIONS } from "~/utils/predefinedSelections";
4646
import { getAlias } from "~/utils/parseResultSettings";
47+
import { setDiscourseNodeSetting } from "~/components/settings/utils/accessors";
48+
import { IndexSchema } from "~/components/settings/utils/zodSchema";
4749

4850
const getSourceCandidates = (cs: Condition[]): string[] =>
4951
cs.flatMap((c) =>
@@ -434,6 +436,9 @@ type QueryEditorComponent = (props: {
434436
setHasResults?: () => void;
435437
hideCustomSwitch?: boolean;
436438
showAlias?: boolean;
439+
discourseNodeType?: string;
440+
settingKey?: "index" | "specification";
441+
returnNode?: string;
437442
}) => JSX.Element;
438443

439444
const QueryEditor: QueryEditorComponent = ({
@@ -442,6 +447,9 @@ const QueryEditor: QueryEditorComponent = ({
442447
setHasResults,
443448
hideCustomSwitch,
444449
showAlias,
450+
discourseNodeType, // eslint-disable-line react/prop-types
451+
settingKey, // eslint-disable-line react/prop-types
452+
returnNode, // eslint-disable-line react/prop-types
445453
}) => {
446454
useEffect(() => {
447455
const previewQuery = ((e: CustomEvent) => {
@@ -476,6 +484,53 @@ const QueryEditor: QueryEditorComponent = ({
476484
const [conditions, _setConditions] = useState(initialConditions);
477485
const [selections, setSelections] = useState(initialSelections);
478486
const [custom, setCustom] = useState(initialCustom);
487+
488+
const blockPropSyncTimeoutRef = useRef(0);
489+
const lastSyncedIndexRef = useRef("");
490+
useEffect(() => {
491+
return () => window.clearTimeout(blockPropSyncTimeoutRef.current);
492+
}, []);
493+
useEffect(() => {
494+
if (!discourseNodeType || !settingKey) return;
495+
496+
const stripped: unknown = JSON.parse(
497+
JSON.stringify(
498+
{ conditions, selections, custom, returnNode },
499+
(key, value: unknown) => (key === "uid" ? undefined : value),
500+
),
501+
);
502+
503+
const serialized = JSON.stringify(stripped);
504+
if (serialized === lastSyncedIndexRef.current) return;
505+
506+
const result = IndexSchema.safeParse(stripped);
507+
if (!result.success) {
508+
console.error(
509+
`${settingKey} blockprop sync failed validation:`,
510+
result.error,
511+
);
512+
return;
513+
}
514+
515+
const path =
516+
settingKey === "index" ? ["index"] : ["specification", "query"];
517+
518+
window.clearTimeout(blockPropSyncTimeoutRef.current);
519+
blockPropSyncTimeoutRef.current = window.setTimeout(() => {
520+
setDiscourseNodeSetting(discourseNodeType, path, result.data);
521+
lastSyncedIndexRef.current = serialized;
522+
}, 250);
523+
524+
return () => window.clearTimeout(blockPropSyncTimeoutRef.current);
525+
}, [
526+
conditions,
527+
selections,
528+
custom,
529+
discourseNodeType,
530+
settingKey,
531+
returnNode,
532+
]);
533+
479534
const customNodeOnChange = (value: string) => {
480535
window.clearTimeout(debounceRef.current);
481536
setCustom(value);

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import createBlock from "roamjs-components/writes/createBlock";
3838
import deleteBlock from "roamjs-components/writes/deleteBlock";
3939
import { USE_REIFIED_RELATIONS } from "~/data/userSettings";
4040
import posthog from "posthog-js";
41+
import { setFeatureFlag } from "~/components/settings/utils/accessors";
4142

4243
const NodeRow = ({ node }: { node: PConceptFull }) => {
4344
return (
@@ -377,6 +378,7 @@ const FeatureFlagsTab = (): React.ReactElement => {
377378
setSuggestiveModeUid(undefined);
378379
}
379380
setSuggestiveModeEnabled(false);
381+
setFeatureFlag("Suggestive mode enabled", false);
380382
}
381383
}}
382384
labelElement={
@@ -399,6 +401,7 @@ const FeatureFlagsTab = (): React.ReactElement => {
399401
}).then((uid) => {
400402
setSuggestiveModeUid(uid);
401403
setSuggestiveModeEnabled(true);
404+
setFeatureFlag("Suggestive mode enabled", true);
402405
setIsAlertOpen(false);
403406
setIsInstructionOpen(true);
404407
});
@@ -447,6 +450,7 @@ const FeatureFlagsTab = (): React.ReactElement => {
447450
void setSetting(USE_REIFIED_RELATIONS, target.checked).catch(
448451
() => undefined,
449452
);
453+
setFeatureFlag("Reified relation triples", target.checked);
450454
posthog.capture("Reified Relations: Toggled", {
451455
enabled: target.checked,
452456
});

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

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import React, { useEffect, useState } from "react";
33
import type { OnloadArgs } from "roamjs-components/types";
44
import type { Filters } from "roamjs-components/components/Filter";
55
import posthog from "posthog-js";
6+
import { setPersonalSetting } from "~/components/settings/utils/accessors";
67

78
//
89
// TODO - REWORK THIS COMPONENT
@@ -124,28 +125,27 @@ const DefaultFilters = ({
124125
);
125126

126127
useEffect(() => {
127-
extensionAPI.settings.set(
128-
"default-filters",
129-
Object.fromEntries(
130-
Object.entries(filters).map(([k, v]) => [
131-
k,
132-
{
133-
includes: Object.fromEntries(
134-
Object.entries(v.includes || {}).map(([k, v]) => [
135-
k,
136-
Array.from(v),
137-
]),
138-
),
139-
excludes: Object.fromEntries(
140-
Object.entries(v.excludes || {}).map(([k, v]) => [
141-
k,
142-
Array.from(v),
143-
]),
144-
),
145-
},
146-
]),
147-
),
128+
const serialized = Object.fromEntries(
129+
Object.entries(filters).map(([k, v]) => [
130+
k,
131+
{
132+
includes: Object.fromEntries(
133+
Object.entries(v.includes || {}).map(([k, v]) => [
134+
k,
135+
Array.from(v),
136+
]),
137+
),
138+
excludes: Object.fromEntries(
139+
Object.entries(v.excludes || {}).map(([k, v]) => [
140+
k,
141+
Array.from(v),
142+
]),
143+
),
144+
},
145+
]),
148146
);
147+
void extensionAPI.settings.set("default-filters", serialized);
148+
setPersonalSetting(["Query", "Default filters"], serialized);
149149
}, [filters]);
150150
return (
151151
<div

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

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import getBasicTreeByParentUid from "roamjs-components/queries/getBasicTreeByPar
55
import getFirstChildUidByBlockUid from "roamjs-components/queries/getFirstChildUidByBlockUid";
66
import updateBlock from "roamjs-components/writes/updateBlock";
77
import deleteBlock from "roamjs-components/writes/deleteBlock";
8+
import { setDiscourseNodeSetting } from "~/components/settings/utils/accessors";
89

910
type Attribute = {
1011
uid: string;
@@ -18,7 +19,12 @@ const NodeAttribute = ({
1819
value,
1920
onChange,
2021
onDelete,
21-
}: Attribute & { onChange: (v: string) => void; onDelete: () => void }) => {
22+
onSync,
23+
}: Attribute & {
24+
onChange: (v: string) => void;
25+
onDelete: () => void;
26+
onSync?: () => void;
27+
}) => {
2228
const timeoutRef = useRef(0);
2329
return (
2430
<div
@@ -40,6 +46,7 @@ const NodeAttribute = ({
4046
text: e.target.value,
4147
uid: getFirstChildUidByBlockUid(uid),
4248
});
49+
onSync?.();
4350
}, 500);
4451
}}
4552
/>
@@ -53,14 +60,32 @@ const NodeAttribute = ({
5360
);
5461
};
5562

56-
const NodeAttributes = ({ uid }: { uid: string }) => {
63+
const toRecord = (attrs: Attribute[]): Record<string, string> =>
64+
Object.fromEntries(attrs.map((a) => [a.label, a.value]));
65+
66+
const NodeAttributes = ({
67+
uid,
68+
nodeType,
69+
}: {
70+
uid: string;
71+
nodeType: string;
72+
}) => {
5773
const [attributes, setAttributes] = useState<Attribute[]>(() =>
5874
getBasicTreeByParentUid(uid).map((t) => ({
5975
uid: t.uid,
6076
label: t.text,
6177
value: t.children[0]?.text,
6278
})),
6379
);
80+
const attributesRef = useRef(attributes);
81+
attributesRef.current = attributes;
82+
const syncToBlockProps = () => {
83+
setDiscourseNodeSetting(
84+
nodeType,
85+
["attributes"],
86+
toRecord(attributesRef.current),
87+
);
88+
};
6489
const [newAttribute, setNewAttribute] = useState("");
6590
return (
6691
<div>
@@ -77,10 +102,17 @@ const NodeAttributes = ({ uid }: { uid: string }) => {
77102
)
78103
}
79104
onDelete={() =>
80-
deleteBlock(a.uid).then(() =>
81-
setAttributes(attributes.filter((aa) => a.uid !== aa.uid)),
82-
)
105+
deleteBlock(a.uid).then(() => {
106+
const updated = attributes.filter((aa) => a.uid !== aa.uid);
107+
setAttributes(updated);
108+
setDiscourseNodeSetting(
109+
nodeType,
110+
["attributes"],
111+
toRecord(updated),
112+
);
113+
})
83114
}
115+
onSync={syncToBlockProps}
84116
/>
85117
))}
86118
</div>
@@ -105,11 +137,17 @@ const NodeAttributes = ({ uid }: { uid: string }) => {
105137
parentUid: uid,
106138
order: attributes.length,
107139
}).then((uid) => {
108-
setAttributes([
140+
const updated = [
109141
...attributes,
110142
{ uid, label: newAttribute, value: DEFAULT },
111-
]);
143+
];
144+
setAttributes(updated);
112145
setNewAttribute("");
146+
setDiscourseNodeSetting(
147+
nodeType,
148+
["attributes"],
149+
toRecord(updated),
150+
);
113151
});
114152
}}
115153
/>

0 commit comments

Comments
 (0)