From a9a3dd5a6d0995f84ce54761db12d36032559e61 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Parent Date: Wed, 25 Feb 2026 18:13:03 -0500 Subject: [PATCH 1/8] eng-1407 move stored relation settings --- .../src/components/settings/AdminPanel.tsx | 130 ---------- .../settings/HomePersonalSettings.tsx | 243 +++++++++++++++++- .../settings/utils/zodSchema.example.ts | 4 +- .../components/settings/utils/zodSchema.ts | 2 +- apps/roam/src/utils/migrateRelations.ts | 11 +- 5 files changed, 250 insertions(+), 140 deletions(-) diff --git a/apps/roam/src/components/settings/AdminPanel.tsx b/apps/roam/src/components/settings/AdminPanel.tsx index 85ba13269..4baac12b4 100644 --- a/apps/roam/src/components/settings/AdminPanel.tsx +++ b/apps/roam/src/components/settings/AdminPanel.tsx @@ -14,7 +14,6 @@ import { } from "@blueprintjs/core"; import Description from "roamjs-components/components/Description"; import { Select } from "@blueprintjs/select"; -import { getSetting, setSetting } from "~/utils/extensionSettings"; import { getSupabaseContext, getLoggedInClient, @@ -27,8 +26,6 @@ import { type NodeSignature, type PConceptFull, } from "@repo/database/lib/queries"; -import migrateRelations from "~/utils/migrateRelations"; -import { countReifiedRelations } from "~/utils/createReifiedBlock"; import type { DGSupabaseClient } from "@repo/database/lib/client"; import internalError from "~/utils/internalError"; import SuggestiveModeSettings from "./SuggestiveModeSettings"; @@ -36,8 +33,6 @@ import { getFormattedConfigTree } from "~/utils/discourseConfigRef"; import refreshConfigTree from "~/utils/refreshConfigTree"; import createBlock from "roamjs-components/writes/createBlock"; import deleteBlock from "roamjs-components/writes/deleteBlock"; -import { USE_REIFIED_RELATIONS } from "~/data/userSettings"; -import posthog from "posthog-js"; import { setFeatureFlag } from "~/components/settings/utils/accessors"; const NodeRow = ({ node }: { node: PConceptFull }) => { @@ -258,98 +253,7 @@ const NodeListTab = (): React.ReactElement => { ); }; -const MigrationTab = (): React.ReactElement => { - let initial = true; - const [useMigrationResults, setMigrationResults] = useState(""); - const [useOngoing, setOngoing] = useState(false); - const [useDryRun, setDryRun] = useState(false); - const enabled = getSetting(USE_REIFIED_RELATIONS, false); - const doMigrateRelations = async () => { - setOngoing(true); - try { - posthog.capture("Reified Relations: Migration Started", { - dryRun: useDryRun, - }); - const before = await countReifiedRelations(); - const numProcessed = await migrateRelations(useDryRun); - const after = await countReifiedRelations(); - if (after - before < numProcessed) - setMigrationResults( - `${after - before} new relations created out of ${numProcessed} distinct relations processed`, - ); - else setMigrationResults(`${numProcessed} new relations created`); - posthog.capture("Reified Relations: Migration Completed", { - dryRun: useDryRun, - processed: numProcessed, - before, - after, - created: after - before, - }); - } catch (e) { - console.error("Relation migration failed", e); - setMigrationResults( - `Migration failed: ${(e as Error).message ?? "see console for details"}`, - ); - posthog.capture("Reified Relations: Migration Failed", { - dryRun: useDryRun, - error: (e as Error).message ?? "unknown error", - }); - } finally { - setOngoing(false); - } - }; - useEffect(() => { - void (async () => { - if (initial) { - const numRelations = await countReifiedRelations(); - setMigrationResults( - numRelations > 0 - ? `${numRelations} already migrated` - : "No migrated relations", - ); - // eslint-disable-next-line react-hooks/exhaustive-deps - initial = false; - } - })(); - return () => { - initial; - }; - }, []); - - return ( - <> -

- - { - const target = e.target as HTMLInputElement; - setDryRun(target.checked); - }} - labelElement={<>Dry run} - /> -

- {useOngoing ? ( - - ) : ( -

{useMigrationResults}

- )} - - ); -}; - const FeatureFlagsTab = (): React.ReactElement => { - const [useReifiedRelations, setUseReifiedRelations] = useState( - getSetting(USE_REIFIED_RELATIONS, false), - ); const settings = useMemo(() => { refreshConfigTree(); return getFormattedConfigTree(); @@ -442,31 +346,6 @@ const FeatureFlagsTab = (): React.ReactElement => {

- { - const target = e.target as HTMLInputElement; - setUseReifiedRelations(target.checked); - void setSetting(USE_REIFIED_RELATIONS, target.checked).catch( - () => undefined, - ); - setFeatureFlag("Reified relation triples", target.checked); - posthog.capture("Reified Relations: Toggled", { - enabled: target.checked, - }); - }} - labelElement={ - <> - Reified relation triples - - - } - /> - + + + + + )} + + + { + setActiveRelationMigration(RelationMigrationDialog.none); + }} + > +
+

+ Deactivating the faster relations system will mean that any + relations created using it will no longer be accessible. The + discourse context overlay will still be usable with the previous + relations system. Any relations created with the faster system will + be accessible should you choose to reactivate. +

+
+ + +
+
+
+ { + setActiveRelationMigration(RelationMigrationDialog.none); + }} + > +
+ {isOngoing ? ( +

Migrating relations, please wait

+ ) : ( +
+

+ Activating the stored relations system will migrate all + previously created relations and newly created relations will + use the new system. You can deactivate this setting to revert to + the old system, and your newly created relations will not be + deleted; however, they will not be accessible until you + reactivate the stored relation system. +

+
+ + +
+
+ )} +
+
); }; diff --git a/apps/roam/src/components/settings/utils/zodSchema.example.ts b/apps/roam/src/components/settings/utils/zodSchema.example.ts index f413d26e2..e71c73ef4 100644 --- a/apps/roam/src/components/settings/utils/zodSchema.example.ts +++ b/apps/roam/src/components/settings/utils/zodSchema.example.ts @@ -89,13 +89,11 @@ const discourseNodeSettings: DiscourseNodeSettings = { const featureFlags: FeatureFlags = { "Enable left sidebar": true, "Suggestive mode enabled": true, - "Reified relation triples": false, }; const defaultFeatureFlags: FeatureFlags = { "Enable left sidebar": false, "Suggestive mode enabled": false, - "Reified relation triples": false, }; const exportSettings: ExportSettings = { @@ -373,6 +371,7 @@ const personalSettings: PersonalSettings = { "Streamline styling": true, "Auto canvas relations": true, "Disable product diagnostics": false, + "Reified relation triples": true, Query: { "Hide query metadata": true, "Default page size": 25, @@ -401,6 +400,7 @@ const defaultPersonalSettings: PersonalSettings = { "Streamline styling": false, "Auto canvas relations": false, "Disable product diagnostics": false, + "Reified relation triples": false, Query: { "Hide query metadata": false, "Default page size": 10, diff --git a/apps/roam/src/components/settings/utils/zodSchema.ts b/apps/roam/src/components/settings/utils/zodSchema.ts index 378020d42..60ec5f331 100644 --- a/apps/roam/src/components/settings/utils/zodSchema.ts +++ b/apps/roam/src/components/settings/utils/zodSchema.ts @@ -156,7 +156,6 @@ export const DiscourseRelationSchema = z.object({ export const FeatureFlagsSchema = z.object({ "Enable left sidebar": z.boolean().default(false), "Suggestive mode enabled": z.boolean().default(false), - "Reified relation triples": z.boolean().default(false), }); export const ExportSettingsSchema = z.object({ @@ -248,6 +247,7 @@ export const PersonalSettingsSchema = z.object({ .object({ modifiers: z.number(), key: z.string() }) .default({ modifiers: 0, key: "" }), "Discourse context overlay": z.boolean().default(false), + "Reified relation triples": z.boolean().default(false), "Suggestive mode overlay": z.boolean().default(false), "Overlay in canvas": z.boolean().default(false), "Text selection popup": z.boolean().default(true), diff --git a/apps/roam/src/utils/migrateRelations.ts b/apps/roam/src/utils/migrateRelations.ts index 41cba8014..dd50575b0 100644 --- a/apps/roam/src/utils/migrateRelations.ts +++ b/apps/roam/src/utils/migrateRelations.ts @@ -4,6 +4,7 @@ import type { json } from "./getBlockProps"; import setBlockProps from "./setBlockProps"; import { getSetting, setSetting } from "./extensionSettings"; import { USE_REIFIED_RELATIONS } from "~/data/userSettings"; +import internalError from "./internalError"; import { createReifiedRelation, DISCOURSE_GRAPH_PROP_NAME, @@ -11,10 +12,11 @@ import { const MIGRATION_PROP_NAME = "relation-migration"; -const migrateRelations = async (dryRun = false): Promise => { +const migrateRelations = async (dryRun = false): Promise => { const authorized = getSetting(USE_REIFIED_RELATIONS, false); if (!authorized) return 0; let numProcessed = 0; + let didError = false; await setSetting(USE_REIFIED_RELATIONS, false); // so queries use patterns // wait for the settings to propagate await new Promise((resolve) => setTimeout(resolve, 150)); @@ -64,10 +66,11 @@ const migrateRelations = async (dryRun = false): Promise => { } numProcessed++; } - } finally { - await setSetting(USE_REIFIED_RELATIONS, true); + } catch (error) { + internalError({ error }); + didError = true; } - return numProcessed; + return didError ? false : numProcessed; }; export default migrateRelations; From 3fb8f5877dda3fc5bb5d0b88082c7aca01058e50 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Parent Date: Thu, 26 Feb 2026 16:07:35 -0500 Subject: [PATCH 2/8] WIP: change value only after the buttons. Somehow not persistent yet. --- .../settings/HomePersonalSettings.tsx | 38 +++++----- apps/roam/src/utils/migrateRelations.ts | 74 +++++++++---------- 2 files changed, 55 insertions(+), 57 deletions(-) diff --git a/apps/roam/src/components/settings/HomePersonalSettings.tsx b/apps/roam/src/components/settings/HomePersonalSettings.tsx index 945fa48f5..59860b8fb 100644 --- a/apps/roam/src/components/settings/HomePersonalSettings.tsx +++ b/apps/roam/src/components/settings/HomePersonalSettings.tsx @@ -34,6 +34,7 @@ import migrateRelations from "~/utils/migrateRelations"; import { countReifiedRelations } from "~/utils/createReifiedBlock"; import posthog from "posthog-js"; import internalError from "~/utils/internalError"; +import { setPersonalSetting } from "./utils/accessors"; const enum RelationMigrationDialog { "none", @@ -50,13 +51,18 @@ const HomePersonalSettings = ({ onloadArgs }: { onloadArgs: OnloadArgs }) => { useState(RelationMigrationDialog.none); const [numExistingRelations, setNumExistingRelations] = useState(0); const [isOngoing, setOngoing] = useState(false); - const setStoredMigration = async (active: boolean) => { - await setSetting(USE_REIFIED_RELATIONS, active); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + let settingStoredMigrationValue = false; + const setStoredRelations = (enabled: boolean) => { const panel = document.getElementById("stored-relation-flag"); const checkboxList = panel?.getElementsByTagName("input"); if (checkboxList && checkboxList.length > 0) { const checkbox = checkboxList.item(0)!; - checkbox.checked = active; + if (checkbox.checked !== enabled) { + settingStoredMigrationValue = true; + checkbox.click(); + settingStoredMigrationValue = false; + } } }; const startMigration = async (): Promise => { @@ -70,7 +76,7 @@ const HomePersonalSettings = ({ onloadArgs }: { onloadArgs: OnloadArgs }) => { intent: Intent.DANGER, id: "migration-error", }); - await setStoredMigration(false); + setStoredRelations(false); return; } const after = await countReifiedRelations(); @@ -93,13 +99,13 @@ const HomePersonalSettings = ({ onloadArgs }: { onloadArgs: OnloadArgs }) => { after, created: after - before, }); - await setStoredMigration(true); + setStoredRelations(true); } catch (error) { internalError({ error, userMessage: "Reified Relations: Migration Failed", }); - await setStoredMigration(false); + setStoredRelations(false); } finally { setOngoing(false); setActiveRelationMigration(RelationMigrationDialog.none); @@ -166,25 +172,26 @@ const HomePersonalSettings = ({ onloadArgs }: { onloadArgs: OnloadArgs }) => { title="Enable stored relations" description="Transition to using stored relations instead of pattern-based relations" settingKeys={["Reified relation triples"]} - initialValue={getSetting(USE_REIFIED_RELATIONS, true)} - onChange={(checked) => { + initialValue={getSetting(USE_REIFIED_RELATIONS, false)} + onBeforeChange={async (checked) => { + if (settingStoredMigrationValue) return true; if (checked) { countReifiedRelations() .then((num: number) => { setNumExistingRelations(num); setActiveRelationMigration( - num + num > 0 ? RelationMigrationDialog.reactivate : RelationMigrationDialog.activate, ); }) .catch((error) => { internalError({ error }); - setActiveRelationMigration(RelationMigrationDialog.none); }); } else { setActiveRelationMigration(RelationMigrationDialog.deactivate); } + return false; }} /> @@ -299,8 +306,8 @@ const HomePersonalSettings = ({ onloadArgs }: { onloadArgs: OnloadArgs }) => {

Activating the faster relations system will migrate all previously created relations and newly created relations will - use the new system. You can leactivate this setting to revert to - the old system, and your newly created elations will not be + use the new system. You can deactivate this setting to revert to + the old system, and your newly created relations will not be deleted; however, they will not be accessible until you reactivate the faster relation system.

@@ -311,7 +318,6 @@ const HomePersonalSettings = ({ onloadArgs }: { onloadArgs: OnloadArgs }) => { From 62fea52d22dbf7663bf40b59ec796bd9867597e3 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Parent Date: Fri, 27 Feb 2026 14:33:41 -0500 Subject: [PATCH 7/8] another ai correction --- apps/roam/src/components/settings/HomePersonalSettings.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/roam/src/components/settings/HomePersonalSettings.tsx b/apps/roam/src/components/settings/HomePersonalSettings.tsx index 185541682..86b94f763 100644 --- a/apps/roam/src/components/settings/HomePersonalSettings.tsx +++ b/apps/roam/src/components/settings/HomePersonalSettings.tsx @@ -54,6 +54,7 @@ const HomePersonalSettings = ({ onloadArgs }: { onloadArgs: OnloadArgs }) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const settingStoredMigrationValue = useRef(); const setStoredRelations = async (enabled: boolean) => { + await setSetting(USE_REIFIED_RELATIONS, enabled); const panel = document.getElementById("stored-relation-flag"); const checkboxList = panel?.getElementsByTagName("input"); if (checkboxList && checkboxList.length > 0) { @@ -61,7 +62,6 @@ const HomePersonalSettings = ({ onloadArgs }: { onloadArgs: OnloadArgs }) => { if (checkbox.checked !== enabled) { settingStoredMigrationValue.current = true; checkbox.click(); - await setSetting(USE_REIFIED_RELATIONS, enabled); settingStoredMigrationValue.current = false; } } From 516959f4bb1212f47c3ceaa010af82dd4493ce34 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Parent Date: Fri, 27 Feb 2026 14:36:04 -0500 Subject: [PATCH 8/8] linting --- apps/roam/src/components/settings/HomePersonalSettings.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/roam/src/components/settings/HomePersonalSettings.tsx b/apps/roam/src/components/settings/HomePersonalSettings.tsx index 86b94f763..9f31ce212 100644 --- a/apps/roam/src/components/settings/HomePersonalSettings.tsx +++ b/apps/roam/src/components/settings/HomePersonalSettings.tsx @@ -34,7 +34,6 @@ import migrateRelations from "~/utils/migrateRelations"; import { countReifiedRelations } from "~/utils/createReifiedBlock"; import posthog from "posthog-js"; import internalError from "~/utils/internalError"; -import { setPersonalSetting } from "./utils/accessors"; const enum RelationMigrationDialog { "none", @@ -174,6 +173,7 @@ const HomePersonalSettings = ({ onloadArgs }: { onloadArgs: OnloadArgs }) => { description="Transition to using stored relations instead of pattern-based relations" settingKeys={["Reified relation triples"]} initialValue={getSetting(USE_REIFIED_RELATIONS, false)} + // eslint-disable-next-line @typescript-eslint/require-await onBeforeChange={async (checked) => { if (settingStoredMigrationValue.current) return true; if (checked) {