Yuki Takei 7 месяцев назад
Родитель
Сommit
f4d2210812

+ 58 - 0
apps/app/src/pages/[[...path]]/use-same-route-navigation.spec.tsx

@@ -163,4 +163,62 @@ describe('useSameRouteNavigation - Essential Bug Fix Verification', () => {
       expect(mockMutateEditingMarkdown).toHaveBeenCalledWith('Second page content');
     });
   });
+
+  describe('Root page navigation debugging', () => {
+    it('should handle navigation to root page correctly', async() => {
+      // Test navigation to root page specifically to debug the reported issue
+      const props = createProps('/');
+      mockRouter.asPath = '/';
+
+      const rootPageData = createPageDataMock('rootPageId', '/', 'Root page content');
+      mockFetchCurrentPage.mockResolvedValue(rootPageData);
+
+      renderHook(() => useSameRouteNavigation(props));
+
+      await act(async() => {
+        await new Promise(resolve => setTimeout(resolve, 0));
+      });
+
+      // Check if root page navigation works correctly
+      expect(mockFetchCurrentPage).toHaveBeenCalledWith('/');
+      expect(mockMutateEditingMarkdown).toHaveBeenCalledWith('Root page content');
+    });
+
+    it('should handle navigation from other page to root page', async() => {
+      // Start from a regular page
+      const props = createProps('/some/page');
+      mockRouter.asPath = '/some/page';
+
+      const regularPageData = createPageDataMock('regularPageId', '/some/page', 'Regular page content');
+      mockFetchCurrentPage.mockResolvedValue(regularPageData);
+
+      const { rerender } = renderHook(() => useSameRouteNavigation(props));
+
+      await act(async() => {
+        await new Promise(resolve => setTimeout(resolve, 0));
+      });
+
+      // Verify initial page was loaded
+      expect(mockFetchCurrentPage).toHaveBeenCalledWith('/some/page');
+
+      // Clear mocks and prepare for root page navigation
+      mockFetchCurrentPage.mockClear();
+      mockMutateEditingMarkdown.mockClear();
+
+      const rootPageData = createPageDataMock('rootPageId', '/', 'Root page content');
+      mockFetchCurrentPage.mockResolvedValue(rootPageData);
+
+      // Navigate to root page
+      mockRouter.asPath = '/';
+      rerender();
+
+      await act(async() => {
+        await new Promise(resolve => setTimeout(resolve, 0));
+      });
+
+      // This should work correctly - the reported bug scenario
+      expect(mockFetchCurrentPage).toHaveBeenCalledWith('/');
+      expect(mockMutateEditingMarkdown).toHaveBeenCalledWith('Root page content');
+    });
+  });
 });

+ 44 - 7
apps/app/src/pages/[[...path]]/use-same-route-navigation.ts

@@ -37,30 +37,43 @@ const usePageStateManager = () => {
   const { mutate: mutateEditingMarkdown } = useEditingMarkdown();
 
   const updatePageState = useCallback(async(targetPathname: string, targetPageId: string | null) => {
-    try {
-      // Clear current state for clean transition
-      setCurrentPageId(undefined);
+    console.log('[NAV-DEBUG] updatePageState called:', {
+      targetPathname,
+      targetPageId,
+      isRootPage: targetPathname === '/',
+      timestamp: new Date().toISOString(),
+    });
 
-      // Set new pageId if available
-      if (targetPageId) {
-        setCurrentPageId(targetPageId);
+    try {
+      // OPTIMIZATION: Only update pageId if it actually changes
+      const currentPageId = pageId;
+      if (currentPageId !== targetPageId) {
+        console.log('[NAV-DEBUG] Updating pageId:', { from: currentPageId, to: targetPageId });
+        setCurrentPageId(targetPageId || undefined);
+      }
+      else {
+        console.log('[NAV-DEBUG] PageId unchanged, skipping update');
       }
 
       // Fetch page data
+      console.log('[NAV-DEBUG] Calling fetchCurrentPage with:', targetPathname);
       const pageData = await fetchCurrentPage(targetPathname);
 
       // Update editing markdown if we have body content
       if (pageData?.revision?.body !== undefined) {
+        console.log('[NAV-DEBUG] Updating editing markdown, body length:', pageData.revision.body.length);
         mutateEditingMarkdown(pageData.revision.body);
       }
 
+      console.log('[NAV-DEBUG] Navigation successful for:', targetPathname);
       return true; // Success
     }
     catch (error) {
       logger.error('Navigation failed for pathname:', targetPathname, error);
+      console.log('[NAV-DEBUG] Navigation failed:', { targetPathname, error });
       return false; // Failure
     }
-  }, [setCurrentPageId, fetchCurrentPage, mutateEditingMarkdown]);
+  }, [pageId, setCurrentPageId, fetchCurrentPage, mutateEditingMarkdown]);
 
   return { pageId, updatePageState };
 };
