diff --git a/packages/frontend/core/src/components/root-app-sidebar/index.tsx b/packages/frontend/core/src/components/root-app-sidebar/index.tsx
index fc1942d43..cd2c39141 100644
--- a/packages/frontend/core/src/components/root-app-sidebar/index.tsx
+++ b/packages/frontend/core/src/components/root-app-sidebar/index.tsx
@@ -229,7 +229,7 @@ export const RootAppSidebar = memo((): ReactElement => {
diff --git a/packages/frontend/core/src/desktop/components/navigation-panel/layouts/collapsible-section.tsx b/packages/frontend/core/src/desktop/components/navigation-panel/layouts/collapsible-section.tsx
index 33d0ff58f..f73b98053 100644
--- a/packages/frontend/core/src/desktop/components/navigation-panel/layouts/collapsible-section.tsx
+++ b/packages/frontend/core/src/desktop/components/navigation-panel/layouts/collapsible-section.tsx
@@ -1,8 +1,5 @@
import { CategoryDivider } from '@affine/core/modules/app-sidebar/views';
-import {
- type CollapsibleSectionName,
- NavigationPanelService,
-} from '@affine/core/modules/navigation-panel';
+import { NavigationPanelService } from '@affine/core/modules/navigation-panel';
import * as Collapsible from '@radix-ui/react-collapsible';
import { useLiveData, useService } from '@toeverything/infra';
import clsx from 'clsx';
@@ -17,7 +14,7 @@ import {
import { content, header, root } from './collapsible-section.css';
interface CollapsibleSectionProps extends PropsWithChildren {
- name: CollapsibleSectionName;
+ path: string[];
title: string;
actions?: ReactNode;
@@ -33,7 +30,7 @@ interface CollapsibleSectionProps extends PropsWithChildren {
}
export const CollapsibleSection = ({
- name,
+ path,
title,
actions,
children,
@@ -48,15 +45,15 @@ export const CollapsibleSection = ({
contentClassName,
contentStyle,
}: CollapsibleSectionProps) => {
- const section = useService(NavigationPanelService).sections[name];
+ const navigationPanelService = useService(NavigationPanelService);
- const collapsed = useLiveData(section.collapsed$);
+ const collapsed = useLiveData(navigationPanelService.collapsed$(path));
const setCollapsed = useCallback(
(v: boolean) => {
- section.setCollapsed(v);
+ navigationPanelService.setCollapsed(path, v);
},
- [section]
+ [navigationPanelService, path]
);
return (
diff --git a/packages/frontend/core/src/desktop/components/navigation-panel/nodes/collection/index.tsx b/packages/frontend/core/src/desktop/components/navigation-panel/nodes/collection/index.tsx
index 94bef235f..a89cc62f2 100644
--- a/packages/frontend/core/src/desktop/components/navigation-panel/nodes/collection/index.tsx
+++ b/packages/frontend/core/src/desktop/components/navigation-panel/nodes/collection/index.tsx
@@ -11,6 +11,7 @@ import {
} from '@affine/core/modules/collection';
import { WorkspaceDialogService } from '@affine/core/modules/dialogs';
import { GlobalContextService } from '@affine/core/modules/global-context';
+import { NavigationPanelService } from '@affine/core/modules/navigation-panel';
import type { AffineDNDData } from '@affine/core/types/dnd';
import { useI18n } from '@affine/i18n';
import { track } from '@affine/track';
@@ -47,6 +48,7 @@ export const NavigationPanelCollectionNode = ({
operations: additionalOperations,
canDrop,
dropEffect,
+ parentPath,
}: {
collectionId: string;
} & GenericNavigationPanelNode) => {
@@ -55,10 +57,21 @@ export const NavigationPanelCollectionNode = ({
GlobalContextService,
WorkspaceDialogService,
});
+ const navigationPanelService = useService(NavigationPanelService);
const active =
useLiveData(globalContextService.globalContext.collectionId.$) ===
collectionId;
- const [collapsed, setCollapsed] = useState(true);
+ const path = useMemo(
+ () => [...(parentPath ?? []), `collection-${collectionId}`],
+ [parentPath, collectionId]
+ );
+ const collapsed = useLiveData(navigationPanelService.collapsed$(path));
+ const setCollapsed = useCallback(
+ (value: boolean) => {
+ navigationPanelService.setCollapsed(path, value);
+ },
+ [navigationPanelService, path]
+ );
const collectionService = useService(CollectionService);
const collection = useLiveData(collectionService.collection$(collectionId));
@@ -160,7 +173,7 @@ export const NavigationPanelCollectionNode = ({
const handleOpenCollapsed = useCallback(() => {
setCollapsed(false);
- }, []);
+ }, [setCollapsed]);
const handleEditCollection = useCallback(() => {
if (!collection) {
@@ -217,15 +230,20 @@ export const NavigationPanelCollectionNode = ({
dropEffect={handleDropEffectOnCollection}
data-testid={`navigation-panel-collection-${collectionId}`}
>
-
+
);
};
const NavigationPanelCollectionNodeChildren = ({
collection,
+ path,
}: {
collection: Collection;
+ path: string[];
}) => {
const t = useI18n();
const { collectionService } = useServices({
@@ -264,6 +282,7 @@ const NavigationPanelCollectionNodeChildren = ({
at: 'navigation-panel:collection:filtered-docs',
collectionId: collection.id,
}}
+ parentPath={path}
operations={
allowList.has(docId)
? [
diff --git a/packages/frontend/core/src/desktop/components/navigation-panel/nodes/doc/index.tsx b/packages/frontend/core/src/desktop/components/navigation-panel/nodes/doc/index.tsx
index 37bc00e9b..3612efc8d 100644
--- a/packages/frontend/core/src/desktop/components/navigation-panel/nodes/doc/index.tsx
+++ b/packages/frontend/core/src/desktop/components/navigation-panel/nodes/doc/index.tsx
@@ -14,6 +14,7 @@ import { DocDisplayMetaService } from '@affine/core/modules/doc-display-meta';
import { DocsSearchService } from '@affine/core/modules/docs-search';
import { FeatureFlagService } from '@affine/core/modules/feature-flag';
import { GlobalContextService } from '@affine/core/modules/global-context';
+import { NavigationPanelService } from '@affine/core/modules/navigation-panel';
import { GuardService } from '@affine/core/modules/permissions';
import type { AffineDNDData } from '@affine/core/types/dnd';
import { useI18n } from '@affine/i18n';
@@ -46,6 +47,7 @@ export const NavigationPanelDocNode = ({
canDrop,
operations: additionalOperations,
dropEffect,
+ parentPath,
}: {
docId: string;
isLinked?: boolean;
@@ -67,11 +69,22 @@ export const NavigationPanelDocNode = ({
FeatureFlagService,
GuardService,
});
+ const navigationPanelService = useService(NavigationPanelService);
const { appSettings } = useAppSettingHelper();
const active =
useLiveData(globalContextService.globalContext.docId.$) === docId;
- const [collapsed, setCollapsed] = useState(true);
+ const path = useMemo(
+ () => [...(parentPath ?? []), `doc-${docId}`],
+ [parentPath, docId]
+ );
+ const collapsed = useLiveData(navigationPanelService.collapsed$(path));
+ const setCollapsed = useCallback(
+ (value: boolean) => {
+ navigationPanelService.setCollapsed(path, value);
+ },
+ [navigationPanelService, path]
+ );
const isCollapsed = appSettings.showLinkedDocInSidebar ? collapsed : true;
const docRecord = useLiveData(docsService.list.doc$(docId));
@@ -227,7 +240,7 @@ export const NavigationPanelDocNode = ({
openInfoModal: () => workspaceDialogService.open('doc-info', { docId }),
openNodeCollapsed: () => setCollapsed(false),
}),
- [docId, workspaceDialogService]
+ [docId, setCollapsed, workspaceDialogService]
)
);
@@ -302,6 +315,7 @@ export const NavigationPanelDocNode = ({
at: 'navigation-panel:doc:linked-docs',
docId,
}}
+ parentPath={path}
isLinked
/>
))
diff --git a/packages/frontend/core/src/desktop/components/navigation-panel/nodes/folder/index.tsx b/packages/frontend/core/src/desktop/components/navigation-panel/nodes/folder/index.tsx
index 28190448e..9387652e9 100644
--- a/packages/frontend/core/src/desktop/components/navigation-panel/nodes/folder/index.tsx
+++ b/packages/frontend/core/src/desktop/components/navigation-panel/nodes/folder/index.tsx
@@ -13,6 +13,7 @@ import { usePageHelper } from '@affine/core/blocksuite/block-suite-page-list/uti
import { WorkspaceDialogService } from '@affine/core/modules/dialogs';
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite';
import { FeatureFlagService } from '@affine/core/modules/feature-flag';
+import { NavigationPanelService } from '@affine/core/modules/navigation-panel';
import {
type FolderNode,
OrganizeService,
@@ -31,7 +32,7 @@ import {
RemoveFolderIcon,
TagsIcon,
} from '@blocksuite/icons/rc';
-import { useLiveData, useServices } from '@toeverything/infra';
+import { useLiveData, useService, useServices } from '@toeverything/infra';
import { difference } from 'lodash-es';
import { useCallback, useMemo, useState } from 'react';
@@ -57,6 +58,7 @@ export const NavigationPanelFolderNode = ({
dropEffect,
canDrop,
reorderable,
+ parentPath,
}: {
defaultRenaming?: boolean;
nodeId: string;
@@ -104,6 +106,7 @@ export const NavigationPanelFolderNode = ({
dropEffect={dropEffect}
reorderable={reorderable}
canDrop={canDrop}
+ parentPath={parentPath}
/>
);
} else if (type === 'doc') {
@@ -117,6 +120,7 @@ export const NavigationPanelFolderNode = ({
canDrop={canDrop}
dropEffect={dropEffect}
operations={additionalOperations}
+ parentPath={parentPath}
/>
)
);
@@ -131,6 +135,7 @@ export const NavigationPanelFolderNode = ({
reorderable={reorderable}
dropEffect={dropEffect}
operations={additionalOperations}
+ parentPath={parentPath}
/>
)
);
@@ -145,6 +150,7 @@ export const NavigationPanelFolderNode = ({
reorderable
dropEffect={dropEffect}
operations={additionalOperations}
+ parentPath={parentPath}
/>
)
);
@@ -177,6 +183,7 @@ const NavigationPanelFolderNodeFolder = ({
canDrop,
dropEffect,
reorderable,
+ parentPath,
}: {
defaultRenaming?: boolean;
node: FolderNode;
@@ -189,11 +196,22 @@ const NavigationPanelFolderNodeFolder = ({
FeatureFlagService,
WorkspaceDialogService,
});
+ const navigationPanelService = useService(NavigationPanelService);
const name = useLiveData(node.name$);
const enableEmojiIcon = useLiveData(
featureFlagService.flags.enable_emoji_folder_icon.$
);
- const [collapsed, setCollapsed] = useState(true);
+ const path = useMemo(
+ () => [...(parentPath ?? []), `folder-${node.id}`],
+ [parentPath, node.id]
+ );
+ const collapsed = useLiveData(navigationPanelService.collapsed$(path));
+ const setCollapsed = useCallback(
+ (value: boolean) => {
+ navigationPanelService.setCollapsed(path, value);
+ },
+ [navigationPanelService, path]
+ );
const [newFolderId, setNewFolderId] = useState(null);
const { createPage } = usePageHelper(
@@ -575,7 +593,7 @@ const NavigationPanelFolderNodeFolder = ({
target: 'doc',
});
setCollapsed(false);
- }, [createPage, node]);
+ }, [createPage, node, setCollapsed]);
const handleCreateSubfolder = useCallback(() => {
const newFolderId = node.createFolder(
@@ -585,7 +603,7 @@ const NavigationPanelFolderNodeFolder = ({
track.$.navigationPanel.organize.createOrganizeItem({ type: 'folder' });
setCollapsed(false);
setNewFolderId(newFolderId);
- }, [node, t]);
+ }, [node, setCollapsed, t]);
const handleAddToFolder = useCallback(
(type: 'doc' | 'collection' | 'tag') => {
@@ -628,7 +646,7 @@ const NavigationPanelFolderNodeFolder = ({
target: type,
});
},
- [children, node, workspaceDialogService]
+ [children, node, setCollapsed, workspaceDialogService]
);
const folderOperations = useMemo(() => {
@@ -761,14 +779,17 @@ const NavigationPanelFolderNodeFolder = ({
[t]
);
- const handleCollapsedChange = useCallback((collapsed: boolean) => {
- if (collapsed) {
- setNewFolderId(null); // reset new folder id to clear the renaming state
- setCollapsed(true);
- } else {
- setCollapsed(false);
- }
- }, []);
+ const handleCollapsedChange = useCallback(
+ (collapsed: boolean) => {
+ if (collapsed) {
+ setNewFolderId(null); // reset new folder id to clear the renaming state
+ setCollapsed(true);
+ } else {
+ setCollapsed(false);
+ }
+ },
+ [setCollapsed]
+ );
return (
))}
diff --git a/packages/frontend/core/src/desktop/components/navigation-panel/nodes/tag/index.tsx b/packages/frontend/core/src/desktop/components/navigation-panel/nodes/tag/index.tsx
index 6ad920674..264ddb1b0 100644
--- a/packages/frontend/core/src/desktop/components/navigation-panel/nodes/tag/index.tsx
+++ b/packages/frontend/core/src/desktop/components/navigation-panel/nodes/tag/index.tsx
@@ -4,14 +4,15 @@ import {
toast,
} from '@affine/component';
import { GlobalContextService } from '@affine/core/modules/global-context';
+import { NavigationPanelService } from '@affine/core/modules/navigation-panel';
import type { Tag } from '@affine/core/modules/tag';
import { TagService } from '@affine/core/modules/tag';
import type { AffineDNDData } from '@affine/core/types/dnd';
import { useI18n } from '@affine/i18n';
import { track } from '@affine/track';
-import { useLiveData, useServices } from '@toeverything/infra';
+import { useLiveData, useService, useServices } from '@toeverything/infra';
import clsx from 'clsx';
-import { useCallback, useMemo, useState } from 'react';
+import { useCallback, useMemo } from 'react';
import {
NavigationPanelTreeNode,
@@ -31,6 +32,7 @@ export const NavigationPanelTagNode = ({
operations: additionalOperations,
dropEffect,
canDrop,
+ parentPath,
}: {
tagId: string;
} & GenericNavigationPanelNode) => {
@@ -39,9 +41,20 @@ export const NavigationPanelTagNode = ({
TagService,
GlobalContextService,
});
+ const navigationPanelService = useService(NavigationPanelService);
const active =
useLiveData(globalContextService.globalContext.tagId.$) === tagId;
- const [collapsed, setCollapsed] = useState(true);
+ const path = useMemo(
+ () => [...(parentPath ?? []), `tag-${tagId}`],
+ [parentPath, tagId]
+ );
+ const collapsed = useLiveData(navigationPanelService.collapsed$(path));
+ const setCollapsed = useCallback(
+ (value: boolean) => {
+ navigationPanelService.setCollapsed(path, value);
+ },
+ [navigationPanelService, path]
+ );
const tagRecord = useLiveData(tagService.tagList.tagByTagId$(tagId));
const tagColor = useLiveData(tagRecord?.color$);
@@ -154,7 +167,7 @@ export const NavigationPanelTagNode = ({
() => ({
openNodeCollapsed: () => setCollapsed(false),
}),
- []
+ [setCollapsed]
)
);
@@ -188,7 +201,7 @@ export const NavigationPanelTagNode = ({
dropEffect={handleDropEffectOnTag}
data-testid={`navigation-panel-tag-${tagId}`}
>
-
+
);
};
@@ -198,7 +211,13 @@ export const NavigationPanelTagNode = ({
* so we split the tag node children into a separate component,
* so it won't be rendered when the tag node is collapsed.
*/
-export const NavigationPanelTagNodeDocs = ({ tag }: { tag: Tag }) => {
+export const NavigationPanelTagNodeDocs = ({
+ tag,
+ path,
+}: {
+ tag: Tag;
+ path: string[];
+}) => {
const tagDocIds = useLiveData(tag.pageIds$);
return tagDocIds.map(docId => (
@@ -209,6 +228,7 @@ export const NavigationPanelTagNodeDocs = ({ tag }: { tag: Tag }) => {
location={{
at: 'navigation-panel:tags:docs',
}}
+ parentPath={path}
/>
));
};
diff --git a/packages/frontend/core/src/desktop/components/navigation-panel/nodes/types.ts b/packages/frontend/core/src/desktop/components/navigation-panel/nodes/types.ts
index 4ec9b5090..05de76479 100644
--- a/packages/frontend/core/src/desktop/components/navigation-panel/nodes/types.ts
+++ b/packages/frontend/core/src/desktop/components/navigation-panel/nodes/types.ts
@@ -46,4 +46,9 @@ export interface GenericNavigationPanelNode {
* The drop effect to be used when an element is dropped over the node.
*/
dropEffect?: NavigationPanelTreeNodeDropEffect;
+ /**
+ * The path segments to the parent node in the navigation tree.
+ * Used to persist the node's collapsed/expanded state in cache storage.
+ */
+ parentPath: string[];
}
diff --git a/packages/frontend/core/src/desktop/components/navigation-panel/sections/collections/index.tsx b/packages/frontend/core/src/desktop/components/navigation-panel/sections/collections/index.tsx
index e10c1ac8c..a1b809d97 100644
--- a/packages/frontend/core/src/desktop/components/navigation-panel/sections/collections/index.tsx
+++ b/packages/frontend/core/src/desktop/components/navigation-panel/sections/collections/index.tsx
@@ -6,7 +6,7 @@ import { useI18n } from '@affine/i18n';
import { track } from '@affine/track';
import { AddCollectionIcon } from '@blocksuite/icons/rc';
import { useLiveData, useServices } from '@toeverything/infra';
-import { useCallback } from 'react';
+import { useCallback, useMemo } from 'react';
import { CollapsibleSection } from '../../layouts/collapsible-section';
import { NavigationPanelCollectionNode } from '../../nodes/collection';
@@ -22,10 +22,9 @@ export const NavigationPanelCollections = () => {
WorkbenchService,
NavigationPanelService,
});
- const navigationPanelSection = navigationPanelService.sections.collections;
const collections = useLiveData(collectionService.collections$);
const { openPromptModal } = usePromptModal();
-
+ const path = useMemo(() => ['collections'], []);
const handleCreateCollection = useCallback(() => {
openPromptModal({
title: t['com.affine.editCollection.saveCollection'](),
@@ -49,20 +48,21 @@ export const NavigationPanelCollections = () => {
type: 'collection',
});
workbenchService.workbench.openCollection(id);
- navigationPanelSection.setCollapsed(false);
+ navigationPanelService.setCollapsed(path, false);
},
});
}, [
collectionService,
- navigationPanelSection,
+ navigationPanelService,
openPromptModal,
+ path,
t,
workbenchService.workbench,
]);
return (
{
location={{
at: 'navigation-panel:collection:list',
}}
+ parentPath={path}
/>
))}
diff --git a/packages/frontend/core/src/desktop/components/navigation-panel/sections/favorites/index.tsx b/packages/frontend/core/src/desktop/components/navigation-panel/sections/favorites/index.tsx
index cb6bfcd6a..5272e4a5f 100644
--- a/packages/frontend/core/src/desktop/components/navigation-panel/sections/favorites/index.tsx
+++ b/packages/frontend/core/src/desktop/components/navigation-panel/sections/favorites/index.tsx
@@ -17,7 +17,7 @@ import { useI18n } from '@affine/i18n';
import { track } from '@affine/track';
import { PlusIcon } from '@blocksuite/icons/rc';
import { useLiveData, useServices } from '@toeverything/infra';
-import { type MouseEventHandler, useCallback } from 'react';
+import { type MouseEventHandler, useCallback, useMemo } from 'react';
import { CollapsibleSection } from '../../layouts/collapsible-section';
import { NavigationPanelCollectionNode } from '../../nodes/collection';
@@ -41,7 +41,7 @@ export const NavigationPanelFavorites = () => {
NavigationPanelService,
});
- const navigationPanelSection = navigationPanelService.sections.favorites;
+ const path = useMemo(() => ['favorites'], []);
const favorites = useLiveData(favoriteService.favoriteList.sortedList$);
@@ -71,10 +71,10 @@ export const NavigationPanelFavorites = () => {
track.$.navigationPanel.favorites.drop({
type: data.source.data.entity.type,
});
- navigationPanelSection.setCollapsed(false);
+ navigationPanelService.setCollapsed(path, false);
}
},
- [navigationPanelSection, favoriteService.favoriteList]
+ [navigationPanelService, favoriteService.favoriteList, path]
);
const handleCreateNewFavoriteDoc: MouseEventHandler = useCallback(
@@ -85,9 +85,9 @@ export const NavigationPanelFavorites = () => {
newDoc.id,
favoriteService.favoriteList.indexAt('before')
);
- navigationPanelSection.setCollapsed(false);
+ navigationPanelService.setCollapsed(path, false);
},
- [createPage, navigationPanelSection, favoriteService.favoriteList]
+ [createPage, navigationPanelService, favoriteService.favoriteList, path]
);
const handleOnChildrenDrop = useCallback(
@@ -162,7 +162,7 @@ export const NavigationPanelFavorites = () => {
return (
{
key={favorite.id}
favorite={favorite}
onDrop={handleOnChildrenDrop}
+ parentPath={path}
/>
))}
@@ -215,11 +216,13 @@ const childLocation = {
const NavigationPanelFavoriteNode = ({
favorite,
onDrop,
+ parentPath,
}: {
favorite: {
id: string;
type: FavoriteSupportTypeUnion;
};
+ parentPath: string[];
onDrop: (
favorite: {
id: string;
@@ -242,6 +245,7 @@ const NavigationPanelFavoriteNode = ({
onDrop={handleOnChildrenDrop}
dropEffect={favoriteChildrenDropEffect}
canDrop={favoriteChildrenCanDrop}
+ parentPath={parentPath}
/>
) : favorite.type === 'tag' ? (
) : favorite.type === 'folder' ? (
) : (
);
};
diff --git a/packages/frontend/core/src/desktop/components/navigation-panel/sections/migration-favorites/index.tsx b/packages/frontend/core/src/desktop/components/navigation-panel/sections/migration-favorites/index.tsx
index 8cc1b2779..118391e4a 100644
--- a/packages/frontend/core/src/desktop/components/navigation-panel/sections/migration-favorites/index.tsx
+++ b/packages/frontend/core/src/desktop/components/navigation-panel/sections/migration-favorites/index.tsx
@@ -5,7 +5,7 @@ import { Trans, useI18n } from '@affine/i18n';
import { track } from '@affine/track';
import { BroomIcon, HelpIcon } from '@blocksuite/icons/rc';
import { useLiveData, useServices } from '@toeverything/infra';
-import { useCallback } from 'react';
+import { useCallback, useMemo } from 'react';
import { CollapsibleSection } from '../../layouts/collapsible-section';
import { NavigationPanelCollectionNode } from '../../nodes/collection';
@@ -25,6 +25,7 @@ export const NavigationPanelMigrationFavorites = () => {
const trashDocs = useLiveData(docsService.list.trashDocs$);
const migrated = useLiveData(migrationFavoriteItemsAdapter.migrated$);
const { openConfirmModal } = useConfirmModal();
+ const path = useMemo(() => ['migration-favorites'], []);
const favorites = useLiveData(
migrationFavoriteItemsAdapter.favorites$.map(favs => {
@@ -99,7 +100,7 @@ export const NavigationPanelMigrationFavorites = () => {
return (
{
))}
@@ -138,11 +140,13 @@ const childLocation = {
};
const NavigationPanelMigrationFavoriteNode = ({
favorite,
+ parentPath,
}: {
favorite: {
id: string;
type: 'collection' | 'doc';
};
+ parentPath: string[];
}) => {
return favorite.type === 'doc' ? (
) : (
);
};
diff --git a/packages/frontend/core/src/desktop/components/navigation-panel/sections/organize/index.tsx b/packages/frontend/core/src/desktop/components/navigation-panel/sections/organize/index.tsx
index f9c8dea06..24e3bd114 100644
--- a/packages/frontend/core/src/desktop/components/navigation-panel/sections/organize/index.tsx
+++ b/packages/frontend/core/src/desktop/components/navigation-panel/sections/organize/index.tsx
@@ -27,10 +27,9 @@ export const NavigationPanelOrganize = () => {
OrganizeService,
NavigationPanelService,
});
- const navigationPanelSection = navigationPanelService.sections.organize;
- const collapsed = useLiveData(navigationPanelSection.collapsed$);
+ const path = useMemo(() => ['organize'], []);
+ const collapsed = useLiveData(navigationPanelService.collapsed$(path));
const [newFolderId, setNewFolderId] = useState(null);
-
const t = useI18n();
const folderTree = organizeService.folderTree;
@@ -46,9 +45,9 @@ export const NavigationPanelOrganize = () => {
);
track.$.navigationPanel.organize.createOrganizeItem({ type: 'folder' });
setNewFolderId(newFolderId);
- navigationPanelSection.setCollapsed(false);
+ navigationPanelService.setCollapsed(path, false);
return newFolderId;
- }, [navigationPanelSection, rootFolder]);
+ }, [navigationPanelService, path, rootFolder]);
const handleOnChildrenDrop = useCallback(
(data: DropTargetDropEvent, node?: FolderNode) => {
@@ -105,7 +104,7 @@ export const NavigationPanelOrganize = () => {
return (
{
at: 'navigation-panel:organize:folder-node',
nodeId: child.id as string,
}}
+ parentPath={path}
/>
))}
diff --git a/packages/frontend/core/src/desktop/components/navigation-panel/sections/tags/index.tsx b/packages/frontend/core/src/desktop/components/navigation-panel/sections/tags/index.tsx
index 232506dd4..3ee2549ba 100644
--- a/packages/frontend/core/src/desktop/components/navigation-panel/sections/tags/index.tsx
+++ b/packages/frontend/core/src/desktop/components/navigation-panel/sections/tags/index.tsx
@@ -5,7 +5,7 @@ import { useI18n } from '@affine/i18n';
import { track } from '@affine/track';
import { AddTagIcon } from '@blocksuite/icons/rc';
import { useLiveData, useServices } from '@toeverything/infra';
-import { useCallback, useEffect, useState } from 'react';
+import { useCallback, useEffect, useMemo, useState } from 'react';
import { CollapsibleSection } from '../../layouts/collapsible-section';
import { NavigationPanelTagNode } from '../../nodes/tag';
@@ -19,8 +19,8 @@ export const NavigationPanelTags = () => {
TagService,
NavigationPanelService,
});
- const navigationPanelSection = navigationPanelService.sections.tags;
- const collapsed = useLiveData(navigationPanelSection.collapsed$);
+ const path = useMemo(() => ['tags'], []);
+ const collapsed = useLiveData(navigationPanelService.collapsed$(path));
const [creating, setCreating] = useState(false);
const tags = useLiveData(tagService.tagList.tags$);
@@ -30,9 +30,9 @@ export const NavigationPanelTags = () => {
(name: string) => {
tagService.tagList.createTag(name, tagService.randomTagColor());
track.$.navigationPanel.organize.createOrganizeItem({ type: 'tag' });
- navigationPanelSection.setCollapsed(false);
+ navigationPanelService.setCollapsed(path, false);
},
- [navigationPanelSection, tagService]
+ [navigationPanelService, path, tagService]
);
useEffect(() => {
@@ -45,7 +45,7 @@ export const NavigationPanelTags = () => {
return (
{
location={{
at: 'navigation-panel:tags:list',
}}
+ parentPath={path}
/>
))}
diff --git a/packages/frontend/core/src/mobile/components/navigation/layouts/collapsible-section.tsx b/packages/frontend/core/src/mobile/components/navigation/layouts/collapsible-section.tsx
index 2fd3fc9e6..ad0efed80 100644
--- a/packages/frontend/core/src/mobile/components/navigation/layouts/collapsible-section.tsx
+++ b/packages/frontend/core/src/mobile/components/navigation/layouts/collapsible-section.tsx
@@ -1,7 +1,4 @@
-import {
- type CollapsibleSectionName,
- NavigationPanelService,
-} from '@affine/core/modules/navigation-panel';
+import { NavigationPanelService } from '@affine/core/modules/navigation-panel';
import { ToggleRightIcon } from '@blocksuite/icons/rc';
import * as Collapsible from '@radix-ui/react-collapsible';
import { useLiveData, useService } from '@toeverything/infra';
@@ -22,7 +19,7 @@ import {
} from './collapsible-section.css';
interface CollapsibleSectionProps extends HTMLAttributes {
- name: CollapsibleSectionName;
+ path: string[];
title: string;
actions?: ReactNode;
testId?: string;
@@ -76,7 +73,7 @@ const CollapsibleSectionTrigger = forwardRef<
});
export const CollapsibleSection = ({
- name,
+ path,
title,
actions,
testId,
@@ -86,12 +83,12 @@ export const CollapsibleSection = ({
children,
...attrs
}: CollapsibleSectionProps) => {
- const section = useService(NavigationPanelService).sections[name];
- const collapsed = useLiveData(section.collapsed$);
+ const navigationPanelService = useService(NavigationPanelService);
+ const collapsed = useLiveData(navigationPanelService.collapsed$(path));
const setCollapsed = useCallback(
- (v: boolean) => section.setCollapsed(v),
- [section]
+ (v: boolean) => navigationPanelService.setCollapsed(path, v),
+ [navigationPanelService, path]
);
return (
diff --git a/packages/frontend/core/src/mobile/components/navigation/nodes/collection/index.tsx b/packages/frontend/core/src/mobile/components/navigation/nodes/collection/index.tsx
index bf2ad5904..01c8cf8fc 100644
--- a/packages/frontend/core/src/mobile/components/navigation/nodes/collection/index.tsx
+++ b/packages/frontend/core/src/mobile/components/navigation/nodes/collection/index.tsx
@@ -6,11 +6,12 @@ import {
} from '@affine/core/modules/collection';
import { WorkspaceDialogService } from '@affine/core/modules/dialogs';
import { GlobalContextService } from '@affine/core/modules/global-context';
+import { NavigationPanelService } from '@affine/core/modules/navigation-panel';
import { ShareDocsListService } from '@affine/core/modules/share-doc';
import { useI18n } from '@affine/i18n';
import track from '@affine/track';
import { FilterMinusIcon, ViewLayersIcon } from '@blocksuite/icons/rc';
-import { useLiveData, useServices } from '@toeverything/infra';
+import { useLiveData, useService, useServices } from '@toeverything/infra';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { AddItemPlaceholder } from '../../layouts/add-item-placeholder';
@@ -26,9 +27,11 @@ const CollectionIcon = () => ;
export const NavigationPanelCollectionNode = ({
collectionId,
operations: additionalOperations,
+ parentPath,
}: {
collectionId: string;
operations?: NodeOperation[];
+ parentPath: string[];
}) => {
const t = useI18n();
const { globalContextService, collectionService, workspaceDialogService } =
@@ -37,17 +40,28 @@ export const NavigationPanelCollectionNode = ({
CollectionService,
WorkspaceDialogService,
});
+ const navigationPanelService = useService(NavigationPanelService);
const active =
useLiveData(globalContextService.globalContext.collectionId.$) ===
collectionId;
- const [collapsed, setCollapsed] = useState(true);
+ const path = useMemo(
+ () => [...parentPath, `collection-${collectionId}`],
+ [parentPath, collectionId]
+ );
+ const collapsed = useLiveData(navigationPanelService.collapsed$(path));
+ const setCollapsed = useCallback(
+ (value: boolean) => {
+ navigationPanelService.setCollapsed(path, value);
+ },
+ [navigationPanelService, path]
+ );
const collection = useLiveData(collectionService.collection$(collectionId));
const name = useLiveData(collection?.name$);
const handleOpenCollapsed = useCallback(() => {
setCollapsed(false);
- }, []);
+ }, [setCollapsed]);
const handleEditCollection = useCallback(() => {
if (!collection) {
@@ -95,6 +109,7 @@ export const NavigationPanelCollectionNode = ({
);
@@ -103,9 +118,11 @@ export const NavigationPanelCollectionNode = ({
const NavigationPanelCollectionNodeChildren = ({
collection,
onAddDoc,
+ path,
}: {
collection: Collection;
onAddDoc?: () => void;
+ path: string[];
}) => {
const t = useI18n();
const { shareDocsListService, collectionService } = useServices({
@@ -147,6 +164,7 @@ const NavigationPanelCollectionNodeChildren = ({
{
const t = useI18n();
const {
@@ -48,9 +51,20 @@ export const NavigationPanelDocNode = ({
DocDisplayMetaService,
FeatureFlagService,
});
+ const navigationPanelService = useService(NavigationPanelService);
const active =
useLiveData(globalContextService.globalContext.docId.$) === docId;
- const [collapsed, setCollapsed] = useState(true);
+ const path = useMemo(
+ () => [...parentPath, `doc-${docId}`],
+ [parentPath, docId]
+ );
+ const collapsed = useLiveData(navigationPanelService.collapsed$(path));
+ const setCollapsed = useCallback(
+ (value: boolean) => {
+ navigationPanelService.setCollapsed(path, value);
+ },
+ [navigationPanelService, path]
+ );
const docRecord = useLiveData(docsService.list.doc$(docId));
const DocIcon = useLiveData(
@@ -103,7 +117,7 @@ export const NavigationPanelDocNode = ({
openInfoModal: () => workspaceDialogService.open('doc-info', { docId }),
openNodeCollapsed: () => setCollapsed(false),
}),
- [docId, workspaceDialogService]
+ [docId, setCollapsed, workspaceDialogService]
);
const operations = useNavigationPanelDocNodeOperationsMenu(docId, option);
const { handleAddLinkedPage } = useNavigationPanelDocNodeOperations(
@@ -150,6 +164,7 @@ export const NavigationPanelDocNode = ({
key={`${child.docId}-${index}`}
docId={child.docId}
isLinked
+ parentPath={path}
/>
))
: null
diff --git a/packages/frontend/core/src/mobile/components/navigation/nodes/folder/index.tsx b/packages/frontend/core/src/mobile/components/navigation/nodes/folder/index.tsx
index 778fc0b65..7d70af5aa 100644
--- a/packages/frontend/core/src/mobile/components/navigation/nodes/folder/index.tsx
+++ b/packages/frontend/core/src/mobile/components/navigation/nodes/folder/index.tsx
@@ -14,6 +14,7 @@ import type {
import { WorkspaceDialogService } from '@affine/core/modules/dialogs';
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite';
import { FeatureFlagService } from '@affine/core/modules/feature-flag';
+import { NavigationPanelService } from '@affine/core/modules/navigation-panel';
import {
type FolderNode,
OrganizeService,
@@ -31,9 +32,9 @@ import {
RemoveFolderIcon,
TagsIcon,
} from '@blocksuite/icons/rc';
-import { useLiveData, useServices } from '@toeverything/infra';
+import { useLiveData, useService, useServices } from '@toeverything/infra';
import { difference } from 'lodash-es';
-import { useCallback, useMemo, useState } from 'react';
+import { useCallback, useMemo } from 'react';
import { AddItemPlaceholder } from '../../layouts/add-item-placeholder';
import { NavigationPanelTreeNode } from '../../tree/node';
@@ -46,11 +47,13 @@ import { FavoriteFolderOperation } from './operations';
export const NavigationPanelFolderNode = ({
nodeId,
operations,
+ parentPath,
}: {
nodeId: string;
operations?:
| NodeOperation[]
| ((type: string, node: FolderNode) => NodeOperation[]);
+ parentPath: string[];
}) => {
const { organizeService } = useServices({
OrganizeService,
@@ -78,24 +81,34 @@ export const NavigationPanelFolderNode = ({
);
}
if (!data) return null;
if (type === 'doc') {
return (
-
+
);
} else if (type === 'collection') {
return (
);
} else if (type === 'tag') {
return (
-
+
);
}
@@ -119,9 +132,11 @@ const NavigationPanelFolderIcon: NavigationPanelTreeNodeIcon = ({
const NavigationPanelFolderNodeFolder = ({
node,
operations: additionalOperations,
+ parentPath,
}: {
node: FolderNode;
operations?: NodeOperation[];
+ parentPath: string[];
}) => {
const t = useI18n();
const { workspaceService, featureFlagService, workspaceDialogService } =
@@ -135,7 +150,18 @@ const NavigationPanelFolderNodeFolder = ({
const enableEmojiIcon = useLiveData(
featureFlagService.flags.enable_emoji_folder_icon.$
);
- const [collapsed, setCollapsed] = useState(true);
+ const navigationPanelService = useService(NavigationPanelService);
+ const path = useMemo(
+ () => [...parentPath, `folder-${node.id}`],
+ [parentPath, node.id]
+ );
+ const collapsed = useLiveData(navigationPanelService.collapsed$(path));
+ const setCollapsed = useCallback(
+ (value: boolean) => {
+ navigationPanelService.setCollapsed(path, value);
+ },
+ [navigationPanelService, path]
+ );
const { createPage } = usePageHelper(
workspaceService.workspace.docCollection
@@ -171,7 +197,7 @@ const NavigationPanelFolderNodeFolder = ({
target: 'doc',
});
setCollapsed(false);
- }, [createPage, node]);
+ }, [createPage, node, setCollapsed]);
const handleCreateSubfolder = useCallback(
(name: string) => {
@@ -179,7 +205,7 @@ const NavigationPanelFolderNodeFolder = ({
track.$.navigationPanel.organize.createOrganizeItem({ type: 'folder' });
setCollapsed(false);
},
- [node]
+ [node, setCollapsed]
);
const handleAddToFolder = useCallback(
@@ -223,7 +249,7 @@ const NavigationPanelFolderNodeFolder = ({
target: type,
});
},
- [children, node, workspaceDialogService]
+ [children, node, setCollapsed, workspaceDialogService]
);
const createSubTipRenderer = useCallback(
@@ -388,13 +414,16 @@ const NavigationPanelFolderNodeFolder = ({
[t]
);
- const handleCollapsedChange = useCallback((collapsed: boolean) => {
- if (collapsed) {
- setCollapsed(true);
- } else {
- setCollapsed(false);
- }
- }, []);
+ const handleCollapsedChange = useCallback(
+ (collapsed: boolean) => {
+ if (collapsed) {
+ setCollapsed(true);
+ } else {
+ setCollapsed(false);
+ }
+ },
+ [setCollapsed]
+ );
return (
))}
{
const t = useI18n();
const { tagService, globalContextService } = useServices({
TagService,
GlobalContextService,
});
+ const navigationPanelService = useService(NavigationPanelService);
const active =
useLiveData(globalContextService.globalContext.tagId.$) === tagId;
- const [collapsed, setCollapsed] = useState(true);
+ const path = useMemo(
+ () => [...parentPath, `tag-${tagId}`],
+ [parentPath, tagId]
+ );
+ const collapsed = useLiveData(navigationPanelService.collapsed$(path));
+ const setCollapsed = useCallback(
+ (value: boolean) => {
+ navigationPanelService.setCollapsed(path, value);
+ },
+ [navigationPanelService, path]
+ );
const tagRecord = useLiveData(tagService.tagList.tagByTagId$(tagId));
const tagColor = useLiveData(tagRecord?.color$);
@@ -57,7 +71,7 @@ export const NavigationPanelTagNode = ({
() => ({
openNodeCollapsed: () => setCollapsed(false),
}),
- []
+ [setCollapsed]
);
const operations = useNavigationPanelTagNodeOperationsMenu(tagId, option);
const { handleNewDoc } = useNavigationPanelTagNodeOperations(tagId, option);
@@ -86,7 +100,11 @@ export const NavigationPanelTagNode = ({
aria-label={tagName}
data-role="navigation-panel-tag"
>
-
+
);
};
@@ -99,9 +117,11 @@ export const NavigationPanelTagNode = ({
export const NavigationPanelTagNodeDocs = ({
tag,
onNewDoc,
+ path,
}: {
tag: Tag;
onNewDoc?: () => void;
+ path: string[];
}) => {
const t = useI18n();
const tagDocIds = useLiveData(tag.pageIds$);
@@ -109,7 +129,7 @@ export const NavigationPanelTagNodeDocs = ({
return (
<>
{tagDocIds.map(docId => (
-
+
))}
>
diff --git a/packages/frontend/core/src/mobile/components/navigation/sections/collections/index.tsx b/packages/frontend/core/src/mobile/components/navigation/sections/collections/index.tsx
index 436fd3e79..b00bddfb6 100644
--- a/packages/frontend/core/src/mobile/components/navigation/sections/collections/index.tsx
+++ b/packages/frontend/core/src/mobile/components/navigation/sections/collections/index.tsx
@@ -7,7 +7,7 @@ import { useI18n } from '@affine/i18n';
import { track } from '@affine/track';
import { AddCollectionIcon } from '@blocksuite/icons/rc';
import { useLiveData, useServices } from '@toeverything/infra';
-import { useCallback } from 'react';
+import { useCallback, useMemo } from 'react';
import { AddItemPlaceholder } from '../../layouts/add-item-placeholder';
import { CollapsibleSection } from '../../layouts/collapsible-section';
@@ -22,7 +22,7 @@ export const NavigationPanelCollections = () => {
WorkbenchService,
NavigationPanelService,
});
- const navigationPanelSection = navigationPanelService.sections.collections;
+ const path = useMemo(() => ['collections'], []);
const collectionMetas = useLiveData(collectionService.collectionMetas$);
const { openPromptModal } = usePromptModal();
@@ -49,12 +49,13 @@ export const NavigationPanelCollections = () => {
type: 'collection',
});
workbenchService.workbench.openCollection(id);
- navigationPanelSection.setCollapsed(false);
+ navigationPanelService.setCollapsed(path, false);
},
});
}, [
collectionService,
- navigationPanelSection,
+ navigationPanelService,
+ path,
openPromptModal,
t,
workbenchService.workbench,
@@ -62,7 +63,7 @@ export const NavigationPanelCollections = () => {
return (
@@ -71,6 +72,7 @@ export const NavigationPanelCollections = () => {
))}
{
});
const t = useI18n();
- const navigationPanelSection = navigationPanelService.sections.favorites;
+ const path = useMemo(() => ['favorites'], []);
const favorites = useLiveData(favoriteService.favoriteList.sortedList$);
const isLoading = useLiveData(favoriteService.favoriteList.isLoading$);
const { createPage } = usePageHelper(
@@ -38,19 +38,23 @@ export const NavigationPanelFavorites = () => {
newDoc.id,
favoriteService.favoriteList.indexAt('before')
);
- navigationPanelSection.setCollapsed(false);
- }, [createPage, navigationPanelSection, favoriteService.favoriteList]);
+ navigationPanelService.setCollapsed(path, false);
+ }, [createPage, favoriteService.favoriteList, navigationPanelService, path]);
return (
{favorites.map(favorite => (
-
+
))}
{
export const FavoriteNode = ({
favorite,
+ parentPath,
}: {
favorite: {
id: string;
type: FavoriteSupportTypeUnion;
};
+ parentPath: string[];
}) => {
return favorite.type === 'doc' ? (
-
+
) : favorite.type === 'tag' ? (
-
+
) : favorite.type === 'folder' ? (
-
+
) : (
-
+
);
};
diff --git a/packages/frontend/core/src/mobile/components/navigation/sections/organize/index.tsx b/packages/frontend/core/src/mobile/components/navigation/sections/organize/index.tsx
index 579581d22..6cb57dc72 100644
--- a/packages/frontend/core/src/mobile/components/navigation/sections/organize/index.tsx
+++ b/packages/frontend/core/src/mobile/components/navigation/sections/organize/index.tsx
@@ -6,7 +6,7 @@ import { useI18n } from '@affine/i18n';
import track from '@affine/track';
import { AddOrganizeIcon } from '@blocksuite/icons/rc';
import { useLiveData, useServices } from '@toeverything/infra';
-import { useCallback, useState } from 'react';
+import { useCallback, useMemo, useState } from 'react';
import { AddItemPlaceholder } from '../../layouts/add-item-placeholder';
import { CollapsibleSection } from '../../layouts/collapsible-section';
@@ -18,7 +18,7 @@ export const NavigationPanelOrganize = () => {
OrganizeService,
NavigationPanelService,
});
- const navigationPanelSection = navigationPanelService.sections.organize;
+ const path = useMemo(() => ['organize'], []);
const [openNewFolderDialog, setOpenNewFolderDialog] = useState(false);
const t = useI18n();
@@ -36,15 +36,15 @@ export const NavigationPanelOrganize = () => {
rootFolder.indexAt('before')
);
track.$.navigationPanel.organize.createOrganizeItem({ type: 'folder' });
- navigationPanelSection.setCollapsed(false);
+ navigationPanelService.setCollapsed(path, false);
return newFolderId;
},
- [navigationPanelSection, rootFolder]
+ [navigationPanelService, path, rootFolder]
);
return (
{/* TODO(@CatsJuice): Organize loading UI */}
@@ -53,6 +53,7 @@ export const NavigationPanelOrganize = () => {
))}
{
TagService,
NavigationPanelService,
});
- const navigationPanelSection = navigationPanelService.sections.tags;
+ const path = useMemo(() => ['tags'], []);
const tags = useLiveData(tagService.tagList.tags$);
const [showNewTagDialog, setShowNewTagDialog] = useState(false);
@@ -36,19 +36,23 @@ export const NavigationPanelTags = () => {
setShowNewTagDialog(false);
tagService.tagList.createTag(name, color);
track.$.navigationPanel.organize.createOrganizeItem({ type: 'tag' });
- navigationPanelSection.setCollapsed(false);
+ navigationPanelService.setCollapsed(path, false);
},
- [navigationPanelSection, tagService]
+ [navigationPanelService, path, tagService]
);
return (
{tags.map(tag => (
-
+
))}
}
diff --git a/packages/frontend/core/src/mobile/views/recent-docs/index.tsx b/packages/frontend/core/src/mobile/views/recent-docs/index.tsx
index b2eb46bbb..87e07f003 100644
--- a/packages/frontend/core/src/mobile/views/recent-docs/index.tsx
+++ b/packages/frontend/core/src/mobile/views/recent-docs/index.tsx
@@ -24,7 +24,7 @@ export const RecentDocs = ({ max = 5 }: { max?: number }) => {
return (
= {
- recent: true,
- favorites: false,
- organize: false,
- collections: true,
- tags: true,
- favoritesOld: true,
- migrationFavorites: true,
- others: false,
-};
-
-export class NavigationPanelSection extends Entity<{
- name: CollapsibleSectionName;
-}> {
- name: CollapsibleSectionName = this.props.name;
- key = `explorer.section.${this.name}`;
- defaultValue = DEFAULT_COLLAPSABLE_STATE[this.name];
-
- constructor(private readonly globalCache: GlobalCache) {
- super();
- }
-
- collapsed$ = LiveData.from(
- this.globalCache
- .watch(this.key)
- .pipe(map(v => v ?? this.defaultValue)),
- this.defaultValue
- );
-
- setCollapsed(collapsed: boolean) {
- this.globalCache.set(this.key, collapsed);
- }
-}
diff --git a/packages/frontend/core/src/modules/navigation-panel/index.ts b/packages/frontend/core/src/modules/navigation-panel/index.ts
index aabe76403..06422e59a 100644
--- a/packages/frontend/core/src/modules/navigation-panel/index.ts
+++ b/packages/frontend/core/src/modules/navigation-panel/index.ts
@@ -1,15 +1,12 @@
import { type Framework } from '@toeverything/infra';
import { GlobalCache } from '../storage';
-import { WorkspaceScope } from '../workspace';
-import { NavigationPanelSection } from './entities/navigation-panel-section';
+import { WorkspaceScope, WorkspaceService } from '../workspace';
import { NavigationPanelService } from './services/navigation-panel';
export { NavigationPanelService } from './services/navigation-panel';
-export type { CollapsibleSectionName } from './types';
export function configureNavigationPanelModule(framework: Framework) {
framework
.scope(WorkspaceScope)
- .service(NavigationPanelService)
- .entity(NavigationPanelSection, [GlobalCache]);
+ .service(NavigationPanelService, [GlobalCache, WorkspaceService]);
}
diff --git a/packages/frontend/core/src/modules/navigation-panel/services/navigation-panel.ts b/packages/frontend/core/src/modules/navigation-panel/services/navigation-panel.ts
index b7f3f1c12..b3a035b3c 100644
--- a/packages/frontend/core/src/modules/navigation-panel/services/navigation-panel.ts
+++ b/packages/frontend/core/src/modules/navigation-panel/services/navigation-panel.ts
@@ -1,25 +1,47 @@
-import { Service } from '@toeverything/infra';
+import { LiveData, Service } from '@toeverything/infra';
-import { NavigationPanelSection } from '../entities/navigation-panel-section';
-import type { CollapsibleSectionName } from '../types';
+import type { GlobalCache } from '../../storage/providers/global';
+import type { WorkspaceService } from '../../workspace';
-const allSectionName: Array = [
- 'recent', // mobile only
- 'favorites',
- 'organize',
- 'collections',
- 'tags',
- 'favoritesOld',
- 'migrationFavorites',
- 'others',
-];
+const DEFAULT_COLLAPSABLE_STATE: Record = {
+ recent: true,
+ favorites: false,
+ organize: false,
+ collections: true,
+ tags: true,
+ favoritesOld: true,
+ migrationFavorites: true,
+ others: false,
+};
export class NavigationPanelService extends Service {
- readonly sections = allSectionName.reduce(
- (prev, name) =>
- Object.assign(prev, {
- [name]: this.framework.createEntity(NavigationPanelSection, { name }),
- }),
- {} as Record
- );
+ constructor(
+ private readonly globalCache: GlobalCache,
+ private readonly workspaceService: WorkspaceService
+ ) {
+ super();
+ }
+
+ private readonly collapsedCache = new Map>();
+
+ collapsed$(path: string[]) {
+ const pathKey = path.join(':');
+ const key = `navigation:${this.workspaceService.workspace.id}:${pathKey}`;
+ const cached$ = this.collapsedCache.get(key);
+ if (!cached$) {
+ const liveData$ = LiveData.from(
+ this.globalCache.watch(key),
+ undefined
+ ).map(v => v ?? DEFAULT_COLLAPSABLE_STATE[pathKey] ?? true);
+ this.collapsedCache.set(key, liveData$);
+ return liveData$;
+ }
+ return cached$;
+ }
+
+ setCollapsed(path: string[], collapsed: boolean) {
+ const pathKey = path.join(':');
+ const key = `navigation:${this.workspaceService.workspace.id}:${pathKey}`;
+ this.globalCache.set(key, collapsed);
+ }
}
diff --git a/packages/frontend/core/src/modules/navigation-panel/types.ts b/packages/frontend/core/src/modules/navigation-panel/types.ts
deleted file mode 100644
index b2423fb67..000000000
--- a/packages/frontend/core/src/modules/navigation-panel/types.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-export type CollapsibleSectionName =
- | 'recent'
- | 'collections'
- | 'favorites'
- | 'tags'
- | 'organize'
- | 'favoritesOld'
- | 'migrationFavorites'
- | 'others';