From c031c3c9cbad0a5bd1cebe3372a929f257934a1c Mon Sep 17 00:00:00 2001 From: sid597 Date: Sun, 5 Jul 2026 23:54:06 +0530 Subject: [PATCH 1/2] ENG-1819: Make global left sidebar fold toggle personal Fold state moves from the shared global setting to a per-user personal setting (PERSONAL_KEYS.globalSectionFolded, legacy dual-write via a {userUid}/Global-Section-Folded marker block). The admin Collapsable flag is removed: the global section is now always foldable per user. Based on prior branch commit d51649d9 reconciled onto current main. --- apps/roam/src/components/LeftSidebarView.tsx | 59 ++++++++---------- .../settings/LeftSidebarGlobalSettings.tsx | 33 ---------- .../components/settings/utils/accessors.ts | 13 ++-- .../src/components/settings/utils/init.ts | 2 +- .../components/settings/utils/settingKeys.ts | 10 +-- .../settings/utils/zodSchema.example.ts | 14 +---- .../components/settings/utils/zodSchema.ts | 7 +-- apps/roam/src/utils/getLeftSidebarSettings.ts | 61 +++---------------- 8 files changed, 47 insertions(+), 152 deletions(-) diff --git a/apps/roam/src/components/LeftSidebarView.tsx b/apps/roam/src/components/LeftSidebarView.tsx index efad98788..8a7750296 100644 --- a/apps/roam/src/components/LeftSidebarView.tsx +++ b/apps/roam/src/components/LeftSidebarView.tsx @@ -32,6 +32,7 @@ import { isQueryBlockRef, type LeftSidebarConfig, type LeftSidebarPersonalSectionConfig, + getGlobalSectionFoldedMarkerText, mergeGlobalSectionWithAccessor, mergePersonalSectionsWithAccessor, } from "~/utils/getLeftSidebarSettings"; @@ -51,7 +52,6 @@ import { PERSONAL_KEYS, GLOBAL_KEYS, LEFT_SIDEBAR_KEYS, - LEFT_SIDEBAR_SETTINGS_KEYS, } from "~/components/settings/utils/settingKeys"; import type { LeftSidebarGlobalSettings } from "~/components/settings/utils/zodSchema"; import { createBlock } from "roamjs-components/writes"; @@ -617,53 +617,45 @@ const PersonalSections = ({ const GlobalSection = ({ config, + folded, + leftSidebarUid, onGlobalChildrenReorder, onloadArgs, }: { config: LeftSidebarConfig["global"]; + folded: boolean; + leftSidebarUid: string; onGlobalChildrenReorder: (oldIndex: number, newIndex: number) => void; onloadArgs: OnloadArgs; }) => { - const [isOpen, setIsOpen] = useState(!config.settings?.folded.value); + const [isOpen, setIsOpen] = useState(!folded); const isTogglingRef = useRef(false); if (!config.children?.length) return null; - const isCollapsable = config.settings?.collapsable.value; const handleToggle = async () => { - if (!isCollapsable || !config.settings) return; if (isTogglingRef.current) return; isTogglingRef.current = true; try { - const settings = config.settings; const nextIsOpen = !isOpen; setIsOpen(nextIsOpen); + const markerText = getGlobalSectionFoldedMarkerText( + window.roamAlphaAPI.user.uid() || "", + ); if (nextIsOpen) { - const children = getBasicTreeByParentUid(settings.uid); + const children = getBasicTreeByParentUid(leftSidebarUid); await Promise.all( children - .filter((c) => c.text === "Folded") + .filter((c) => c.text === markerText) .map((c) => deleteBlock(c.uid)), ); - settings.folded.uid = undefined; - settings.folded.value = false; } else { - const newUid = window.roamAlphaAPI.util.generateUID(); await createBlock({ - parentUid: settings.uid, - node: { text: "Folded", uid: newUid }, + parentUid: leftSidebarUid, + node: { text: markerText }, }); - settings.folded.uid = newUid; - settings.folded.value = true; } refreshConfigTree(); - setGlobalSetting( - [ - GLOBAL_KEYS.leftSidebar, - LEFT_SIDEBAR_KEYS.settings, - LEFT_SIDEBAR_SETTINGS_KEYS.folded, - ], - !nextIsOpen, - ); + setPersonalSetting([PERSONAL_KEYS.globalSectionFolded], !nextIsOpen); } finally { isTogglingRef.current = false; } @@ -690,18 +682,12 @@ const GlobalSection = ({ >
GLOBAL - {isCollapsable && ( - - - - )} + + +
- {isCollapsable ? ( - {children} - ) : ( - children - )} + {children} ); }; @@ -718,6 +704,9 @@ const buildConfig = (snapshot?: SettingsSnapshot): LeftSidebarConfig => { : getPersonalSetting< ReturnType[typeof PERSONAL_KEYS.leftSidebar] >([PERSONAL_KEYS.leftSidebar]); + const globalSectionFoldedValue = snapshot + ? snapshot.personalSettings[PERSONAL_KEYS.globalSectionFolded] + : getPersonalSetting([PERSONAL_KEYS.globalSectionFolded]); // Read UIDs from old system (needed for fold CRUD during dual-write) const oldConfig = getCurrentLeftSidebarConfig(); @@ -727,6 +716,10 @@ const buildConfig = (snapshot?: SettingsSnapshot): LeftSidebarConfig => { favoritesMigrated: oldConfig.favoritesMigrated, sidebarMigrated: oldConfig.sidebarMigrated, global: mergeGlobalSectionWithAccessor(oldConfig.global, globalValues), + globalSectionFolded: { + uid: oldConfig.globalSectionFolded.uid, + value: globalSectionFoldedValue ?? oldConfig.globalSectionFolded.value, + }, personal: { uid: oldConfig.personal.uid, sections: mergePersonalSectionsWithAccessor( @@ -916,6 +909,8 @@ const LeftSidebarView = ({ diff --git a/apps/roam/src/components/settings/LeftSidebarGlobalSettings.tsx b/apps/roam/src/components/settings/LeftSidebarGlobalSettings.tsx index 28cb045b2..2e412976a 100644 --- a/apps/roam/src/components/settings/LeftSidebarGlobalSettings.tsx +++ b/apps/roam/src/components/settings/LeftSidebarGlobalSettings.tsx @@ -1,7 +1,6 @@ import React, { useCallback, useEffect, useMemo, useState, memo } from "react"; import { Button, ButtonGroup, Collapse } from "@blueprintjs/core"; import { arrayMove } from "@dnd-kit/sortable"; -import { GlobalFlagPanel } from "~/components/settings/components/BlockPropSettingPanels"; import { setGlobalSetting, type SettingsSnapshot, @@ -9,7 +8,6 @@ import { import { GLOBAL_KEYS, LEFT_SIDEBAR_KEYS, - LEFT_SIDEBAR_SETTINGS_KEYS, } from "~/components/settings/utils/settingKeys"; import AutocompleteInput from "roamjs-components/components/AutocompleteInput"; import getAllPageNames from "roamjs-components/queries/getAllPageNames"; @@ -118,11 +116,6 @@ const LeftSidebarGlobalSectionsContent = ({ order: 0, node: { text: globalSectionText }, }); - const settingsUid = await createBlock({ - parentUid: globalSectionUid, - order: 0, - node: { text: "Settings" }, - }); const childrenUid = await createBlock({ parentUid: globalSectionUid, order: 0, @@ -132,11 +125,6 @@ const LeftSidebarGlobalSectionsContent = ({ setPages([]); setGlobalSection({ uid: globalSectionUid, - settings: { - uid: settingsUid, - collapsable: { uid: undefined, value: false }, - folded: { uid: undefined, value: false }, - }, childrenUid, children: [], }); @@ -289,27 +277,6 @@ const LeftSidebarGlobalSectionsContent = ({ return (
-
- -
-
{ return readPathValue(leftSidebarSettings, keys.slice(1)); } + if (keys[0] === "Global section folded") { + return getLeftSidebarSettings(discourseConfigRef.tree).globalSectionFolded + .value; + } + return undefined; }; @@ -403,14 +408,6 @@ const getLegacyGlobalSetting = (keys: string[]): unknown => { leftSidebarSettings["Children"] = sidebar.global.children.map( (c) => c.text, ); - const sidebarSettingValues: Record = {}; - sidebarSettingValues["Collapsable"] = - sidebar.global.settings?.collapsable.value ?? - DEFAULT_GLOBAL_SETTINGS["Left sidebar"].Settings.Collapsable; - sidebarSettingValues["Folded"] = - sidebar.global.settings?.folded.value ?? - DEFAULT_GLOBAL_SETTINGS["Left sidebar"].Settings.Folded; - leftSidebarSettings["Settings"] = sidebarSettingValues; if (keys.length === 1) return leftSidebarSettings; return readPathValue(leftSidebarSettings, keys.slice(1)); } diff --git a/apps/roam/src/components/settings/utils/init.ts b/apps/roam/src/components/settings/utils/init.ts index 4a0dacfa3..9d6116766 100644 --- a/apps/roam/src/components/settings/utils/init.ts +++ b/apps/roam/src/components/settings/utils/init.ts @@ -125,7 +125,7 @@ const ensureLegacyConfigBlocks = async (pageUid: string): Promise => { const globalSectionMap = buildBlockMap(leftSidebarMap["Global-Section"]); await ensureBlocksExist( leftSidebarMap["Global-Section"], - ["Children", "Settings"], + ["Children"], globalSectionMap, ); diff --git a/apps/roam/src/components/settings/utils/settingKeys.ts b/apps/roam/src/components/settings/utils/settingKeys.ts index 827f26100..9557c2691 100644 --- a/apps/roam/src/components/settings/utils/settingKeys.ts +++ b/apps/roam/src/components/settings/utils/settingKeys.ts @@ -32,6 +32,7 @@ export const PERSONAL_KEYS = { personalNodeMenuTrigger: "Personal node menu trigger", nodeSearchMenuTrigger: "Node search menu trigger", leftSidebar: "Left sidebar", + globalSectionFolded: "Global section folded", query: "Query", reifiedRelationTriples: "Reified relation triples", canvasNodeShortcuts: "Canvas node shortcuts", @@ -61,7 +62,6 @@ export const SUGGESTIVE_MODE_KEYS = { export const LEFT_SIDEBAR_KEYS = { children: "Children", - settings: "Settings", } as const satisfies Record; export const EXPORT_KEYS = { @@ -74,14 +74,6 @@ export const EXPORT_KEYS = { frontmatter: "Frontmatter", } as const satisfies Record; -export const LEFT_SIDEBAR_SETTINGS_KEYS = { - collapsable: "Collapsable", - folded: "Folded", -} as const satisfies Record< - string, - keyof LeftSidebarGlobalSettings["Settings"] ->; - export const DISCOURSE_NODE_KEYS = { canvasSettings: "canvasSettings", overlay: "overlay", diff --git a/apps/roam/src/components/settings/utils/zodSchema.example.ts b/apps/roam/src/components/settings/utils/zodSchema.example.ts index 3b3f3cd1d..fa51f8584 100644 --- a/apps/roam/src/components/settings/utils/zodSchema.example.ts +++ b/apps/roam/src/components/settings/utils/zodSchema.example.ts @@ -137,10 +137,6 @@ const suggestiveModeGlobalSettings: SuggestiveModeGlobalSettings = { const leftSidebarGlobalSettings: LeftSidebarGlobalSettings = { Children: ["daily-notes-uid", "quick-capture-uid", "inbox-uid"], - Settings: { - Collapsable: true, - Folded: false, - }, }; const globalSettings: GlobalSettings = { @@ -148,10 +144,6 @@ const globalSettings: GlobalSettings = { "Canvas page format": "Canvas - {date} - {title}", "Left sidebar": { Children: ["daily-notes-uid", "quick-capture-uid", "inbox-uid"], - Settings: { - Collapsable: true, - Folded: false, - }, }, Export: { "Remove special characters": true, @@ -255,10 +247,6 @@ const defaultGlobalSettings: GlobalSettings = { "Canvas page format": "", "Left sidebar": { Children: [], - Settings: { - Collapsable: false, - Folded: false, - }, }, Export: { "Remove special characters": false, @@ -372,6 +360,7 @@ const personalSettings: PersonalSettings = { }, }, ], + "Global section folded": false, "Personal node menu trigger": { modifiers: 0, key: ";;" }, "Node search menu trigger": "//", "Discourse tool shortcut": { modifiers: 0, key: "d" }, @@ -404,6 +393,7 @@ const personalSettings: PersonalSettings = { const defaultPersonalSettings: PersonalSettings = { "Left sidebar": [], + "Global section folded": false, "Personal node menu trigger": { modifiers: 0, key: "" }, "Node search menu trigger": "", "Discourse tool shortcut": { modifiers: 0, key: "" }, diff --git a/apps/roam/src/components/settings/utils/zodSchema.ts b/apps/roam/src/components/settings/utils/zodSchema.ts index 992635fb1..48d6dcb94 100644 --- a/apps/roam/src/components/settings/utils/zodSchema.ts +++ b/apps/roam/src/components/settings/utils/zodSchema.ts @@ -185,12 +185,6 @@ export const SuggestiveModeGlobalSettingsSchema = z.object({ export const LeftSidebarGlobalSettingsSchema = z.object({ Children: z.array(z.string()).default([]), - Settings: z - .object({ - Collapsable: z.boolean().default(false), - Folded: z.boolean().default(false), - }) - .default({}), }); export const GlobalSettingsSchema = z.object({ @@ -242,6 +236,7 @@ export const QuerySettingsSchema = z.object({ export const PersonalSettingsSchema = z.object({ "Left sidebar": LeftSidebarPersonalSettingsSchema, + "Global section folded": z.boolean().default(false), "Personal node menu trigger": z .union([ z.object({ modifiers: z.number(), key: z.string() }), diff --git a/apps/roam/src/utils/getLeftSidebarSettings.ts b/apps/roam/src/utils/getLeftSidebarSettings.ts index 4c61d2bfc..fc2579c83 100644 --- a/apps/roam/src/utils/getLeftSidebarSettings.ts +++ b/apps/roam/src/utils/getLeftSidebarSettings.ts @@ -65,15 +65,8 @@ export type LeftSidebarPersonalSectionConfig = { childrenUid?: string; }; -type LeftSidebarGlobalSectionSettings = { - uid: string; - collapsable: BooleanSetting; - folded: BooleanSetting; -}; - export type LeftSidebarGlobalSectionConfig = { uid: string; - settings?: LeftSidebarGlobalSectionSettings; children: RoamBasicNode[]; childrenUid: string; }; @@ -83,6 +76,7 @@ export type LeftSidebarConfig = { favoritesMigrated: BooleanSetting; sidebarMigrated: BooleanSetting; global: LeftSidebarGlobalSectionConfig; + globalSectionFolded: BooleanSetting; allPersonalSections: AllUsersPersonalSections; personal: { uid: string; @@ -90,24 +84,8 @@ export type LeftSidebarConfig = { }; }; -const getGlobalSectionSettings = ( - settingsNode: RoamBasicNode, -): LeftSidebarGlobalSectionSettings => { - const settingsTree = settingsNode?.children || []; - const collapsableSetting = getUidAndBooleanSetting({ - tree: settingsTree, - text: "Collapsable", - }); - const foldedSetting = getUidAndBooleanSetting({ - tree: settingsTree, - text: "Folded", - }); - return { - uid: settingsNode.uid, - collapsable: collapsableSetting, - folded: foldedSetting, - }; -}; +export const getGlobalSectionFoldedMarkerText = (userUid: string): string => + `${userUid}/Global-Section-Folded`; export const getLeftSidebarGlobalSectionConfig = ( leftSidebarChildren: RoamBasicNode[], @@ -123,17 +101,8 @@ export const getLeftSidebarGlobalSectionConfig = ( key: "Children", }); - const settingsNode = getSubTree({ - tree: globalChildren, - key: "Settings", - }); - const settings = settingsNode - ? getGlobalSectionSettings(settingsNode) - : undefined; - return { uid: globalSectionNode?.uid || "", - settings, children: childrenNode?.children || [], childrenUid: childrenNode?.uid || "", }; @@ -271,23 +240,6 @@ export const mergeGlobalSectionWithAccessor = ( uid: config.uid, childrenUid: config.childrenUid, children, - settings: { - uid: config.settings?.uid ?? "", - collapsable: { - uid: config.settings?.collapsable.uid ?? "", - value: - globalValues?.Settings.Collapsable ?? - config.settings?.collapsable.value ?? - false, - }, - folded: { - uid: config.settings?.folded.uid ?? "", - value: - globalValues?.Settings.Folded ?? - config.settings?.folded.value ?? - false, - }, - }, }; }; @@ -365,11 +317,18 @@ export const getLeftSidebarSettings = ( tree: leftSidebarChildren, text: "Sidebar Migrated", }); + const globalSectionFolded = getUidAndBooleanSetting({ + tree: leftSidebarChildren, + text: getGlobalSectionFoldedMarkerText( + window.roamAlphaAPI.user.uid() || "", + ), + }); return { uid: leftSidebarUid, favoritesMigrated, sidebarMigrated, global, + globalSectionFolded, personal, allPersonalSections, }; From efcb1d6dd6395fe2760e802c165c284c01fe6d21 Mon Sep 17 00:00:00 2001 From: sid597 Date: Mon, 6 Jul 2026 13:48:27 +0530 Subject: [PATCH 2/2] ENG-1819: Skip fold marker persistence when user uid is unavailable Review follow-up: roamAlphaAPI.user.uid() is typed string | null. With no uid the fold toggle is now session-only instead of reading/writing a marker block that every uid-less user would share. --- apps/roam/src/components/LeftSidebarView.tsx | 33 ++++++++++--------- apps/roam/src/utils/getLeftSidebarSettings.ts | 13 ++++---- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/apps/roam/src/components/LeftSidebarView.tsx b/apps/roam/src/components/LeftSidebarView.tsx index 8a7750296..dad935f65 100644 --- a/apps/roam/src/components/LeftSidebarView.tsx +++ b/apps/roam/src/components/LeftSidebarView.tsx @@ -638,23 +638,24 @@ const GlobalSection = ({ try { const nextIsOpen = !isOpen; setIsOpen(nextIsOpen); - const markerText = getGlobalSectionFoldedMarkerText( - window.roamAlphaAPI.user.uid() || "", - ); - if (nextIsOpen) { - const children = getBasicTreeByParentUid(leftSidebarUid); - await Promise.all( - children - .filter((c) => c.text === markerText) - .map((c) => deleteBlock(c.uid)), - ); - } else { - await createBlock({ - parentUid: leftSidebarUid, - node: { text: markerText }, - }); + const userUid = window.roamAlphaAPI.user.uid(); + if (userUid) { + const markerText = getGlobalSectionFoldedMarkerText(userUid); + if (nextIsOpen) { + const children = getBasicTreeByParentUid(leftSidebarUid); + await Promise.all( + children + .filter((c) => c.text === markerText) + .map((c) => deleteBlock(c.uid)), + ); + } else { + await createBlock({ + parentUid: leftSidebarUid, + node: { text: markerText }, + }); + } + refreshConfigTree(); } - refreshConfigTree(); setPersonalSetting([PERSONAL_KEYS.globalSectionFolded], !nextIsOpen); } finally { isTogglingRef.current = false; diff --git a/apps/roam/src/utils/getLeftSidebarSettings.ts b/apps/roam/src/utils/getLeftSidebarSettings.ts index fc2579c83..a2d306029 100644 --- a/apps/roam/src/utils/getLeftSidebarSettings.ts +++ b/apps/roam/src/utils/getLeftSidebarSettings.ts @@ -317,12 +317,13 @@ export const getLeftSidebarSettings = ( tree: leftSidebarChildren, text: "Sidebar Migrated", }); - const globalSectionFolded = getUidAndBooleanSetting({ - tree: leftSidebarChildren, - text: getGlobalSectionFoldedMarkerText( - window.roamAlphaAPI.user.uid() || "", - ), - }); + const currentUserUid = window.roamAlphaAPI.user.uid(); + const globalSectionFolded: BooleanSetting = currentUserUid + ? getUidAndBooleanSetting({ + tree: leftSidebarChildren, + text: getGlobalSectionFoldedMarkerText(currentUserUid), + }) + : { uid: undefined, value: false }; return { uid: leftSidebarUid, favoritesMigrated,