diff --git a/apps/client/src/features/page/queries/page-query.ts b/apps/client/src/features/page/queries/page-query.ts index 64d03ddd..e7388fe4 100644 --- a/apps/client/src/features/page/queries/page-query.ts +++ b/apps/client/src/features/page/queries/page-query.ts @@ -163,9 +163,6 @@ export function useDeletePageMutation() { export function useMovePageMutation() { return useMutation({ mutationFn: (data) => movePage(data), - onSuccess: () => { - invalidateOnMovePage(); - }, }); } @@ -458,17 +455,127 @@ export function invalidateOnUpdatePage( }); } -export function invalidateOnMovePage() { - //for move invalidate all sidebars for now (how to do???) - //invalidate all root sidebar pages - queryClient.invalidateQueries({ - queryKey: ["root-sidebar-pages"], - }); - //invalidate all sub sidebar pages - queryClient.invalidateQueries({ - queryKey: ["sidebar-pages"], - }); - // --- +export function updateCacheOnMovePage( + spaceId: string, + pageId: string, + oldParentId: string | null, + newParentId: string | null, + pageData: Partial, +) { + // Remove page from old parent's cache + const oldQueryKey = + oldParentId === null + ? ["root-sidebar-pages", spaceId] + : ["sidebar-pages", { pageId: oldParentId, spaceId }]; + + queryClient.setQueryData>>( + oldQueryKey, + (old) => { + if (!old) return old; + return { + ...old, + pages: old.pages.map((page) => ({ + ...page, + items: page.items.filter((item) => item.id !== pageId), + })), + }; + }, + ); + + // Update old parent's hasChildren flag if it has no more children + if (oldParentId !== null) { + const oldParentCache = queryClient.getQueryData< + InfiniteData> + >(["sidebar-pages", { pageId: oldParentId, spaceId }]); + + const remainingChildren = + oldParentCache?.pages.flatMap((p) => p.items).length ?? 0; + + if (remainingChildren === 0) { + // Update hasChildren in all caches where old parent appears + const allSideBarMatches = queryClient.getQueriesData({ + predicate: (query) => + query.queryKey[0] === "root-sidebar-pages" || + query.queryKey[0] === "sidebar-pages", + }); + + allSideBarMatches.forEach(([key]) => { + queryClient.setQueryData>>( + key, + (old) => { + if (!old) return old; + return { + ...old, + pages: old.pages.map((page) => ({ + ...page, + items: page.items.map((item) => + item.id === oldParentId + ? { ...item, hasChildren: false } + : item, + ), + })), + }; + }, + ); + }); + } + } + + // Add page to new parent's cache + const newQueryKey = + newParentId === null + ? ["root-sidebar-pages", spaceId] + : ["sidebar-pages", { pageId: newParentId, spaceId }]; + + queryClient.setQueryData>>>( + newQueryKey, + (old) => { + if (!old) return old; + + // Check if page already exists in new location + const exists = old.pages.some((page) => + page.items.some((item) => item.id === pageId), + ); + if (exists) return old; + + return { + ...old, + pages: old.pages.map((page, index) => { + if (index === old.pages.length - 1) { + return { + ...page, + items: [...page.items, pageData], + }; + } + return page; + }), + }; + }, + ); + + // Update new parent's hasChildren flag + if (newParentId !== null) { + const allSideBarMatches = queryClient.getQueriesData({ + predicate: (query) => + query.queryKey[0] === "root-sidebar-pages" || + query.queryKey[0] === "sidebar-pages", + }); + + allSideBarMatches.forEach(([key]) => { + queryClient.setQueryData>>(key, (old) => { + if (!old) return old; + return { + ...old, + pages: old.pages.map((page) => ({ + ...page, + items: page.items.map((item) => + item.id === newParentId ? { ...item, hasChildren: true } : item, + ), + })), + }; + }); + }); + } } export function invalidateOnDeletePage(pageId: string) { diff --git a/apps/client/src/features/page/tree/hooks/use-tree-mutation.ts b/apps/client/src/features/page/tree/hooks/use-tree-mutation.ts index b2a58f30..162992dd 100644 --- a/apps/client/src/features/page/tree/hooks/use-tree-mutation.ts +++ b/apps/client/src/features/page/tree/hooks/use-tree-mutation.ts @@ -16,6 +16,7 @@ import { useRemovePageMutation, useMovePageMutation, useUpdatePageMutation, + updateCacheOnMovePage, } from "@/features/page/queries/page-query.ts"; import { generateJitteredKeyBetween } from "fractional-indexing-jittered"; import { SpaceTreeNode } from "@/features/page/tree/types.ts"; @@ -175,9 +176,25 @@ export function useTreeMutation(spaceId: string) { parentPageId: args.parentId, }; + const draggedNode = args.dragNodes[0]; + const nodeData = draggedNode.data as SpaceTreeNode; + const oldParentId = nodeData.parentPageId ?? null; + const pageData = { + id: nodeData.id, + slugId: nodeData.slugId, + title: nodeData.name, + icon: nodeData.icon, + position: newPosition, + spaceId: nodeData.spaceId, + parentPageId: args.parentId, + hasChildren: nodeData.hasChildren, + }; + try { await movePageMutation.mutateAsync(payload); + updateCacheOnMovePage(spaceId, draggedNodeId, oldParentId, args.parentId, pageData); + setTimeout(() => { emit({ operation: "moveTreeNode", @@ -185,8 +202,10 @@ export function useTreeMutation(spaceId: string) { payload: { id: draggedNodeId, parentId: args.parentId, + oldParentId, index: args.index, position: newPosition, + pageData, }, }); }, 50); diff --git a/apps/client/src/features/websocket/types/types.ts b/apps/client/src/features/websocket/types/types.ts index 21561038..3bc5b941 100644 --- a/apps/client/src/features/websocket/types/types.ts +++ b/apps/client/src/features/websocket/types/types.ts @@ -45,8 +45,10 @@ export type MoveTreeNodeEvent = { payload: { id: string; parentId: string; + oldParentId: string | null; index: number; position: string; + pageData: Partial; }; }; diff --git a/apps/client/src/features/websocket/use-query-subscription.ts b/apps/client/src/features/websocket/use-query-subscription.ts index 3aa95417..faa7139f 100644 --- a/apps/client/src/features/websocket/use-query-subscription.ts +++ b/apps/client/src/features/websocket/use-query-subscription.ts @@ -8,7 +8,7 @@ import { IPagination } from "@/lib/types"; import { invalidateOnCreatePage, invalidateOnDeletePage, - invalidateOnMovePage, + updateCacheOnMovePage, invalidateOnUpdatePage, } from "../page/queries/page-query"; import { RQ_KEY } from "../comment/queries/comment-query"; @@ -41,7 +41,13 @@ export const useQuerySubscription = () => { invalidateOnCreatePage(data.payload.data); break; case "moveTreeNode": - invalidateOnMovePage(); + updateCacheOnMovePage( + data.spaceId, + data.payload.id, + data.payload.oldParentId, + data.payload.parentId, + data.payload.pageData, + ); break; case "deleteTreeNode": invalidateOnDeletePage(data.payload.node.id);