Skip to content

Commit 26ebb72

Browse files
authored
ENG-1127 fix: update left sidebar when page titles change (#607)
* save block uids of pages instead of page titles itself * fix * use dry principle, fix after merge * add migration for existing users
1 parent 88756f4 commit 26ebb72

5 files changed

Lines changed: 136 additions & 36 deletions

File tree

apps/roam/src/components/LeftSidebarView.tsx

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ import type {
3030
LeftSidebarConfig,
3131
LeftSidebarPersonalSectionConfig,
3232
} from "~/utils/getLeftSidebarSettings";
33-
import type { BooleanSetting } from "~/utils/getExportSettings";
3433
import { createBlock } from "roamjs-components/writes";
3534
import deleteBlock from "roamjs-components/writes/deleteBlock";
3635
import getTextByBlockUid from "roamjs-components/queries/getTextByBlockUid";
@@ -41,6 +40,8 @@ import { OnloadArgs } from "roamjs-components/types";
4140
import renderOverlay from "roamjs-components/util/renderOverlay";
4241
import getBasicTreeByParentUid from "roamjs-components/queries/getBasicTreeByParentUid";
4342
import { DISCOURSE_CONFIG_PAGE_TITLE } from "~/utils/renderNodeConfigPage";
43+
import getPageTitleByPageUid from "roamjs-components/queries/getPageTitleByPageUid";
44+
import { migrateLeftSidebarSettings } from "~/utils/migrateLeftSidebarSettings";
4445

4546
const parseReference = (text: string) => {
4647
const extracted = extractRef(text);
@@ -56,10 +57,10 @@ const truncate = (s: string, max: number | undefined): string => {
5657
return s.length > max ? `${s.slice(0, max)}...` : s;
5758
};
5859

59-
const openTarget = async (e: React.MouseEvent, sectionTitle: string) => {
60+
const openTarget = async (e: React.MouseEvent, targetUid: string) => {
6061
e.preventDefault();
6162
e.stopPropagation();
62-
const target = parseReference(sectionTitle);
63+
const target = parseReference(targetUid);
6364
if (target.type === "block") {
6465
if (e.shiftKey) {
6566
await openBlockInSidebar(target.uid);
@@ -71,16 +72,16 @@ const openTarget = async (e: React.MouseEvent, sectionTitle: string) => {
7172
return;
7273
}
7374

74-
const uid = getPageUidByPageTitle(sectionTitle);
75-
if (!uid) return;
7675
if (e.shiftKey) {
7776
await window.roamAlphaAPI.ui.rightSidebar.addWindow({
7877
// @ts-expect-error - todo test
7978
// eslint-disable-next-line @typescript-eslint/naming-convention
80-
window: { type: "outline", "block-uid": uid },
79+
window: { type: "outline", "block-uid": targetUid },
8180
});
8281
} else {
83-
await window.roamAlphaAPI.ui.mainWindow.openPage({ page: { uid } });
82+
await window.roamAlphaAPI.ui.mainWindow.openPage({
83+
page: { uid: targetUid },
84+
});
8485
}
8586
};
8687

@@ -127,7 +128,11 @@ const SectionChildren = ({
127128
{childrenNodes.map((child) => {
128129
const ref = parseReference(child.text);
129130
const alias = child.alias?.value;
130-
const label = alias || truncate(ref.display, truncateAt);
131+
const display =
132+
ref.type === "page"
133+
? getPageTitleByPageUid(ref.display)
134+
: getTextByBlockUid(ref.uid);
135+
const label = alias || truncate(display, truncateAt);
131136
const onClick = (e: React.MouseEvent) => {
132137
return void openTarget(e, child.text);
133138
};
@@ -184,7 +189,7 @@ const PersonalSectionItem = ({
184189
onClick={() => {
185190
if ((section.children?.length || 0) > 0) {
186191
handleChevronClick();
187-
}
192+
}
188193
}}
189194
>
190195
{(blockText || titleRef.display).toUpperCase()}
@@ -410,13 +415,13 @@ const LeftSidebarView = ({ onloadArgs }: { onloadArgs: OnloadArgs }) => {
410415
};
411416

412417
const migrateFavorites = async () => {
413-
const configPageUid = getPageUidByPageTitle(DISCOURSE_CONFIG_PAGE_TITLE);
414-
if (!configPageUid) return;
415-
416418
const config = getFormattedConfigTree().leftSidebar;
417419

418420
if (config.favoritesMigrated.value) return;
419421

422+
const configPageUid = getPageUidByPageTitle(DISCOURSE_CONFIG_PAGE_TITLE);
423+
if (!configPageUid) return;
424+
420425
let leftSidebarUid = config.uid;
421426
if (leftSidebarUid) {
422427
const leftSidebarTree = getBasicTreeByParentUid(leftSidebarUid);
@@ -434,11 +439,13 @@ const migrateFavorites = async () => {
434439
}
435440

436441
const results = window.roamAlphaAPI.q(`
437-
[:find ?title
442+
[:find ?uid
438443
:where [?e :page/sidebar]
439-
[?e :node/title ?title]]
444+
[?e :block/uid ?uid]]
440445
`);
441-
const titles = (results as string[][]).map(([title]) => title);
446+
const favorites = (results as string[][]).map(([uid]) => ({
447+
uid,
448+
}));
442449

443450
if (!leftSidebarUid) {
444451
const tree = getBasicTreeByParentUid(configPageUid);
@@ -482,13 +489,13 @@ const migrateFavorites = async () => {
482489
}
483490

484491
const childrenTree = getBasicTreeByParentUid(childrenUid);
485-
const existingTitles = new Set(childrenTree.map((c) => c.text));
486-
const newTitles = titles.filter((t) => !existingTitles.has(t));
492+
const existingTexts = new Set(childrenTree.map((c) => c.text));
493+
const newFavorites = favorites.filter(({ uid }) => !existingTexts.has(uid));
487494

488-
if (newTitles.length > 0) {
495+
if (newFavorites.length > 0) {
489496
await Promise.all(
490-
newTitles.map((text) =>
491-
createBlock({ parentUid: childrenUid, node: { text } }),
497+
newFavorites.map(({ uid }) =>
498+
createBlock({ parentUid: childrenUid, node: { text: uid } }),
492499
),
493500
);
494501
refreshAndNotify();
@@ -511,6 +518,7 @@ export const mountLeftSidebar = async (
511518
let root = wrapper.querySelector(`#${id}`) as HTMLDivElement;
512519
if (!root) {
513520
await migrateFavorites();
521+
await migrateLeftSidebarSettings();
514522
wrapper.innerHTML = "";
515523
root = document.createElement("div");
516524
root.id = id;

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

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import getAllPageNames from "roamjs-components/queries/getAllPageNames";
66
import createBlock from "roamjs-components/writes/createBlock";
77
import deleteBlock from "roamjs-components/writes/deleteBlock";
88
import type { RoamBasicNode } from "roamjs-components/types";
9-
import { getSubTree } from "roamjs-components/util";
9+
import { extractRef, getSubTree } from "roamjs-components/util";
1010
import getPageUidByPageTitle from "roamjs-components/queries/getPageUidByPageTitle";
1111
import discourseConfigRef from "~/utils/discourseConfigRef";
1212
import { DISCOURSE_CONFIG_PAGE_TITLE } from "~/utils/renderNodeConfigPage";
@@ -15,6 +15,8 @@ import { LeftSidebarGlobalSectionConfig } from "~/utils/getLeftSidebarSettings";
1515
import { render as renderToast } from "roamjs-components/components/Toast";
1616
import refreshConfigTree from "~/utils/refreshConfigTree";
1717
import { refreshAndNotify } from "~/components/LeftSidebarView";
18+
import getPageTitleByPageUid from "roamjs-components/queries/getPageTitleByPageUid";
19+
import getTextByBlockUid from "roamjs-components/queries/getTextByBlockUid";
1820

1921
const PageItem = memo(
2022
({
@@ -32,10 +34,15 @@ const PageItem = memo(
3234
onMove: (index: number, direction: "up" | "down") => void;
3335
onRemove: (page: RoamBasicNode) => void;
3436
}) => {
37+
const pageDisplayTitle =
38+
getPageTitleByPageUid(page.text) ||
39+
getTextByBlockUid(extractRef(page.text)) ||
40+
page.text;
41+
3542
return (
3643
<div className="group flex items-center justify-between rounded bg-gray-50 p-2 hover:bg-gray-100">
37-
<div className="mr-2 flex-grow truncate">{page.text}</div>
38-
<ButtonGroup minimal>
44+
<div className="mr-2 min-w-0 flex-1 truncate">{pageDisplayTitle}</div>
45+
<ButtonGroup minimal className="flex-shrink-0">
3946
<Button
4047
icon="arrow-up"
4148
small
@@ -174,7 +181,8 @@ const LeftSidebarGlobalSectionsContent = ({
174181
async (pageName: string) => {
175182
if (!pageName || !childrenUid) return;
176183

177-
if (pages.some((p) => p.text === pageName)) {
184+
const targetUid = getPageUidByPageTitle(pageName);
185+
if (pages.some((p) => p.text === targetUid)) {
178186
console.warn(`Page "${pageName}" already exists in global section`);
179187
return;
180188
}
@@ -183,11 +191,11 @@ const LeftSidebarGlobalSectionsContent = ({
183191
const newPageUid = await createBlock({
184192
parentUid: childrenUid,
185193
order: "last",
186-
node: { text: pageName },
194+
node: { text: targetUid },
187195
});
188196

189197
const newPage: RoamBasicNode = {
190-
text: pageName,
198+
text: targetUid,
191199
uid: newPageUid,
192200
children: [],
193201
};
@@ -229,10 +237,11 @@ const LeftSidebarGlobalSectionsContent = ({
229237
setIsExpanded((prev) => !prev);
230238
}, []);
231239

232-
const isAddButtonDisabled = useMemo(
233-
() => !newPageInput || pages.some((p) => p.text === newPageInput),
234-
[newPageInput, pages],
235-
);
240+
const isAddButtonDisabled = useMemo(() => {
241+
if (!newPageInput) return true;
242+
const targetUid = getPageUidByPageTitle(newPageInput);
243+
return !targetUid || pages.some((p) => p.text === targetUid);
244+
}, [newPageInput, pages]);
236245

237246
if (isInitializing || !globalSection) {
238247
return (

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

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { render as renderToast } from "roamjs-components/components/Toast";
2727
import refreshConfigTree from "~/utils/refreshConfigTree";
2828
import { refreshAndNotify } from "~/components/LeftSidebarView";
2929
import { memo, Dispatch, SetStateAction } from "react";
30+
import getPageTitleByPageUid from "roamjs-components/queries/getPageTitleByPageUid";
3031

3132
const SectionItem = memo(
3233
({
@@ -155,11 +156,13 @@ const SectionItem = memo(
155156
) => {
156157
if (!childName || !childrenUid) return;
157158

159+
const targetUid = getPageUidByPageTitle(childName) || childName.trim();
160+
158161
try {
159162
const newChild = await createBlock({
160163
parentUid: childrenUid,
161164
order: "last",
162-
node: { text: childName },
165+
node: { text: targetUid },
163166
});
164167

165168
setSections((prev) =>
@@ -170,7 +173,7 @@ const SectionItem = memo(
170173
children: [
171174
...(s.children || []),
172175
{
173-
text: childName,
176+
text: targetUid,
174177
uid: newChild,
175178
children: [],
176179
alias: { value: "" },
@@ -378,24 +381,28 @@ const SectionItem = memo(
378381
{(section.children || []).map((child, index) => {
379382
const childAlias = child.alias?.value;
380383
const isSettingsOpen = childSettingsUid === child.uid;
384+
const childDisplayTitle =
385+
getPageTitleByPageUid(child.text) ||
386+
getTextByBlockUid(extractRef(child.text)) ||
387+
child.text;
381388
return (
382389
<div key={child.uid}>
383390
<div className="group flex items-center justify-between rounded bg-gray-50 p-2 hover:bg-gray-100">
384391
<div
385392
className="mr-2 min-w-0 flex-1 truncate"
386-
title={child.text}
393+
title={childDisplayTitle}
387394
>
388395
{childAlias ? (
389396
<span>
390397
<span className="font-medium">
391398
{childAlias}
392399
</span>
393400
<span className="ml-2 text-xs text-gray-400">
394-
({child.text})
401+
({childDisplayTitle})
395402
</span>
396403
</span>
397404
) : (
398-
child.text
405+
childDisplayTitle
399406
)}
400407
</div>
401408
<ButtonGroup minimal className="flex-shrink-0">
@@ -439,7 +446,7 @@ const SectionItem = memo(
439446
setChildSettingsUid(null);
440447
refreshAndNotify();
441448
}}
442-
title={`Settings for "${child.text}"`}
449+
title={`Settings for "${childDisplayTitle}"`}
443450
style={{ width: "400px" }}
444451
>
445452
<div className="p-4">

apps/roam/src/utils/getLeftSidebarSettings.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ export type LeftSidebarGlobalSectionConfig = {
4343
export type LeftSidebarConfig = {
4444
uid: string;
4545
favoritesMigrated: BooleanSetting;
46+
sidebarMigrated: BooleanSetting;
4647
global: LeftSidebarGlobalSectionConfig;
4748
personal: {
4849
uid: string;
@@ -187,9 +188,14 @@ export const getLeftSidebarSettings = (
187188
tree: leftSidebarChildren,
188189
text: "Favorites Migrated",
189190
});
191+
const sidebarMigrated = getUidAndBooleanSetting({
192+
tree: leftSidebarChildren,
193+
text: "Sidebar Migrated",
194+
});
190195
return {
191196
uid: leftSidebarUid,
192197
favoritesMigrated,
198+
sidebarMigrated,
193199
global,
194200
personal,
195201
};
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import getPageUidByPageTitle from "roamjs-components/queries/getPageUidByPageTitle";
2+
import getPageTitleByPageUid from "roamjs-components/queries/getPageTitleByPageUid";
3+
import updateBlock from "roamjs-components/writes/updateBlock";
4+
import createBlock from "roamjs-components/writes/createBlock";
5+
import { getFormattedConfigTree } from "./discourseConfigRef";
6+
import { DISCOURSE_CONFIG_PAGE_TITLE } from "./renderNodeConfigPage";
7+
import refreshConfigTree from "./refreshConfigTree";
8+
9+
const migrateSectionChildren = async (
10+
children: { uid: string; text: string }[],
11+
) => {
12+
const promises = children.map(async (child) => {
13+
const currentText = child.text;
14+
15+
const titleFromUid = getPageTitleByPageUid(currentText);
16+
if (titleFromUid) {
17+
return;
18+
}
19+
20+
const uidFromTitle = getPageUidByPageTitle(currentText);
21+
if (uidFromTitle) {
22+
try {
23+
await updateBlock({
24+
uid: child.uid,
25+
text: uidFromTitle,
26+
});
27+
console.log(
28+
`Migrated sidebar item "${currentText}" to UID "${uidFromTitle}"`,
29+
);
30+
} catch (e) {
31+
console.error(`Failed to migrate sidebar item "${currentText}"`, e);
32+
}
33+
}
34+
});
35+
36+
await Promise.all(promises);
37+
};
38+
39+
export const migrateLeftSidebarSettings = async () => {
40+
const leftSidebarSettings = getFormattedConfigTree().leftSidebar;
41+
42+
if (!leftSidebarSettings.uid) return;
43+
44+
if (leftSidebarSettings.sidebarMigrated.value) return;
45+
46+
const configPageUid = getPageUidByPageTitle(DISCOURSE_CONFIG_PAGE_TITLE);
47+
if (!configPageUid) return;
48+
49+
const globalChildren = leftSidebarSettings.global.children;
50+
if (globalChildren.length > 0) {
51+
await migrateSectionChildren(globalChildren);
52+
}
53+
54+
const personalSections = leftSidebarSettings.personal.sections;
55+
for (const section of personalSections) {
56+
const children = section.children || [];
57+
if (children.length > 0) {
58+
await migrateSectionChildren(children);
59+
}
60+
}
61+
62+
if (leftSidebarSettings.uid) {
63+
await createBlock({
64+
parentUid: leftSidebarSettings.uid,
65+
node: { text: "Sidebar Migrated" },
66+
});
67+
}
68+
69+
refreshConfigTree();
70+
};

0 commit comments

Comments
 (0)