فهرست منبع

re-implement useIsLatestRevisionId

Yuki Takei 5 ماه پیش
والد
کامیت
8eb91cb93b

+ 4 - 3
apps/app/src/client/components/Page/DisplaySwitcher.tsx

@@ -3,8 +3,9 @@ import type { JSX } from 'react';
 import dynamic from 'next/dynamic';
 
 import { useHashChangedEffect } from '~/client/services/side-effects/hash-changed';
-import { useIsEditable, useLatestRevision } from '~/states/page';
+import { useIsEditable } from '~/states/page';
 import { EditorMode, useEditorMode, useReservedNextCaretLine } from '~/states/ui/editor';
+import { useIsLatestRevision } from '~/stores/page';
 
 import { LazyRenderer } from '../Common/LazyRenderer';
 
@@ -17,14 +18,14 @@ export const DisplaySwitcher = (): JSX.Element => {
 
   const { editorMode } = useEditorMode();
   const isEditable = useIsEditable();
-  const isLatestRevision = useLatestRevision();
+  const { data: isLatestRevision } = useIsLatestRevision();
 
   useHashChangedEffect();
   useReservedNextCaretLine();
 
   return (
     <LazyRenderer shouldRender={isEditable === true && editorMode === EditorMode.Editor}>
-      { isLatestRevision
+      { isLatestRevision !== false
         ? <PageEditor />
         : <PageEditorReadOnly />
       }

+ 5 - 3
apps/app/src/client/components/PageEditor/PageEditorReadOnly.tsx

@@ -5,7 +5,8 @@ import { CodeMirrorEditorReadOnly } from '@growi/editor/dist/client/components/C
 import { throttle } from 'throttle-debounce';
 
 import { useShouldExpandContent } from '~/services/layout/use-should-expand-content';
-import { useCurrentPageData, useLatestRevision } from '~/states/page';
+import { useCurrentPageData } from '~/states/page';
+import { useIsLatestRevision } from '~/stores/page';
 import { usePreviewOptions } from '~/stores/renderer';
 
 import { EditorNavbar } from './EditorNavbar';
@@ -21,7 +22,7 @@ export const PageEditorReadOnly = react.memo(({ visibility }: Props): JSX.Elemen
 
   const currentPage = useCurrentPageData();
   const { data: rendererOptions } = usePreviewOptions();
-  const isLatestRevision = useLatestRevision();
+  const { data: isLatestRevision } = useIsLatestRevision();
   const shouldExpandContent = useShouldExpandContent(currentPage);
 
   const { scrollEditorHandler, scrollPreviewHandler } = useScrollSync(GlobalCodeMirrorEditorKey.READONLY, previewRef);
@@ -30,7 +31,8 @@ export const PageEditorReadOnly = react.memo(({ visibility }: Props): JSX.Elemen
 
   const revisionBody = currentPage?.revision?.body;
 
-  if (rendererOptions == null || isLatestRevision) {
+  // Show read-only editor only when viewing an old revision
+  if (rendererOptions == null || isLatestRevision !== false) {
     return <></>;
   }
 

+ 5 - 7
apps/app/src/components/PageView/PageAlerts/OldRevisionAlert.tsx

@@ -3,17 +3,14 @@ import { useRouter } from 'next/router';
 import { returnPathForURL } from '@growi/core/dist/utils/path-utils';
 import { useTranslation } from 'react-i18next';
 
-import {
-  useCurrentPageData,
-  useFetchCurrentPage,
-  useLatestRevision,
-} from '~/states/page';
+import { useCurrentPageData, useFetchCurrentPage } from '~/states/page';
+import { useIsLatestRevision } from '~/stores/page';
 
 export const OldRevisionAlert = (): JSX.Element => {
   const router = useRouter();
   const { t } = useTranslation();
 
-  const isOldRevisionPage = useLatestRevision();
+  const { data: isLatestRevision } = useIsLatestRevision();
   const page = useCurrentPageData();
   const { fetchCurrentPage } = useFetchCurrentPage();
 
@@ -27,7 +24,8 @@ export const OldRevisionAlert = (): JSX.Element => {
     fetchCurrentPage({ force: true });
   }, [fetchCurrentPage, page, router]);
 
-  if (page == null || isOldRevisionPage) {
+  // Show alert only when viewing an old revision (isLatestRevision === false)
+  if (isLatestRevision !== false) {
     // biome-ignore lint/complexity/noUselessFragments: ignore
     return <></>;
   }

+ 0 - 3
apps/app/src/states/page/hooks.ts

@@ -17,7 +17,6 @@ import {
   isRevisionOutdatedAtom,
   isTrashPageAtom,
   isUntitledPageAtom,
-  latestRevisionAtom,
   pageNotFoundAtom,
   redirectFromAtom,
   remoteRevisionBodyAtom,
@@ -45,8 +44,6 @@ export const useIsIdenticalPath = () => useAtomValue(isIdenticalPathAtom);
 
 export const useIsForbidden = () => useAtomValue(isForbiddenAtom);
 
-export const useLatestRevision = () => useAtomValue(latestRevisionAtom);
-
 export const useShareLinkId = () => useAtomValue(shareLinkIdAtom);
 
 export const useTemplateTags = () => useAtomValue(templateTagsAtom);

+ 1 - 6
apps/app/src/states/page/hydrate.ts

@@ -11,7 +11,6 @@ import {
   currentPageDataAtom,
   currentPageIdAtom,
   isForbiddenAtom,
-  latestRevisionAtom,
   pageNotFoundAtom,
   redirectFromAtom,
   remoteRevisionBodyAtom,
@@ -31,7 +30,7 @@ import {
  * Data sources:
  * - page._id, page.revision -> Auto-extracted from IPagePopulatedToShowRevision
  * - remoteRevisionId, remoteRevisionBody -> Auto-extracted from page.revision
- * - templateTags, templateBody, isLatestRevision -> Explicitly provided via options
+ * - templateTags, templateBody -> Explicitly provided via options
  *
  * @example
  * // Basic usage
@@ -39,7 +38,6 @@ import {
  *
  * // With template data and custom flags
  * useHydratePageAtoms(pageWithMeta?.data, {
- *   isLatestRevision: false,
  *   templateTags: ['tag1', 'tag2'],
  *   templateBody: 'Template content'
  * });
@@ -49,7 +47,6 @@ export const useHydratePageAtoms = (
   pageMeta: IPageNotFoundInfo | IPageInfo | undefined,
   options?: {
     // always overwrited
-    isLatestRevision?: boolean;
     shareLinkId?: string;
     redirectFrom?: string;
     templateTags?: string[];
@@ -79,8 +76,6 @@ export const useHydratePageAtoms = (
   // always overwrited
   useHydrateAtoms(
     [
-      [latestRevisionAtom, options?.isLatestRevision ?? true],
-
       // ShareLink page state
       [shareLinkIdAtom, options?.shareLinkId],
 

+ 0 - 1
apps/app/src/states/page/internal-atoms.ts

@@ -13,7 +13,6 @@ export const currentPageDataAtom = atom<IPagePopulatedToShowRevision>();
 export const pageNotFoundAtom = atom(false);
 export const isIdenticalPathAtom = atom<boolean>(false);
 export const isForbiddenAtom = atom<boolean>(false);
-export const latestRevisionAtom = atom(true);
 
 // ShareLink page state atoms (internal)
 export const shareLinkIdAtom = atom<string>();

+ 37 - 1
apps/app/src/stores/page.tsx

@@ -27,7 +27,7 @@ import type {
   IResCurrentGrantData,
 } from '~/interfaces/page-grant';
 import { useIsGuestUser, useIsReadOnlyUser } from '~/states/context';
-import { usePageNotFound } from '~/states/page';
+import { useCurrentPageData, usePageNotFound } from '~/states/page';
 import { useShareLinkId } from '~/states/page/hooks';
 
 import type { IPageTagsInfo } from '../interfaces/tag';
@@ -154,6 +154,42 @@ export const useSWRMUTxPageInfo = (
   );
 };
 
+/**
+ * Hook to check if the current page is displaying the latest revision
+ * Returns SWRResponse with boolean value:
+ * - data: undefined - not yet determined (no currentPage data)
+ * - data: true - viewing the latest revision (or latestRevisionId not available)
+ * - data: false - viewing an old revision
+ */
+export const useIsLatestRevision = (): SWRResponse<boolean, Error> => {
+  const currentPage = useCurrentPageData();
+  const pageId = currentPage?._id;
+  const shareLinkId = useShareLinkId();
+  const { data: pageInfo } = useSWRxPageInfo(pageId, shareLinkId);
+
+  // Extract latestRevisionId if available (only exists in IPageInfoForEntity)
+  const latestRevisionId = pageInfo && 'latestRevisionId' in pageInfo ? pageInfo.latestRevisionId : undefined;
+
+  const key = useMemo(() => {
+    // Cannot determine without currentPage
+    if (currentPage?.revision?._id == null) {
+      return null;
+    }
+    return ['isLatestRevision', currentPage.revision._id, latestRevisionId ?? null];
+  }, [currentPage?.revision?._id, latestRevisionId]);
+
+  return useSWRImmutable(
+    key,
+    ([, currentRevisionId, latestRevisionId]) => {
+      // If latestRevisionId is not available, assume it's the latest
+      if (latestRevisionId == null) {
+        return true;
+      }
+      return latestRevisionId === currentRevisionId;
+    },
+  );
+};
+
 export const useSWRxPageRevision = (
   pageId: string,
   revisionId: Ref<IRevision>,