Skip to content

Commit 4877527

Browse files
authored
Merge pull request #861 from DiscourseGraphs/eng-1484-reactive-settings-some-settings-require-reloading-the-graph
ENG-1484: reactive settings for triggers and suggestive mode overlay
2 parents 8b23c58 + fd332e9 commit 4877527

7 files changed

Lines changed: 138 additions & 48 deletions

File tree

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

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ import {
1919
getFeatureFlag,
2020
setFeatureFlag,
2121
} from "~/components/settings/utils/accessors";
22+
import {
23+
onSettingChange,
24+
settingKeys,
25+
} from "~/components/settings/utils/settingsEmitter";
2226
import {
2327
getSupabaseContext,
2428
getLoggedInClient,
@@ -433,26 +437,18 @@ const FeatureFlagsTab = (): React.ReactElement => {
433437
<p>Are you sure you want to proceed?</p>
434438
</Alert>
435439

436-
{/* TODO(ENG-1484): Add pull watcher reactivity so toggling suggestive mode
437-
starts/stops sync and shows the tab without requiring a reload. */}
438440
<Alert
439441
isOpen={isInstructionOpen}
440-
onConfirm={() => window.location.reload()}
441-
onCancel={() => setIsInstructionOpen(false)}
442-
confirmButtonText="Reload Graph"
443-
cancelButtonText="Later"
442+
onConfirm={() => setIsInstructionOpen(false)}
443+
confirmButtonText="OK"
444444
intent={Intent.PRIMARY}
445445
>
446446
<p>
447447
If this is the first time enabling it, you will need to generate and
448448
upload all node embeddings to supabase.
449449
</p>
450450
<p>
451-
Please reload the graph to see the new &apos;Suggestive Mode&apos;
452-
tab.
453-
</p>
454-
<p>
455-
Then go to Suggestive Mode{" "}
451+
Go to Suggestive Mode{" "}
456452
{"-> Sync Config -> Click on 'Generate & Upload All Node Embeddings'"}
457453
</p>
458454
</Alert>
@@ -509,6 +505,15 @@ const FeatureFlagsTab = (): React.ReactElement => {
509505

510506
const AdminPanel = (): React.ReactElement => {
511507
const [selectedTabId, setSelectedTabId] = useState<TabId>("admin");
508+
const [showSuggestiveTab, setShowSuggestiveTab] = useState(
509+
getFeatureFlag("Suggestive mode enabled"),
510+
);
511+
512+
useEffect(() => {
513+
return onSettingChange(settingKeys.suggestiveModeEnabled, (newValue) => {
514+
setShowSuggestiveTab(Boolean(newValue));
515+
});
516+
}, []);
512517

513518
return (
514519
<Tabs
@@ -543,7 +548,7 @@ const AdminPanel = (): React.ReactElement => {
543548
</div>
544549
}
545550
/>
546-
{getFeatureFlag("Suggestive mode enabled") && (
551+
{showSuggestiveTab && (
547552
<Tab
548553
id="suggestive-mode-settings"
549554
title="Suggestive mode"

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

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,20 +39,12 @@ const HomePersonalSettings = ({ onloadArgs }: { onloadArgs: OnloadArgs }) => {
3939
<div className="flex flex-col gap-4 p-1">
4040
<Label>
4141
Personal node menu trigger
42-
<Description
43-
description={
44-
"Override the global trigger for the discourse node menu. Must refresh after editing."
45-
}
46-
/>
42+
<Description description="Override the global trigger for the discourse node menu." />
4743
<NodeMenuTriggerComponent extensionAPI={extensionAPI} />
4844
</Label>
4945
<Label>
5046
Node search menu trigger
51-
<Description
52-
description={
53-
"Set the trigger character for the node search menu. Must refresh after editing."
54-
}
55-
/>
47+
<Description description="Set the trigger character for the node search menu." />
5648
<NodeSearchMenuTriggerSetting onloadArgs={onloadArgs} />
5749
</Label>
5850
<KeyboardShortcutInput

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ export const featureFlagHandlers: Partial<
9595
"Enable left sidebar": (newValue, oldValue) => {
9696
emitSettingChange(settingKeys.leftSidebarFlag, newValue, oldValue);
9797
},
98+
"Suggestive mode enabled": (newValue, oldValue) => {
99+
emitSettingChange(settingKeys.suggestiveModeEnabled, newValue, oldValue);
100+
},
98101
/* eslint-enable @typescript-eslint/naming-convention */
99102
};
100103

@@ -111,6 +114,9 @@ export const globalSettingsHandlers: GlobalSettingsHandlers = {
111114
"Left sidebar": (newValue, oldValue) => {
112115
emitSettingChange(settingKeys.globalLeftSidebar, newValue, oldValue);
113116
},
117+
Trigger: (newValue, oldValue) => {
118+
emitSettingChange(settingKeys.globalTrigger, newValue, oldValue);
119+
},
114120
/* eslint-enable @typescript-eslint/naming-convention */
115121
};
116122

@@ -127,6 +133,19 @@ export const personalSettingsHandlers: PersonalSettingsHandlers = {
127133
"Left sidebar": (newValue, oldValue) => {
128134
emitSettingChange(settingKeys.personalLeftSidebar, newValue, oldValue);
129135
},
136+
"Personal node menu trigger": (newValue, oldValue) => {
137+
emitSettingChange(settingKeys.personalNodeMenuTrigger, newValue, oldValue);
138+
},
139+
"Node search menu trigger": (newValue, oldValue) => {
140+
emitSettingChange(settingKeys.nodeSearchMenuTrigger, newValue, oldValue);
141+
},
142+
"Suggestive mode overlay": (newValue, oldValue) => {
143+
emitSettingChange(
144+
settingKeys.personalSuggestiveModeOverlay,
145+
newValue,
146+
oldValue,
147+
);
148+
},
130149
/* eslint-enable @typescript-eslint/naming-convention */
131150
};
132151

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ export const settingKeys = {
44
leftSidebarFlag: "Enable left sidebar",
55
globalLeftSidebar: "global:Left sidebar",
66
personalLeftSidebar: "personal:Left sidebar",
7+
globalTrigger: "global:Trigger",
8+
personalNodeMenuTrigger: "personal:Personal node menu trigger",
9+
nodeSearchMenuTrigger: "personal:Node search menu trigger",
10+
personalSuggestiveModeOverlay: "personal:Suggestive mode overlay",
11+
suggestiveModeEnabled: "flag:Suggestive mode enabled",
712
} as const;
813

914
const listeners = new Map<string, Set<SettingChangeCallback>>();

apps/roam/src/index.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ export default runExtension(async (onloadArgs) => {
105105
streamlineStyleElement.id = "streamline-styling";
106106
}
107107

108-
const { observers, listeners } = initObservers({ onloadArgs });
108+
const { observers, listeners, cleanups } = initObservers({ onloadArgs });
109109
const {
110110
pageActionListener,
111111
hashChangeListener,
@@ -119,12 +119,21 @@ export default runExtension(async (onloadArgs) => {
119119
document.addEventListener("input", discourseNodeSearchTriggerListener);
120120
document.addEventListener("selectionchange", nodeCreationPopoverListener);
121121

122-
const isSuggestiveModeEnabled = getFeatureFlag("Suggestive mode enabled");
123-
124-
if (isSuggestiveModeEnabled) {
122+
if (getFeatureFlag("Suggestive mode enabled")) {
125123
initializeSupabaseSync();
126124
}
127125

126+
const unsubSuggestiveMode = onSettingChange(
127+
settingKeys.suggestiveModeEnabled,
128+
(newValue) => {
129+
if (newValue) {
130+
initializeSupabaseSync();
131+
} else {
132+
setSyncActivity(false);
133+
}
134+
},
135+
);
136+
128137
const { extensionAPI } = onloadArgs;
129138
window.roamjs.extension.queryBuilder = {
130139
runQuery: (parentUid: string) =>
@@ -194,7 +203,9 @@ export default runExtension(async (onloadArgs) => {
194203
observers: observers,
195204
unload: () => {
196205
unsubLeftSidebarFlag();
206+
unsubSuggestiveMode();
197207
cleanupPullWatchers();
208+
cleanups.forEach((fn) => fn());
198209
setSyncActivity(false);
199210
window.roamjs.extension?.smartblocks?.unregisterCommand("QUERYBUILDER");
200211
// @ts-expect-error - tldraw throws a warning on multiple loads

apps/roam/src/utils/initializeObserversAndListeners.ts

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ import {
5656
getFeatureFlag,
5757
getGlobalSetting,
5858
} from "~/components/settings/utils/accessors";
59+
import {
60+
onSettingChange,
61+
settingKeys,
62+
} from "~/components/settings/utils/settingsEmitter";
5963
import {
6064
PERSONAL_KEYS,
6165
GLOBAL_KEYS,
@@ -95,6 +99,7 @@ export const initObservers = ({
9599
discourseNodeSearchTriggerListener: EventListener;
96100
nodeCreationPopoverListener: EventListener;
97101
};
102+
cleanups: Array<() => void>;
98103
} => {
99104
const pageTitleObserver = createHTMLObserver({
100105
tag: "H1",
@@ -196,10 +201,19 @@ export const initObservers = ({
196201
});
197202
}) as EventListener;
198203

204+
const suggestiveHandler = getSuggestiveOverlayHandler(onloadArgs);
205+
const toggleSuggestiveOverlay = onPageRefObserverChange(suggestiveHandler);
199206
if (getPersonalSetting<boolean>([PERSONAL_KEYS.suggestiveModeOverlay])) {
200-
addPageRefObserver(getSuggestiveOverlayHandler(onloadArgs));
207+
addPageRefObserver(suggestiveHandler);
201208
}
202209

210+
const unsubSuggestiveOverlay = onSettingChange(
211+
settingKeys.personalSuggestiveModeOverlay,
212+
(newValue) => {
213+
toggleSuggestiveOverlay(Boolean(newValue));
214+
},
215+
);
216+
203217
const graphOverviewExportObserver = createHTMLObserver({
204218
tag: "DIV",
205219
className: "rm-graph-view-control-panel__main-options",
@@ -241,17 +255,36 @@ export const initObservers = ({
241255
}
242256
};
243257

244-
const globalTrigger = (
258+
let globalTrigger = (
245259
getGlobalSetting<string>([GLOBAL_KEYS.trigger]) ?? "\\"
246260
).trim();
247261
const personalTriggerCombo = getPersonalSetting<IKeyCombo>([
248262
PERSONAL_KEYS.personalNodeMenuTrigger,
249263
]);
250-
const personalTrigger = personalTriggerCombo?.key;
251-
const personalModifiers = personalTriggerCombo
264+
let personalTrigger = personalTriggerCombo?.key;
265+
let personalModifiers = personalTriggerCombo
252266
? getModifiersFromCombo(personalTriggerCombo)
253267
: [];
254268

269+
const unsubGlobalTrigger = onSettingChange(
270+
settingKeys.globalTrigger,
271+
(newValue) => {
272+
globalTrigger = (newValue as string).trim();
273+
},
274+
);
275+
276+
const unsubPersonalTrigger = onSettingChange(
277+
settingKeys.personalNodeMenuTrigger,
278+
(newValue) => {
279+
const combo =
280+
newValue && typeof newValue === "object"
281+
? (newValue as IKeyCombo)
282+
: undefined;
283+
personalTrigger = combo?.key;
284+
personalModifiers = combo ? getModifiersFromCombo(combo) : [];
285+
},
286+
);
287+
255288
const leftSidebarObserver = createHTMLObserver({
256289
tag: "DIV",
257290
useBody: true,
@@ -309,9 +342,16 @@ export const initObservers = ({
309342
}
310343
};
311344

312-
const customTrigger =
345+
let customTrigger =
313346
getPersonalSetting<string>([PERSONAL_KEYS.nodeSearchMenuTrigger]) ?? "@";
314347

348+
const unsubSearchTrigger = onSettingChange(
349+
settingKeys.nodeSearchMenuTrigger,
350+
(newValue) => {
351+
customTrigger = newValue as string;
352+
},
353+
);
354+
315355
const discourseNodeSearchTriggerListener = (e: Event) => {
316356
const evt = e as KeyboardEvent;
317357
const target = evt.target as HTMLElement;
@@ -414,5 +454,11 @@ export const initObservers = ({
414454
discourseNodeSearchTriggerListener,
415455
nodeCreationPopoverListener,
416456
},
457+
cleanups: [
458+
unsubGlobalTrigger,
459+
unsubPersonalTrigger,
460+
unsubSearchTrigger,
461+
unsubSuggestiveOverlay,
462+
],
417463
};
418464
};

0 commit comments

Comments
 (0)