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

+ 2 - 2
apps/app/src/client/components/PageStatusAlert.tsx

@@ -2,10 +2,10 @@ import React, { useCallback, type JSX } from 'react';
 
 import { useTranslation } from 'next-i18next';
 
+import { useCurrentPageData } from '~/states/page';
 import { useIsGuestUser, useIsReadOnlyUser } from '~/stores-universal/context';
 import { useEditorMode } from '~/stores-universal/ui';
 import { usePageStatusAlert } from '~/stores/alert';
-import { useSWRxCurrentPage } from '~/stores/page';
 import { useRemoteRevisionId, useRemoteRevisionLastUpdateUser } from '~/stores/remote-latest-page';
 
 import { Username } from '../../components/User/Username';
@@ -21,7 +21,7 @@ export const PageStatusAlert = (): JSX.Element => {
   const { data: pageStatusAlertData } = usePageStatusAlert();
   const { data: remoteRevisionId } = useRemoteRevisionId();
   const { data: remoteRevisionLastUpdateUser } = useRemoteRevisionLastUpdateUser();
-  const { data: pageData } = useSWRxCurrentPage();
+  const [pageData] = useCurrentPageData();
 
   const onClickRefreshPage = useCallback(() => {
     pageStatusAlertData?.onRefleshPage?.();

+ 3 - 3
apps/app/src/client/components/PageTimeline.tsx

@@ -4,7 +4,7 @@ import type { IPageHasId } from '@growi/core';
 import { useTranslation } from 'next-i18next';
 import Link from 'next/link';
 
-import { useCurrentPagePath } from '~/stores/page';
+import { useCurrentPagePath } from '~/states/page';
 import { useSWRINFxPageTimeline } from '~/stores/page-timeline';
 import { useTimelineOptions } from '~/stores/renderer';
 
@@ -46,9 +46,9 @@ export const PageTimeline = (): JSX.Element => {
 
   const PER_PAGE = 3;
   const { t } = useTranslation();
-  const { data: currentPagePath } = useCurrentPagePath();
+  const [currentPagePath] = useCurrentPagePath();
 
-  const swrInfinitexPageTimeline = useSWRINFxPageTimeline(currentPagePath, PER_PAGE);
+  const swrInfinitexPageTimeline = useSWRINFxPageTimeline(currentPagePath ?? undefined, PER_PAGE);
   const { data } = swrInfinitexPageTimeline;
 
   const isEmpty = data?.[0]?.pages.length === 0;

+ 2 - 2
apps/app/src/client/components/TemplateModal/use-formatter.tsx

@@ -3,7 +3,7 @@ import path from 'path';
 import { format as dateFnsFormat } from 'date-fns/format';
 import mustache from 'mustache';
 
-import { useCurrentPagePath } from '~/stores/page';
+import { useCurrentPagePath } from '~/states/page';
 import loggerFactory from '~/utils/logger';
 
 const logger = loggerFactory('growi:components:TemplateModal:use-formatter');
@@ -15,7 +15,7 @@ type FormatterData = {
 }
 
 export const useFormatter = (): FormatterData => {
-  const { data: currentPagePath } = useCurrentPagePath();
+  const [currentPagePath] = useCurrentPagePath();
 
   const format: FormatMethod = (markdown) => {
     if (markdown == null) {

+ 2 - 2
apps/app/src/components/Common/PagePathNav/PagePathNavLayout.tsx

@@ -2,7 +2,7 @@ import type { ReactNode, JSX } from 'react';
 
 import dynamic from 'next/dynamic';
 
-import { useIsNotFound } from '~/stores/page';
+import { usePageNotFound } from '~/states/page';
 
 import styles from './PagePathNav.module.scss';
 
@@ -38,7 +38,7 @@ export const PagePathNavLayout = (props: Props): JSX.Element => {
     maxWidth,
   } = props;
 
-  const { data: isNotFound } = useIsNotFound();
+  const [isNotFound] = usePageNotFound();
 
   const copyDropdownId = `copydropdown-in-pagepathnavlayout-${pageId}`;
 

+ 2 - 2
apps/app/src/components/PageView/PageAlerts/PageAlerts.tsx

@@ -2,7 +2,7 @@ import type { JSX } from 'react';
 
 import dynamic from 'next/dynamic';
 
-import { useIsNotFound } from '~/stores/page';
+import { usePageNotFound } from '~/states/page';
 
 import { OldRevisionAlert } from './OldRevisionAlert';
 import { PageGrantAlert } from './PageGrantAlert';
@@ -17,7 +17,7 @@ const TrashPageAlert = dynamic(() => import('./TrashPageAlert').then(mod => mod.
 
 export const PageAlerts = (): JSX.Element => {
 
-  const { data: isNotFound } = useIsNotFound();
+  const [isNotFound] = usePageNotFound();
 
   return (
     <div className="row d-edit-none">

+ 2 - 2
apps/app/src/components/PageView/PageAlerts/PageGrantAlert.tsx

@@ -3,12 +3,12 @@ import React, { type JSX } from 'react';
 import { isPopulated } from '@growi/core';
 import { useTranslation } from 'react-i18next';
 
-import { useSWRxCurrentPage } from '~/stores/page';
+import { useCurrentPageData } from '~/states/page';
 
 
 export const PageGrantAlert = (): JSX.Element => {
   const { t } = useTranslation();
-  const { data: pageData } = useSWRxCurrentPage();
+  const [pageData] = useCurrentPageData();
 
   if (pageData == null || pageData.grant == null || pageData.grant === 1) {
     return <></>;

+ 2 - 2
apps/app/src/components/PageView/PageAlerts/PageRedirectedAlert.tsx

@@ -2,12 +2,12 @@ import React, { useState, useCallback, type JSX } from 'react';
 
 import { useTranslation } from 'next-i18next';
 
-import { useCurrentPagePath } from '~/stores/page';
+import { useCurrentPagePath } from '~/states/page';
 import { useRedirectFrom } from '~/stores/page-redirect';
 
 export const PageRedirectedAlert = React.memo((): JSX.Element => {
   const { t } = useTranslation();
-  const { data: currentPagePath } = useCurrentPagePath();
+  const [currentPagePath] = useCurrentPagePath();
   const { data: redirectFrom } = useRedirectFrom();
 
   const [isUnlinked, setIsUnlinked] = useState(false);

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

@@ -2,13 +2,13 @@ import React, { useCallback, type JSX } from 'react';
 
 import { useTranslation } from 'react-i18next';
 
-import { useSWRMUTxCurrentPage, useSWRxCurrentPage } from '~/stores/page';
+import { useCurrentPageData, usePageFetcher } from '~/states/page';
 
 
 export const WipPageAlert = (): JSX.Element => {
   const { t } = useTranslation();
-  const { data: currentPage } = useSWRxCurrentPage();
-  const { trigger: mutateCurrentPage } = useSWRMUTxCurrentPage();
+  const [currentPage] = useCurrentPageData();
+  const { fetchAndUpdatePage } = usePageFetcher();
 
   const clickPagePublishButton = useCallback(async() => {
     const pageId = currentPage?._id;
@@ -21,7 +21,7 @@ export const WipPageAlert = (): JSX.Element => {
       const publish = (await import('~/client/services/page-operation')).publish;
       await publish(pageId);
 
-      await mutateCurrentPage();
+      await fetchAndUpdatePage();
 
       const mutatePageTree = (await import('~/stores/page-listing')).mutatePageTree;
       await mutatePageTree();
@@ -36,7 +36,7 @@ export const WipPageAlert = (): JSX.Element => {
       const toastError = (await import('~/client/util/toastr')).toastError;
       toastError(t('wip_page.fail_publish_page'));
     }
-  }, [currentPage?._id, mutateCurrentPage, t]);
+  }, [currentPage?._id, fetchAndUpdatePage, t]);
 
 
   if (!currentPage?.wip) {

+ 4 - 7
apps/app/src/components/PageView/PageView.tsx

@@ -2,7 +2,6 @@ import React, {
   useEffect, useMemo, useRef, useState, type JSX,
 } from 'react';
 
-import type { IPagePopulatedToShowRevision } from '@growi/core';
 import { isUsersHomepage } from '@growi/core/dist/utils/page-path-utils';
 import { useSlidesByFrontmatter } from '@growi/presentation/dist/services';
 import dynamic from 'next/dynamic';
@@ -11,10 +10,10 @@ import { PagePathNavTitle } from '~/components/Common/PagePathNavTitle';
 import type { RendererConfig } from '~/interfaces/services/renderer';
 import { useShouldExpandContent } from '~/services/layout/use-should-expand-content';
 import { generateSSRViewOptions } from '~/services/renderer/renderer';
+import { useCurrentPageData, usePageNotFound } from '~/states/page';
 import {
   useIsForbidden, useIsIdenticalPath, useIsNotCreatable,
 } from '~/stores-universal/context';
-import { useSWRxCurrentPage, useIsNotFound } from '~/stores/page';
 import { useViewOptions } from '~/stores/renderer';
 
 import { UserInfo } from '../User/UserInfo';
@@ -40,7 +39,6 @@ const SlideRenderer = dynamic(() => import('~/client/components/Page/SlideRender
 type Props = {
   pagePath: string,
   rendererConfig: RendererConfig,
-  initialPage?: IPagePopulatedToShowRevision,
   className?: string,
 }
 
@@ -51,18 +49,17 @@ export const PageView = (props: Props): JSX.Element => {
   const [isCommentsLoaded, setCommentsLoaded] = useState(false);
 
   const {
-    pagePath, initialPage, rendererConfig, className,
+    pagePath, rendererConfig, className,
   } = props;
 
   const { data: isIdenticalPathPage } = useIsIdenticalPath();
   const { data: isForbidden } = useIsForbidden();
   const { data: isNotCreatable } = useIsNotCreatable();
-  const { data: isNotFoundMeta } = useIsNotFound();
+  const [isNotFoundMeta] = usePageNotFound();
 
-  const { data: pageBySWR } = useSWRxCurrentPage();
+  const [page] = useCurrentPageData();
   const { data: viewOptions } = useViewOptions();
 
-  const page = pageBySWR ?? initialPage;
   const isNotFound = isNotFoundMeta || page == null;
   const isUsersHomepagePath = isUsersHomepage(pagePath);
 

+ 2 - 2
apps/app/src/components/ShareLinkPageView/ShareLinkPageView.tsx

@@ -9,7 +9,7 @@ import type { RendererConfig } from '~/interfaces/services/renderer';
 import type { IShareLinkHasId } from '~/interfaces/share-link';
 import { useShouldExpandContent } from '~/services/layout/use-should-expand-content';
 import { generateSSRViewOptions } from '~/services/renderer/renderer';
-import { useIsNotFound } from '~/stores/page';
+import { usePageNotFound } from '~/states/page';
 import { useViewOptions } from '~/stores/renderer';
 import loggerFactory from '~/utils/logger';
 
@@ -43,7 +43,7 @@ export const ShareLinkPageView = (props: Props): JSX.Element => {
     isExpired, disableLinkSharing,
   } = props;
 
-  const { data: isNotFoundMeta } = useIsNotFound();
+  const [isNotFoundMeta] = usePageNotFound();
 
   const { data: viewOptions } = useViewOptions();
 

+ 14 - 46
apps/app/src/pages/[[...path]].page.tsx

@@ -32,7 +32,11 @@ import type { CurrentPageYjsData } from '~/interfaces/yjs';
 import type { PageModel, PageDocument } from '~/server/models/page';
 import type { PageRedirectModel } from '~/server/models/page-redirect';
 import { useEditorModeClassName } from '~/services/layout/use-editor-mode-class-name';
+import { useHydratePageAtoms } from '~/states/hydrate/page';
 import { useHydrateSidebarAtoms } from '~/states/hydrate/sidebar';
+import {
+  useCurrentPageData, usePageFetcher, useCurrentPageId, useCurrentPagePath,
+} from '~/states/page';
 import {
   useCurrentUser,
   useIsForbidden, useIsSharedUser,
@@ -51,12 +55,7 @@ import {
   useIsAiEnabled, useLimitLearnablePageCountPerAssistant, useIsUsersHomepageDeletionEnabled,
 } from '~/stores-universal/context';
 import { useEditingMarkdown } from '~/stores/editor';
-import {
-  useSWRxCurrentPage, useSWRMUTxCurrentPage, useCurrentPageId,
-  useIsNotFound, useIsLatestRevision, useTemplateTagData, useTemplateBodyData,
-} from '~/stores/page';
 import { useRedirectFrom } from '~/stores/page-redirect';
-import { useRemoteRevisionId } from '~/stores/remote-latest-page';
 import { useSetupGlobalSocket, useSetupGlobalSocketForPage } from '~/stores/websocket';
 import { useCurrentPageYjsData, useSWRMUTxCurrentPageYjsData } from '~/stores/yjs';
 import loggerFactory from '~/utils/logger';
@@ -136,7 +135,7 @@ type GrowiContextualSubNavigationProps = {
 
 const GrowiContextualSubNavigation = (props: GrowiContextualSubNavigationProps): JSX.Element => {
   const { isLinkSharingDisabled } = props;
-  const { data: currentPage } = useSWRxCurrentPage();
+  const [currentPage] = useCurrentPageData();
   return (
     <GrowiContextualSubNavigationSubstance currentPage={currentPage} isLinkSharingDisabled={isLinkSharingDisabled} />
   );
@@ -270,22 +269,15 @@ const Page: NextPageWithLayout<Props> = (props: Props) => {
 
   useCurrentPathname(props.currentPathname);
 
-  const { data: currentPage } = useSWRxCurrentPage(pageWithMeta?.data ?? null); // store initial data
+  // Initialize Jotai atoms with initial data
+  useHydratePageAtoms(pageWithMeta?.data);
 
-  const { trigger: mutateCurrentPage } = useSWRMUTxCurrentPage();
+  const { fetchAndUpdatePage } = usePageFetcher();
   const { trigger: mutateCurrentPageYjsDataFromApi } = useSWRMUTxCurrentPageYjsData();
 
   const { mutate: mutateEditingMarkdown } = useEditingMarkdown();
-  const { data: currentPageId, mutate: mutateCurrentPageId } = useCurrentPageId();
-
-  const { mutate: mutateIsNotFound } = useIsNotFound();
-
-  const { mutate: mutateIsLatestRevision } = useIsLatestRevision();
-
-  const { mutate: mutateRemoteRevisionId } = useRemoteRevisionId();
-
-  const { mutate: mutateTemplateTagData } = useTemplateTagData();
-  const { mutate: mutateTemplateBodyData } = useTemplateBodyData();
+  const [currentPageId] = useCurrentPageId();
+  const [currentPagePath] = useCurrentPagePath();
 
   const { mutate: mutateCurrentPageYjsData } = useCurrentPageYjsData();
 
@@ -300,7 +292,7 @@ const Page: NextPageWithLayout<Props> = (props: Props) => {
 
     if (currentPageId != null && revisionId != null && !props.isNotFound) {
       const mutatePageData = async() => {
-        const pageData = await mutateCurrentPage();
+        const pageData = await fetchAndUpdatePage();
         mutateEditingMarkdown(pageData?.revision?.body);
       };
 
@@ -309,7 +301,7 @@ const Page: NextPageWithLayout<Props> = (props: Props) => {
       mutatePageData();
     }
   }, [
-    revisionId, currentPageId, mutateCurrentPage,
+    revisionId, currentPageId, fetchAndUpdatePage,
     mutateCurrentPageYjsDataFromApi, mutateEditingMarkdown, props.isNotFound, props.skipSSR,
   ]);
 
@@ -337,37 +329,14 @@ const Page: NextPageWithLayout<Props> = (props: Props) => {
     }
   }, [mutateEditingMarkdown, revisionBody, props.currentPathname]);
 
-  useEffect(() => {
-    mutateRemoteRevisionId(revisionId);
-  }, [mutateRemoteRevisionId, revisionId]);
-
-  useEffect(() => {
-    mutateCurrentPageId(pageId ?? null);
-  }, [mutateCurrentPageId, pageId]);
-
-  useEffect(() => {
-    mutateIsNotFound(props.isNotFound);
-  }, [mutateIsNotFound, props.isNotFound]);
-
-  useEffect(() => {
-    mutateIsLatestRevision(props.isLatestRevision);
-  }, [mutateIsLatestRevision, props.isLatestRevision]);
-
-  useEffect(() => {
-    mutateTemplateTagData(props.templateTagData);
-  }, [props.templateTagData, mutateTemplateTagData]);
-
-  useEffect(() => {
-    mutateTemplateBodyData(props.templateBodyData);
-  }, [props.templateBodyData, mutateTemplateBodyData]);
-
   useEffect(() => {
     mutateCurrentPageYjsData(props.yjsData);
   }, [mutateCurrentPageYjsData, props.yjsData]);
 
   // If the data on the page changes without router.push, pageWithMeta remains old because getServerSideProps() is not executed
   // So preferentially take page data from useSWRxCurrentPage
-  const pagePath = currentPage?.path ?? pageWithMeta?.data.path ?? props.currentPathname;
+  // const pagePath = currentPage?.path ?? pageWithMeta?.data.path ?? props.currentPathname;
+  const pagePath = currentPagePath ?? props.currentPathname;
 
   const title = generateCustomTitleForPage(props, pagePath);
 
@@ -383,7 +352,6 @@ const Page: NextPageWithLayout<Props> = (props: Props) => {
         <PageView
           className="d-edit-none"
           pagePath={pagePath}
-          initialPage={pageWithMeta?.data}
           rendererConfig={props.rendererConfig}
         />
 

+ 4 - 175
apps/app/src/stores/page.tsx

@@ -7,10 +7,8 @@ import type {
   IPageInfo, IPageInfoForOperation,
   IRevision, IRevisionHasId,
 } from '@growi/core';
-import { useSWRStatic } from '@growi/core/dist/swr';
-import { isClient, pagePathUtils } from '@growi/core/dist/utils';
 import useSWR, {
-  mutate, useSWRConfig, type SWRResponse, type SWRConfiguration,
+  mutate, type SWRResponse, type SWRConfiguration,
 } from 'swr';
 import useSWRImmutable from 'swr/immutable';
 import useSWRInfinite, { type SWRInfiniteResponse } from 'swr/infinite';
@@ -20,98 +18,15 @@ import { apiGet } from '~/client/util/apiv1-client';
 import { apiv3Get } from '~/client/util/apiv3-client';
 import type { IPagePathWithDescendantCount } from '~/interfaces/page';
 import type { IRecordApplicableGrant, IResCurrentGrantData } from '~/interfaces/page-grant';
+import { usePageNotFound } from '~/states/page';
 import {
-  useCurrentPathname, useShareLinkId, useIsGuestUser, useIsReadOnlyUser,
+  useShareLinkId, useIsGuestUser, useIsReadOnlyUser,
 } from '~/stores-universal/context';
 import type { AxiosResponse } from '~/utils/axios';
 
 import type { IPageTagsInfo } from '../interfaces/tag';
 
 
-import { useRemoteRevisionId } from './remote-latest-page';
-
-
-const { isPermalink: _isPermalink } = pagePathUtils;
-
-
-export const useCurrentPageId = (initialData?: Nullable<string>): SWRResponse<Nullable<string>, Error> => {
-  return useSWRStatic<Nullable<string>, Error>('currentPageId', initialData);
-};
-
-export const useIsLatestRevision = (initialData?: boolean): SWRResponse<boolean, any> => {
-  return useSWRStatic('isLatestRevision', initialData);
-};
-
-export const useIsNotFound = (initialData?: boolean): SWRResponse<boolean, Error> => {
-  return useSWRStatic<boolean, Error>('isNotFound', initialData, { fallbackData: false });
-};
-
-export const useTemplateTagData = (initialData?: string[]): SWRResponse<string[], Error> => {
-  return useSWRStatic<string[], Error>('templateTagData', initialData);
-};
-
-export const useTemplateBodyData = (initialData?: string): SWRResponse<string, Error> => {
-  return useSWRStatic<string, Error>('templateBodyData', initialData);
-};
-
-/** "useSWRxCurrentPage" is intended for initial data retrieval only. Use "useSWRMUTxCurrentPage" for revalidation */
-export const useSWRxCurrentPage = (initialData?: IPagePopulatedToShowRevision|null): SWRResponse<IPagePopulatedToShowRevision|null> => {
-  const key = 'currentPage';
-
-  const { data: isLatestRevision } = useIsLatestRevision();
-
-  const { cache } = useSWRConfig();
-
-  // Problem 1: https://github.com/weseek/growi/pull/7772/files#diff-4c1708c4f959974166c15435c6b35950ba01bbf35e7e4b8e99efeb125a8000a7
-  // Problem 2: https://redmine.weseek.co.jp/issues/141027
-  // Problem 3: https://redmine.weseek.co.jp/issues/153618
-  // Problem 4: https://redmine.weseek.co.jp/issues/153759
-  const shouldMutate = (() => {
-    if (initialData === undefined) {
-      return false;
-    }
-
-    // reset when null
-    if (initialData == null) {
-      return true;
-    }
-
-    const cachedData = cache.get(key)?.data as IPagePopulatedToShowRevision|null;
-    if (initialData._id !== cachedData?._id) {
-      return true;
-    }
-
-    // mutate when the empty page has updated
-    if (cachedData?.revision == null && initialData.revision != null) {
-      return true;
-    }
-
-    // mutate when opening a previous revision.
-    if (!isLatestRevision
-        && cachedData.revision?._id != null && initialData.revision?._id != null
-        && cachedData.revision._id !== initialData.revision._id
-    ) {
-      return true;
-    }
-
-    return false;
-  })();
-
-  useEffect(() => {
-    if (shouldMutate) {
-      mutate(key, initialData, {
-        optimisticData: initialData,
-        populateCache: true,
-        revalidate: false,
-      });
-    }
-  }, [initialData, key, shouldMutate]);
-
-  return useSWR(key, null, {
-    keepPreviousData: true,
-  });
-};
-
 const getPageApiErrorHandler = (errs: AxiosResponse[]) => {
   if (!Array.isArray(errs)) { throw Error('error is not array') }
 
@@ -123,39 +38,6 @@ const getPageApiErrorHandler = (errs: AxiosResponse[]) => {
   throw Error('failed to get page');
 };
 
-export const useSWRMUTxCurrentPage = (): SWRMutationResponse<IPagePopulatedToShowRevision|null> => {
-  const key = 'currentPage';
-
-  const { data: currentPageId } = useCurrentPageId();
-  const { data: shareLinkId } = useShareLinkId();
-
-  // Get URL parameter for specific revisionId
-  let revisionId: string|undefined;
-  if (isClient()) {
-    const urlParams = new URLSearchParams(window.location.search);
-    const requestRevisionId = urlParams.get('revisionId');
-    revisionId = requestRevisionId != null ? requestRevisionId : undefined;
-  }
-
-  return useSWRMutation(
-    key,
-    () => apiv3Get<{ page: IPagePopulatedToShowRevision }>('/page', { pageId: currentPageId, shareLinkId, revisionId })
-      .then((result) => {
-        const newData = result.data.page;
-
-        // for the issue https://redmine.weseek.co.jp/issues/156150
-        mutate('currentPage', newData, false);
-
-        return newData;
-      })
-      .catch(getPageApiErrorHandler),
-    {
-      populateCache: true,
-      revalidate: false,
-    },
-  );
-};
-
 export const useSWRxPageByPath = (path?: string, config?: SWRConfiguration): SWRResponse<IPagePopulatedToShowRevision|null, Error> => {
   return useSWR(
     path != null ? ['/page', path] : null,
@@ -294,7 +176,7 @@ export const useSWRxCurrentGrantData = (
 
   const { data: isGuestUser } = useIsGuestUser();
   const { data: isReadOnlyUser } = useIsReadOnlyUser();
-  const { data: isNotFound } = useIsNotFound();
+  const [isNotFound] = usePageNotFound();
 
   const key = !isGuestUser && !isReadOnlyUser && !isNotFound && pageId != null
     ? ['/page/grant-data', pageId]
@@ -322,59 +204,6 @@ export const useSWRxApplicableGrant = (
  *                     @deprecated Use enhanced versions from ~/states/page instead
  *********************************************************** */
 
-/**
- * @deprecated Use useCurrentPagePathEnhanced from ~/states/page instead
- */
-export const useCurrentPagePath = (): SWRResponse<string | undefined, Error> => {
-  const { data: currentPage } = useSWRxCurrentPage();
-  const { data: currentPathname } = useCurrentPathname();
-
-  return useSWRImmutable(
-    ['currentPagePath', currentPage?.path, currentPathname],
-    ([, , pathname]) => {
-      if (currentPage?.path != null) {
-        return currentPage.path;
-      }
-      if (pathname != null && !_isPermalink(pathname)) {
-        return pathname;
-      }
-      return undefined;
-    },
-    // TODO: set fallbackData
-    // { fallbackData:  }
-  );
-};
-
-/**
- * @deprecated Use useIsTrashPageEnhanced from ~/states/page instead
- */
-export const useIsTrashPage = (): SWRResponse<boolean, Error> => {
-  const { data: pagePath } = useCurrentPagePath();
-
-  return useSWRImmutable(
-    pagePath == null ? null : ['isTrashPage', pagePath],
-    ([, pagePath]) => pagePathUtils.isTrashPage(pagePath),
-    // TODO: set fallbackData
-    // { fallbackData:  }
-  );
-};
-
-/**
- * @deprecated Use useIsRevisionOutdatedEnhanced from ~/states/page instead
- */
-export const useIsRevisionOutdated = (): SWRResponse<boolean, Error> => {
-  const { data: currentPage } = useSWRxCurrentPage();
-  const { data: remoteRevisionId } = useRemoteRevisionId();
-
-  const currentRevisionId = currentPage?.revision?._id;
-
-  return useSWRImmutable(
-    currentRevisionId != null && remoteRevisionId != null ? ['useIsRevisionOutdated', currentRevisionId, remoteRevisionId] : null,
-    ([, remoteRevisionId, currentRevisionId]) => { return remoteRevisionId !== currentRevisionId },
-  );
-};
-
-
 export const useSWRxPagePathsWithDescendantCount = (
     paths?: string[], userGroups?: string[], isIncludeEmpty?: boolean, includeAnyoneWithTheLink?: boolean,
 ): SWRResponse<IPagePathWithDescendantCount[], Error> => {

+ 2 - 17
apps/app/src/stores/remote-latest-page.ts

@@ -4,15 +4,6 @@ import type { IUserHasId } from '@growi/core';
 import { useSWRStatic } from '@growi/core/dist/swr';
 import type { SWRResponse } from 'swr';
 
-
-export const useRemoteRevisionId = (initialData?: string): SWRResponse<string, Error> => {
-  return useSWRStatic<string, Error>('remoteRevisionId', initialData);
-};
-
-export const useRemoteRevisionBody = (initialData?: string): SWRResponse<string, Error> => {
-  return useSWRStatic<string, Error>('remoteRevisionBody', initialData);
-};
-
 export const useRemoteRevisionLastUpdateUser = (initialData?: IUserHasId): SWRResponse<IUserHasId, Error> => {
   return useSWRStatic<IUserHasId, Error>('remoteRevisionLastUpdateUser', initialData);
 };
@@ -22,8 +13,6 @@ export const useRemoteRevisionLastUpdatedAt = (initialData?: Date): SWRResponse<
 };
 
 export type RemoteRevisionData = {
-  remoteRevisionId: string,
-  remoteRevisionBody: string,
   remoteRevisionLastUpdateUser?: IUserHasId,
   remoteRevisionLastUpdatedAt: Date,
 }
@@ -31,20 +20,16 @@ export type RemoteRevisionData = {
 
 // set remote data all at once
 export const useSetRemoteLatestPageData = (): { setRemoteLatestPageData: (pageData: RemoteRevisionData) => void } => {
-  const { mutate: mutateRemoteRevisionId } = useRemoteRevisionId();
-  const { mutate: mutateRemoteRevisionBody } = useRemoteRevisionBody();
   const { mutate: mutateRemoteRevisionLastUpdateUser } = useRemoteRevisionLastUpdateUser();
   const { mutate: mutateRemoteRevisionLastUpdatedAt } = useRemoteRevisionLastUpdatedAt();
 
   const setRemoteLatestPageData = useCallback((remoteRevisionData: RemoteRevisionData) => {
     const {
-      remoteRevisionId, remoteRevisionBody, remoteRevisionLastUpdateUser, remoteRevisionLastUpdatedAt,
+      remoteRevisionLastUpdateUser, remoteRevisionLastUpdatedAt,
     } = remoteRevisionData;
-    mutateRemoteRevisionId(remoteRevisionId);
-    mutateRemoteRevisionBody(remoteRevisionBody);
     mutateRemoteRevisionLastUpdateUser(remoteRevisionLastUpdateUser);
     mutateRemoteRevisionLastUpdatedAt(remoteRevisionLastUpdatedAt);
-  }, [mutateRemoteRevisionBody, mutateRemoteRevisionId, mutateRemoteRevisionLastUpdateUser, mutateRemoteRevisionLastUpdatedAt]);
+  }, [mutateRemoteRevisionLastUpdateUser, mutateRemoteRevisionLastUpdatedAt]);
 
   return useMemo(() => {
     return {

+ 2 - 2
apps/app/src/stores/renderer.tsx

@@ -6,11 +6,11 @@ import useSWR, { type SWRConfiguration, type SWRResponse } from 'swr';
 import { getGrowiFacade } from '~/features/growi-plugin/client/utils/growi-facade-utils';
 import type { RendererOptions } from '~/interfaces/renderer-options';
 import type { RendererConfigExt } from '~/interfaces/services/renderer';
+import { useCurrentPagePath } from '~/states/page';
 import { useRendererConfig } from '~/stores-universal/context';
 import { useNextThemes } from '~/stores-universal/use-next-themes';
 import loggerFactory from '~/utils/logger';
 
-import { useCurrentPagePath } from './page';
 import { useCurrentPageTocNode } from './ui';
 
 const logger = loggerFactory('growi:cli:services:renderer');
@@ -59,7 +59,7 @@ export const useViewOptions = (): SWRResponse<RendererOptions, Error> => {
 };
 
 export const useTocOptions = (): SWRResponse<RendererOptions, Error> => {
-  const { data: currentPagePath } = useCurrentPagePath();
+  const [currentPagePath] = useCurrentPagePath();
   const rendererConfig = useRendererConfigExt();
   const { data: tocNode } = useCurrentPageTocNode();
 

+ 17 - 17
apps/app/src/stores/ui.tsx

@@ -17,14 +17,14 @@ import useSWRImmutable from 'swr/immutable';
 
 import type { IPageSelectedGrant } from '~/interfaces/page';
 import type { UpdateDescCountData } from '~/interfaces/websocket';
+import {
+  usePageNotFound, useCurrentPagePath, useIsTrashPage, useCurrentPageId,
+} from '~/states/page';
 import {
   useIsEditable, useIsReadOnlyUser,
   useIsSharedUser, useIsIdenticalPath, useCurrentUser, useShareLinkId,
 } from '~/stores-universal/context';
 import { EditorMode, useEditorMode } from '~/stores-universal/ui';
-import {
-  useIsNotFound, useCurrentPagePath, useIsTrashPage, useCurrentPageId,
-} from '~/stores/page';
 import loggerFactory from '~/utils/logger';
 
 import { useStaticSWR } from './use-static-swr';
@@ -39,7 +39,7 @@ const logger = loggerFactory('growi:stores:ui');
  *********************************************************** */
 
 export const useCurrentPageTocNode = (): SWRResponse<HtmlElementNode, any> => {
-  const { data: currentPagePath } = useCurrentPagePath();
+  const [currentPagePath] = useCurrentPagePath();
 
   return useStaticSWR(['currentPageTocNode', currentPagePath]);
 };
@@ -235,9 +235,9 @@ export const useIsAbleToShowTrashPageManagementButtons = (): SWRResponse<boolean
   const { data: _currentUser } = useCurrentUser();
   const isCurrentUserExist = _currentUser != null;
 
-  const { data: _currentPageId } = useCurrentPageId();
-  const { data: _isNotFound } = useIsNotFound();
-  const { data: _isTrashPage } = useIsTrashPage();
+  const [_currentPageId] = useCurrentPageId();
+  const [_isNotFound] = usePageNotFound();
+  const [_isTrashPage] = useIsTrashPage();
   const { data: _isReadOnlyUser } = useIsReadOnlyUser();
   const isPageExist = _currentPageId != null && _isNotFound === false;
   const isTrashPage = isPageExist && _isTrashPage === true;
@@ -253,10 +253,10 @@ export const useIsAbleToShowTrashPageManagementButtons = (): SWRResponse<boolean
 
 export const useIsAbleToShowPageManagement = (): SWRResponse<boolean, Error> => {
   const key = 'isAbleToShowPageManagement';
-  const { data: currentPageId } = useCurrentPageId();
-  const { data: _isTrashPage } = useIsTrashPage();
+  const [currentPageId] = useCurrentPageId();
+  const [isNotFound] = usePageNotFound();
+  const [_isTrashPage] = useIsTrashPage();
   const { data: _isSharedUser } = useIsSharedUser();
-  const { data: isNotFound } = useIsNotFound();
 
   const pageId = currentPageId;
   const includesUndefined = [pageId, _isTrashPage, _isSharedUser, isNotFound].some(v => v === undefined);
@@ -273,10 +273,10 @@ export const useIsAbleToShowPageManagement = (): SWRResponse<boolean, Error> =>
 
 export const useIsAbleToShowTagLabel = (): SWRResponse<boolean, Error> => {
   const key = 'isAbleToShowTagLabel';
-  const { data: pageId } = useCurrentPageId();
-  const { data: currentPagePath } = useCurrentPagePath();
+  const [pageId] = useCurrentPageId();
+  const [isNotFound] = usePageNotFound();
+  const [currentPagePath] = useCurrentPagePath();
   const { data: isIdenticalPath } = useIsIdenticalPath();
-  const { data: isNotFound } = useIsNotFound();
   const { data: editorMode } = useEditorMode();
   const { data: shareLinkId } = useShareLinkId();
 
@@ -307,9 +307,9 @@ export const useIsAbleToChangeEditorMode = (): SWRResponse<boolean, Error> => {
 
 export const useIsAbleToShowPageAuthors = (): SWRResponse<boolean, Error> => {
   const key = 'isAbleToShowPageAuthors';
-  const { data: pageId } = useCurrentPageId();
-  const { data: pagePath } = useCurrentPagePath();
-  const { data: isNotFound } = useIsNotFound();
+  const [pageId] = useCurrentPageId();
+  const [isNotFound] = usePageNotFound();
+  const [pagePath] = useCurrentPagePath();
 
   const includesUndefined = [pageId, pagePath, isNotFound].some(v => v === undefined);
   const isPageExist = (pageId != null) && !isNotFound;
@@ -324,7 +324,7 @@ export const useIsAbleToShowPageAuthors = (): SWRResponse<boolean, Error> => {
 export const useIsUntitledPage = (): SWRResponse<boolean> => {
   const key = 'isUntitledPage';
 
-  const { data: pageId } = useCurrentPageId();
+  const [pageId] = useCurrentPageId();
 
   return useSWRStatic(
     pageId == null ? null : [key, pageId],

+ 3 - 3
apps/app/src/stores/yjs.ts

@@ -7,7 +7,7 @@ import useSWRMutation, { type SWRMutationResponse } from 'swr/mutation';
 import { apiv3Get } from '~/client/util/apiv3-client';
 import type { CurrentPageYjsData } from '~/interfaces/yjs';
 
-import { useCurrentPageId } from './page';
+import { useCurrentPageId } from '../states/page';
 
 type CurrentPageYjsDataUtils = {
   updateHasYdocsNewerThanLatestRevision(hasYdocsNewerThanLatestRevision: boolean): void
@@ -15,7 +15,7 @@ type CurrentPageYjsDataUtils = {
 }
 
 export const useCurrentPageYjsData = (): SWRResponse<CurrentPageYjsData, Error> & CurrentPageYjsDataUtils => {
-  const { data: currentPageId } = useCurrentPageId();
+  const [currentPageId] = useCurrentPageId();
 
   const key = currentPageId != null
     ? `/page/${currentPageId}/yjs-data`
@@ -35,7 +35,7 @@ export const useCurrentPageYjsData = (): SWRResponse<CurrentPageYjsData, Error>
 };
 
 export const useSWRMUTxCurrentPageYjsData = (): SWRMutationResponse<CurrentPageYjsData, Error> => {
-  const { data: currentPageId } = useCurrentPageId();
+  const [currentPageId] = useCurrentPageId();
 
   const key = currentPageId != null
     ? `/page/${currentPageId}/yjs-data`