@@ -94,13 +107,24 @@ export const useSameRouteNavigation = (props: Props): void => {
 
   // Process pathname changes - monitor both props.currentPathname and router.asPath
   useEffect(() => {
+    console.log('[NAV-DEBUG] useEffect triggered:', {
+      targetPathname,
+      lastProcessedPathname: lastProcessedPathnameRef.current,
+      hasInitialData,
+      currentPageId: pageId,
+      currentPagePath: currentPage?.path,
+      isFetching: isFetchingRef.current,
+    });
+
     // Skip if we already processed this pathname
     if (lastProcessedPathnameRef.current === targetPathname) {
+      console.log('[NAV-DEBUG] Skipping - already processed:', targetPathname);
       return;
     }
 
     // Skip if we have initial data and don't need to refetch
     if (hasInitialData) {
+      console.log('[NAV-DEBUG] Skipping - has initial data');
       lastProcessedPathnameRef.current = targetPathname;
       return;
     }
@@ -109,6 +133,13 @@ export const useSameRouteNavigation = (props: Props): void => {
     const targetPageId = extractPageIdFromPathname(targetPathname);
     const currentPagePath = currentPage?.path;
 
+    console.log('[NAV-DEBUG] Checking if should fetch:', {
+      targetPageId,
+      targetPathname,
+      currentPageId: pageId,
+      currentPagePath,
+    });
+
     // Use extracted shouldFetchPage function
     const shouldFetch = shouldFetchPage({
       targetPageId,
@@ -117,17 +148,22 @@ export const useSameRouteNavigation = (props: Props): void => {
       currentPagePath,
     });
 
+    console.log('[NAV-DEBUG] shouldFetch result:', shouldFetch);
+
     if (!shouldFetch) {
+      console.log('[NAV-DEBUG] Skipping - shouldFetch is false');
       lastProcessedPathnameRef.current = targetPathname;
       return;
     }
 
     // Prevent concurrent fetches
     if (isFetchingRef.current) {
+      console.log('[NAV-DEBUG] Skipping - already fetching');
       return;
     }
 
     isFetchingRef.current = true;
+    console.log('[NAV-DEBUG] Starting navigation for:', targetPathname);
 
     const performNavigation = async(): Promise<void> => {
       await updatePageState(targetPathname, targetPageId);
@@ -135,6 +171,7 @@ export const useSameRouteNavigation = (props: Props): void => {
       // Mark as processed regardless of success to prevent retry loops
       lastProcessedPathnameRef.current = targetPathname;
       isFetchingRef.current = false;
+      console.log('[NAV-DEBUG] Navigation completed for:', targetPathname);
     };
 
     performNavigation();

+ 25 - 0
apps/app/src/states/page/use-fetch-current-page.ts

@@ -44,6 +44,15 @@ export const useFetchCurrentPage = (): {
 
       const currentPageId = get(currentPageIdAtom);
 
+      // DEBUG: Log detailed navigation state
+      console.log('[FETCH-DEBUG] useFetchCurrentPage called:', {
+        currentPathname,
+        currentPath,
+        currentPageId,
+        timestamp: new Date().toISOString(),
+        isRootPage: currentPath === '/',
+      });
+
       // Get URL parameter for specific revisionId - only when needed
       const revisionId = isClient() && window.location.search
         ? new URLSearchParams(window.location.search).get('revisionId') || undefined
@@ -62,11 +71,14 @@ export const useFetchCurrentPage = (): {
         // Use pageId when available, fallback to path
         if (currentPageId) {
           apiParams.pageId = currentPageId;
+          console.log('[FETCH-DEBUG] Using pageId:', currentPageId);
         }
         else if (currentPath) {
           apiParams.path = currentPath;
+          console.log('[FETCH-DEBUG] Using path:', currentPath);
         }
         else {
+          console.log('[FETCH-DEBUG] No valid identifier, returning null');
           return null; // No valid identifier
         }
 
@@ -77,12 +89,25 @@ export const useFetchCurrentPage = (): {
 
         const newData = response.data.page;
 
+        // DEBUG: Log successful fetch result
+        console.log('[FETCH-DEBUG] Page fetched successfully:', {
+          pageId: newData?._id,
+          path: newData?.path,
+          isRootPage: newData?.path === '/',
+          previousPageId: currentPageId,
+          pageIdChanged: newData?._id !== currentPageId,
+        });
+
         // Batch atom updates to minimize re-renders
         set(currentPageDataAtom, newData);
         set(pageNotFoundAtom, false);
 
         // Update pageId atom if data differs from current
         if (newData?._id !== currentPageId) {
+          console.log('[FETCH-DEBUG] Updating pageId atom:', {
+            from: currentPageId,
+            to: newData?._id,
+          });
           set(currentPageIdAtom, newData?._id);
         }