From 234ae759f53dfd3b1047b8e9e1a855b9e3c5ab52 Mon Sep 17 00:00:00 2001 From: vvzvlad Date: Sat, 20 Jun 2026 17:39:34 +0300 Subject: [PATCH] refactor(tree): borrow cleanups from the sibling expand-all impl - extract collectAllIds / collectBranchIds into tree/utils and use them in space-tree.tsx instead of inline closures - drop the duplicate SidebarPageTreeDto, reuse the existing SidebarPageDto for the /pages/tree endpoint - type the getSpaceTree client call as api.post<{ items: IPage[] }> Co-Authored-By: Claude Opus 4.8 --- .../features/page/services/page-service.ts | 2 +- .../page/tree/components/space-tree.tsx | 22 +++----------- .../src/features/page/tree/utils/utils.ts | 30 +++++++++++++++++++ .../src/core/page/dto/sidebar-page.dto.ts | 10 ------- apps/server/src/core/page/page.controller.ts | 4 +-- 5 files changed, 37 insertions(+), 31 deletions(-) diff --git a/apps/client/src/features/page/services/page-service.ts b/apps/client/src/features/page/services/page-service.ts index 6434ec7c..47b30fb7 100644 --- a/apps/client/src/features/page/services/page-service.ts +++ b/apps/client/src/features/page/services/page-service.ts @@ -96,7 +96,7 @@ export async function getSpaceTree(params: { spaceId: string; pageId?: string; }): Promise { - const req = await api.post("/pages/tree", params); + const req = await api.post<{ items: IPage[] }>("/pages/tree", params); return req.data.items; } diff --git a/apps/client/src/features/page/tree/components/space-tree.tsx b/apps/client/src/features/page/tree/components/space-tree.tsx index e3e339c9..24fe8f0a 100644 --- a/apps/client/src/features/page/tree/components/space-tree.tsx +++ b/apps/client/src/features/page/tree/components/space-tree.tsx @@ -25,6 +25,8 @@ import { buildTree, buildTreeWithChildren, mergeRootTrees, + collectAllIds, + collectBranchIds, } from "@/features/page/tree/utils/utils.ts"; import { SpaceTreeNode } from "@/features/page/tree/types.ts"; import { treeModel } from "@/features/page/tree/model/tree-model"; @@ -226,16 +228,7 @@ const SpaceTree = forwardRef(function SpaceTree( }); // Open every branch node (node with children) of the current space only. - const branchIds: string[] = []; - const collectBranchIds = (nodes: SpaceTreeNode[]) => { - for (const n of nodes) { - if (n.children && n.children.length > 0) { - branchIds.push(n.id); - collectBranchIds(n.children); - } - } - }; - collectBranchIds(fullTree); + const branchIds = collectBranchIds(fullTree); setOpenTreeNodes((prev) => { const next = { ...prev }; @@ -260,14 +253,7 @@ const SpaceTree = forwardRef(function SpaceTree( const collapseAll = useCallback(() => { // The open-map is shared across spaces; collapse only current-space ids so // other spaces' expanded state is left intact. - const ids = new Set(); - const walk = (nodes: SpaceTreeNode[]) => { - for (const n of nodes) { - ids.add(n.id); - if (n.children?.length) walk(n.children); - } - }; - walk(filteredData); + const ids = collectAllIds(filteredData); setOpenTreeNodes((prev) => { const next = { ...prev }; diff --git a/apps/client/src/features/page/tree/utils/utils.ts b/apps/client/src/features/page/tree/utils/utils.ts index 0c42f9b9..5d5c0bad 100644 --- a/apps/client/src/features/page/tree/utils/utils.ts +++ b/apps/client/src/features/page/tree/utils/utils.ts @@ -216,3 +216,33 @@ export function mergeRootTrees( return sortPositionKeys(merged); } + +// Collect every node id in the tree (roots, branches, leaves). Used by +// collapseAll to clear the open-state map for all current-space nodes. +export function collectAllIds(nodes: SpaceTreeNode[]): string[] { + const ids: string[] = []; + const walk = (list: SpaceTreeNode[]) => { + for (const n of list) { + ids.push(n.id); + if (n.children?.length) walk(n.children); + } + }; + walk(nodes); + return ids; +} + +// Collect ids of branch nodes (nodes that have children). Used by expandAll to +// open every branch in the open-state map; leaves need no entry. +export function collectBranchIds(nodes: SpaceTreeNode[]): string[] { + const ids: string[] = []; + const walk = (list: SpaceTreeNode[]) => { + for (const n of list) { + if (n.children?.length) { + ids.push(n.id); + walk(n.children); + } + } + }; + walk(nodes); + return ids; +} diff --git a/apps/server/src/core/page/dto/sidebar-page.dto.ts b/apps/server/src/core/page/dto/sidebar-page.dto.ts index 1fd96700..7f4568f9 100644 --- a/apps/server/src/core/page/dto/sidebar-page.dto.ts +++ b/apps/server/src/core/page/dto/sidebar-page.dto.ts @@ -9,13 +9,3 @@ export class SidebarPageDto { @IsString() pageId: string; } - -export class SidebarPageTreeDto { - @IsOptional() - @IsUUID() - spaceId?: string; - - @IsOptional() - @IsString() - pageId?: string; -} diff --git a/apps/server/src/core/page/page.controller.ts b/apps/server/src/core/page/page.controller.ts index 03ea9353..25c358ee 100644 --- a/apps/server/src/core/page/page.controller.ts +++ b/apps/server/src/core/page/page.controller.ts @@ -32,7 +32,7 @@ import { import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard'; import { PaginationOptions } from '@docmost/db/pagination/pagination-options'; import { Page, User, Workspace } from '@docmost/db/types/entity.types'; -import { SidebarPageDto, SidebarPageTreeDto } from './dto/sidebar-page.dto'; +import { SidebarPageDto } from './dto/sidebar-page.dto'; import { SpaceCaslAction, SpaceCaslSubject, @@ -581,7 +581,7 @@ export class PageController { @HttpCode(HttpStatus.OK) @Post('/tree') async getPagesTree( - @Body() dto: SidebarPageTreeDto, + @Body() dto: SidebarPageDto, @AuthUser() user: User, ) { if (!dto.spaceId && !dto.pageId) {