|
|
@@ -1,29 +1,30 @@
|
|
|
-import { useCallback } from 'react';
|
|
|
+import { useCallback, useEffect } from 'react';
|
|
|
+import type { TreeInstance } from '@headless-tree/core';
|
|
|
import { atom, useAtomValue, useSetAtom } from 'jotai';
|
|
|
|
|
|
+import { ROOT_PAGE_VIRTUAL_ID } from '~/constants/page-tree';
|
|
|
+
|
|
|
// Update generation number
|
|
|
-const treeUpdateGenerationAtom = atom<number>(1);
|
|
|
+const generationAtom = atom<number>(1);
|
|
|
|
|
|
// Array of IDs for last updated items
|
|
|
-// ['*'] is a special value meaning full tree update
|
|
|
-const lastUpdatedItemIdsAtom = atom<string[]>([]);
|
|
|
+// null is a special value meaning full tree update
|
|
|
+const lastUpdatedItemIdsAtom = atom<string[] | null>(null);
|
|
|
|
|
|
// Read-only hooks
|
|
|
-export const useTreeUpdateGeneration = () => {
|
|
|
- return useAtomValue(treeUpdateGenerationAtom);
|
|
|
-};
|
|
|
+export const usePageTreeInformationGeneration = () =>
|
|
|
+ useAtomValue(generationAtom);
|
|
|
|
|
|
-export const useLastUpdatedItemIds = () => {
|
|
|
- return useAtomValue(lastUpdatedItemIdsAtom);
|
|
|
-};
|
|
|
+export const usePageTreeInformationLastUpdatedItemIds = () =>
|
|
|
+ useAtomValue(lastUpdatedItemIdsAtom);
|
|
|
|
|
|
// Hook for notifying tree updates
|
|
|
-export const useNotifyTreeUpdate = () => {
|
|
|
- const setGeneration = useSetAtom(treeUpdateGenerationAtom);
|
|
|
+export const usePageTreeInformationUpdate = () => {
|
|
|
+ const setGeneration = useSetAtom(generationAtom);
|
|
|
const setLastUpdatedIds = useSetAtom(lastUpdatedItemIdsAtom);
|
|
|
|
|
|
// Notify update for specific items
|
|
|
- const notifyItemsUpdated = useCallback(
|
|
|
+ const notifyUpdateItems = useCallback(
|
|
|
(itemIds: string[]) => {
|
|
|
setLastUpdatedIds(itemIds);
|
|
|
setGeneration((prev) => prev + 1);
|
|
|
@@ -32,13 +33,51 @@ export const useNotifyTreeUpdate = () => {
|
|
|
);
|
|
|
|
|
|
// Notify update for all trees
|
|
|
- const notifyAllTreesUpdated = useCallback(() => {
|
|
|
- setLastUpdatedIds(['*']);
|
|
|
+ const notifyUpdateAllTrees = useCallback(() => {
|
|
|
+ setLastUpdatedIds(null);
|
|
|
setGeneration((prev) => prev + 1);
|
|
|
}, [setGeneration, setLastUpdatedIds]);
|
|
|
|
|
|
return {
|
|
|
- notifyItemsUpdated,
|
|
|
- notifyAllTreesUpdated,
|
|
|
+ notifyUpdateItems,
|
|
|
+ notifyUpdateAllTrees,
|
|
|
};
|
|
|
};
|
|
|
+
|
|
|
+export const usePageTreeRevalidationEffect = (
|
|
|
+ tree: TreeInstance<unknown>,
|
|
|
+ generation: number,
|
|
|
+ opts?: { onRevalidated?: () => void },
|
|
|
+) => {
|
|
|
+ const globalGeneration = useAtomValue(generationAtom);
|
|
|
+ const globalLastUpdatedItemIds = useAtomValue(lastUpdatedItemIdsAtom);
|
|
|
+
|
|
|
+ const { getItemInstance } = tree;
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ if (globalGeneration <= generation) return; // Already up to date
|
|
|
+
|
|
|
+ // Determine update scope
|
|
|
+ const shouldUpdateAll = globalLastUpdatedItemIds == null;
|
|
|
+
|
|
|
+ if (shouldUpdateAll) {
|
|
|
+ // Full tree update: refetch from root
|
|
|
+ const root = getItemInstance(ROOT_PAGE_VIRTUAL_ID);
|
|
|
+ root?.invalidateChildrenIds(true);
|
|
|
+ } else {
|
|
|
+ // Partial update: refetch children of specified items
|
|
|
+ globalLastUpdatedItemIds.forEach((itemId) => {
|
|
|
+ const item = getItemInstance(itemId);
|
|
|
+ item?.invalidateChildrenIds(true);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ opts?.onRevalidated?.();
|
|
|
+ }, [
|
|
|
+ globalGeneration,
|
|
|
+ generation,
|
|
|
+ getItemInstance,
|
|
|
+ globalLastUpdatedItemIds,
|
|
|
+ opts,
|
|
|
+ ]);
|
|
|
+};
|