Przeglądaj źródła

Merge pull request #6048 from weseek/support/omit-unstated

support: Omit unstated
Yuki Takei 3 lat temu
rodzic
commit
c75d368d49

+ 0 - 19
packages/app/src/client/services/AppContainer.js

@@ -15,11 +15,6 @@ export default class AppContainer extends Container {
 
 
     this.config = JSON.parse(document.getElementById('growi-context-hydrate').textContent || '{}');
     this.config = JSON.parse(document.getElementById('growi-context-hydrate').textContent || '{}');
 
 
-    const currentUserElem = document.getElementById('growi-current-user');
-    if (currentUserElem != null) {
-      this.currentUser = JSON.parse(currentUserElem.textContent);
-    }
-
     const userLocaleId = this.currentUser?.lang;
     const userLocaleId = this.currentUser?.lang;
     this.i18n = i18nFactory(userLocaleId);
     this.i18n = i18nFactory(userLocaleId);
 
 
@@ -71,20 +66,6 @@ export default class AppContainer extends Container {
     window.crowiPlugin = window.growiPlugin;
     window.crowiPlugin = window.growiPlugin;
   }
   }
 
 
-  get currentUserId() {
-    if (this.currentUser == null) {
-      return null;
-    }
-    return this.currentUser._id;
-  }
-
-  get currentUsername() {
-    if (this.currentUser == null) {
-      return null;
-    }
-    return this.currentUser.username;
-  }
-
   getConfig() {
   getConfig() {
     return this.config;
     return this.config;
   }
   }

+ 0 - 23
packages/app/src/client/services/PageContainer.js

@@ -136,29 +136,6 @@ export default class PageContainer extends Container {
     return 'PageContainer';
     return 'PageContainer';
   }
   }
 
 
-  /**
-   * whether to Empty Trash Page
-   * not displayed when guest user and not on trash page
-   */
-  get isAbleToShowEmptyTrashButton() {
-    const { currentUser } = this.appContainer;
-    const { path, hasChildren } = this.state;
-
-    return (currentUser != null && currentUser.admin && path === '/trash' && hasChildren);
-  }
-
-  /**
-   * whether to display trash management buttons
-   * ex.) undo, delete completly
-   * not displayed when guest user
-   */
-  get isAbleToShowTrashPageManagementButtons() {
-    const { currentUser } = this.appContainer;
-    const { isDeleted } = this.state;
-
-    return (isDeleted && currentUser != null);
-  }
-
   /**
   /**
    * initialize state for markdown data
    * initialize state for markdown data
    */
    */

+ 0 - 79
packages/app/src/components/Admin/Users/RemoveAdminButton.jsx

@@ -1,79 +0,0 @@
-import React, { Fragment } from 'react';
-import PropTypes from 'prop-types';
-import { withTranslation } from 'react-i18next';
-
-import { withUnstatedContainers } from '../../UnstatedUtils';
-import AppContainer from '~/client/services/AppContainer';
-import { toastSuccess, toastError } from '~/client/util/apiNotification';
-import AdminUsersContainer from '~/client/services/AdminUsersContainer';
-
-class RemoveAdminButton extends React.Component {
-
-  constructor(props) {
-    super(props);
-
-    this.onClickRemoveAdminBtn = this.onClickRemoveAdminBtn.bind(this);
-  }
-
-  async onClickRemoveAdminBtn() {
-    const { t } = this.props;
-
-    try {
-      const username = await this.props.adminUsersContainer.removeUserAdmin(this.props.user._id);
-      toastSuccess(t('toaster.remove_user_admin', { username }));
-    }
-    catch (err) {
-      toastError(err);
-    }
-  }
-
-
-  renderRemoveAdminBtn() {
-    const { t } = this.props;
-
-    return (
-      <button className="dropdown-item" type="button" onClick={() => { this.onClickRemoveAdminBtn() }}>
-        <i className="icon-fw icon-user-unfollow"></i> {t('admin:user_management.user_table.remove_admin_access')}
-      </button>
-    );
-  }
-
-  renderRemoveAdminAlert() {
-    const { t } = this.props;
-
-    return (
-      <div className="px-4">
-        <i className="icon-fw icon-user-unfollow mb-2"></i>{t('admin:user_management.user_table.remove_admin_access')}
-        <p className="alert alert-danger">{t('admin:user_management.user_table.cannot_remove')}</p>
-      </div>
-    );
-  }
-
-  render() {
-    const { user } = this.props;
-    const { currentUsername } = this.props.appContainer;
-
-    return (
-      <Fragment>
-        {user.username !== currentUsername ? this.renderRemoveAdminBtn()
-          : this.renderRemoveAdminAlert()}
-      </Fragment>
-    );
-  }
-
-}
-
-/**
-* Wrapper component for using unstated
-*/
-const RemoveAdminButtonWrapper = withUnstatedContainers(RemoveAdminButton, [AppContainer, AdminUsersContainer]);
-
-RemoveAdminButton.propTypes = {
-  t: PropTypes.func.isRequired, // i18next
-  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
-  adminUsersContainer: PropTypes.instanceOf(AdminUsersContainer).isRequired,
-
-  user: PropTypes.object.isRequired,
-};
-
-export default withTranslation()(RemoveAdminButtonWrapper);

+ 62 - 0
packages/app/src/components/Admin/Users/RemoveAdminMenuItem.tsx

@@ -0,0 +1,62 @@
+import React, { useCallback, useMemo } from 'react';
+
+import { useTranslation } from 'react-i18next';
+
+import AdminUsersContainer from '~/client/services/AdminUsersContainer';
+import { toastSuccess, toastError } from '~/client/util/apiNotification';
+import { IUserHasId } from '~/interfaces/user';
+import { useCurrentUser } from '~/stores/context';
+
+import { withUnstatedContainers } from '../../UnstatedUtils';
+
+
+const RemoveAdminAlert = React.memo((): JSX.Element => {
+  const { t } = useTranslation();
+
+  return (
+    <div className="px-4">
+      <i className="icon-fw icon-user-unfollow mb-2"></i>{t('admin:user_management.user_table.remove_admin_access')}
+      <p className="alert alert-danger">{t('admin:user_management.user_table.cannot_remove')}</p>
+    </div>
+  );
+});
+
+
+type Props = {
+  adminUsersContainer: AdminUsersContainer,
+  user: IUserHasId,
+}
+
+const RemoveAdminMenuItem = (props: Props): JSX.Element => {
+  const { t } = useTranslation();
+
+  const { adminUsersContainer, user } = props;
+
+  const { data: currentUser } = useCurrentUser();
+
+  const clickRemoveAdminBtnHandler = useCallback(async() => {
+    try {
+      const username = await adminUsersContainer.removeUserAdmin(user._id);
+      toastSuccess(t('toaster.remove_user_admin', { username }));
+    }
+    catch (err) {
+      toastError(err);
+    }
+  }, [adminUsersContainer, t, user._id]);
+
+
+  return user.username !== currentUser?.username
+    ? (
+      <button className="dropdown-item" type="button" onClick={clickRemoveAdminBtnHandler}>
+        <i className="icon-fw icon-user-unfollow"></i> {t('admin:user_management.user_table.remove_admin_access')}
+      </button>
+    )
+    : <RemoveAdminAlert />;
+};
+
+/**
+* Wrapper component for using unstated
+*/
+const RemoveAdminMenuItemWrapper = withUnstatedContainers(RemoveAdminMenuItem, [AdminUsersContainer]);
+
+export default RemoveAdminMenuItemWrapper;

+ 60 - 0
packages/app/src/components/Admin/Users/StatusSuspendMenuItem.tsx

@@ -0,0 +1,60 @@
+import React, { useCallback } from 'react';
+
+import { useTranslation } from 'react-i18next';
+
+import AdminUsersContainer from '~/client/services/AdminUsersContainer';
+import { toastSuccess, toastError } from '~/client/util/apiNotification';
+import { withUnstatedContainers } from '~/components/UnstatedUtils';
+import { IUserHasId } from '~/interfaces/user';
+import { useCurrentUser } from '~/stores/context';
+
+
+const SuspendAlert = React.memo((): JSX.Element => {
+  const { t } = useTranslation();
+
+  return (
+    <div className="px-4">
+      <i className="icon-fw icon-ban mb-2"></i>{t('admin:user_management.user_table.deactivate_account')}
+      <p className="alert alert-danger">{t('admin:user_management.user_table.your_own')}</p>
+    </div>
+  );
+});
+
+
+type Props = {
+  adminUsersContainer: AdminUsersContainer,
+  user: IUserHasId,
+}
+
+const StatusSuspendMenuItem = (props: Props): JSX.Element => {
+  const { t } = useTranslation();
+
+  const { adminUsersContainer, user } = props;
+
+  const { data: currentUser } = useCurrentUser();
+
+  const clickDeactiveBtnHandler = useCallback(async() => {
+    try {
+      const username = await adminUsersContainer.deactivateUser(user._id);
+      toastSuccess(t('toaster.deactivate_user_success', { username }));
+    }
+    catch (err) {
+      toastError(err);
+    }
+  }, [adminUsersContainer, t, user._id]);
+
+  return user.username !== currentUser?.username
+    ? (
+      <button className="dropdown-item" type="button" onClick={clickDeactiveBtnHandler}>
+        <i className="icon-fw icon-ban"></i> {t('admin:user_management.user_table.deactivate_account')}
+      </button>
+    )
+    : <SuspendAlert />;
+};
+
+/**
+ * Wrapper component for using unstated
+ */
+const StatusSuspendMenuItemWrapper = withUnstatedContainers(StatusSuspendMenuItem, [AdminUsersContainer]);
+
+export default StatusSuspendMenuItemWrapper;

+ 0 - 78
packages/app/src/components/Admin/Users/StatusSuspendedButton.jsx

@@ -1,78 +0,0 @@
-import React, { Fragment } from 'react';
-import PropTypes from 'prop-types';
-import { withTranslation } from 'react-i18next';
-
-import { withUnstatedContainers } from '../../UnstatedUtils';
-import AppContainer from '~/client/services/AppContainer';
-import { toastSuccess, toastError } from '~/client/util/apiNotification';
-import AdminUsersContainer from '~/client/services/AdminUsersContainer';
-
-class StatusSuspendedButton extends React.Component {
-
-  constructor(props) {
-    super(props);
-
-    this.onClickDeactiveBtn = this.onClickDeactiveBtn.bind(this);
-  }
-
-  async onClickDeactiveBtn() {
-    const { t } = this.props;
-
-    try {
-      const username = await this.props.adminUsersContainer.deactivateUser(this.props.user._id);
-      toastSuccess(t('toaster.deactivate_user_success', { username }));
-    }
-    catch (err) {
-      toastError(err);
-    }
-  }
-
-  renderSuspendedBtn() {
-    const { t } = this.props;
-
-    return (
-      <button className="dropdown-item" type="button" onClick={() => { this.onClickDeactiveBtn() }}>
-        <i className="icon-fw icon-ban"></i> {t('admin:user_management.user_table.deactivate_account')}
-      </button>
-    );
-  }
-
-  renderSuspendedAlert() {
-    const { t } = this.props;
-
-    return (
-      <div className="px-4">
-        <i className="icon-fw icon-ban mb-2"></i>{t('admin:user_management.user_table.deactivate_account')}
-        <p className="alert alert-danger">{t('admin:user_management.user_table.your_own')}</p>
-      </div>
-    );
-  }
-
-  render() {
-    const { user } = this.props;
-    const { currentUsername } = this.props.appContainer;
-
-    return (
-      <Fragment>
-        {user.username !== currentUsername ? this.renderSuspendedBtn()
-          : this.renderSuspendedAlert()}
-      </Fragment>
-    );
-  }
-
-}
-
-/**
- * Wrapper component for using unstated
- */
-const StatusSuspendedFormWrapper = withUnstatedContainers(StatusSuspendedButton, [AppContainer, AdminUsersContainer]);
-
-StatusSuspendedButton.propTypes = {
-  t: PropTypes.func.isRequired, // i18next
-  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
-  adminUsersContainer: PropTypes.instanceOf(AdminUsersContainer).isRequired,
-
-  user: PropTypes.object.isRequired,
-};
-
-export default withTranslation()(StatusSuspendedFormWrapper);

+ 12 - 9
packages/app/src/components/Admin/Users/UserMenu.jsx

@@ -1,20 +1,23 @@
 import React, { Fragment } from 'react';
 import React, { Fragment } from 'react';
+
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
 import { withTranslation } from 'react-i18next';
 import {
 import {
   UncontrolledDropdown, DropdownToggle, DropdownMenu,
   UncontrolledDropdown, DropdownToggle, DropdownMenu,
 } from 'reactstrap';
 } from 'reactstrap';
 
 
-import StatusActivateButton from './StatusActivateButton';
-import StatusSuspendedButton from './StatusSuspendedButton';
-import UserRemoveButton from './UserRemoveButton';
-import RemoveAdminButton from './RemoveAdminButton';
+import AdminUsersContainer from '~/client/services/AdminUsersContainer';
+import AppContainer from '~/client/services/AppContainer';
+
+import { withUnstatedContainers } from '../../UnstatedUtils';
+
 import GiveAdminButton from './GiveAdminButton';
 import GiveAdminButton from './GiveAdminButton';
+import RemoveAdminMenuItem from './RemoveAdminMenuItem';
 import SendInvitationEmailButton from './SendInvitationEmailButton';
 import SendInvitationEmailButton from './SendInvitationEmailButton';
+import StatusActivateButton from './StatusActivateButton';
+import StatusSuspendedMenuItem from './StatusSuspendMenuItem';
+import UserRemoveButton from './UserRemoveButton';
 
 
-import { withUnstatedContainers } from '../../UnstatedUtils';
-import AppContainer from '~/client/services/AppContainer';
-import AdminUsersContainer from '~/client/services/AdminUsersContainer';
 
 
 class UserMenu extends React.Component {
 class UserMenu extends React.Component {
 
 
@@ -63,7 +66,7 @@ class UserMenu extends React.Component {
         <li className="dropdown-header">{t('status')}</li>
         <li className="dropdown-header">{t('status')}</li>
         <li>
         <li>
           {(user.status === 1 || user.status === 3) && <StatusActivateButton user={user} />}
           {(user.status === 1 || user.status === 3) && <StatusActivateButton user={user} />}
-          {user.status === 2 && <StatusSuspendedButton user={user} />}
+          {user.status === 2 && <StatusSuspendedMenuItem user={user} />}
           {user.status === 5 && (
           {user.status === 5 && (
             <SendInvitationEmailButton
             <SendInvitationEmailButton
               user={user}
               user={user}
@@ -85,7 +88,7 @@ class UserMenu extends React.Component {
         <li className="dropdown-divider pl-0"></li>
         <li className="dropdown-divider pl-0"></li>
         <li className="dropdown-header">{t('admin:user_management.user_table.administrator_menu')}</li>
         <li className="dropdown-header">{t('admin:user_management.user_table.administrator_menu')}</li>
         <li>
         <li>
-          {user.admin === true && <RemoveAdminButton user={user} />}
+          {user.admin === true && <RemoveAdminMenuItem user={user} />}
           {user.admin === false && <GiveAdminButton user={user} />}
           {user.admin === false && <GiveAdminButton user={user} />}
         </li>
         </li>
       </Fragment>
       </Fragment>

+ 5 - 40
packages/app/src/components/Page/TrashPageAlert.jsx

@@ -1,5 +1,4 @@
-import React, { useState } from 'react';
-
+import React from 'react';
 
 
 import { UserPicture } from '@growi/ui';
 import { UserPicture } from '@growi/ui';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
@@ -9,8 +8,8 @@ import PageContainer from '~/client/services/PageContainer';
 import { useCurrentUpdatedAt, useShareLinkId } from '~/stores/context';
 import { useCurrentUpdatedAt, useShareLinkId } from '~/stores/context';
 import { usePageDeleteModal, usePutBackPageModal } from '~/stores/modal';
 import { usePageDeleteModal, usePutBackPageModal } from '~/stores/modal';
 import { useSWRxPageInfo } from '~/stores/page';
 import { useSWRxPageInfo } from '~/stores/page';
+import { useIsAbleToShowTrashPageManagementButtons } from '~/stores/ui';
 
 
-import EmptyTrashModal from '../EmptyTrashModal';
 import { withUnstatedContainers } from '../UnstatedUtils';
 import { withUnstatedContainers } from '../UnstatedUtils';
 
 
 const onDeletedHandler = (pathOrPathsToDelete, isRecursively, isCompletely) => {
 const onDeletedHandler = (pathOrPathsToDelete, isRecursively, isCompletely) => {
@@ -27,6 +26,8 @@ const TrashPageAlert = (props) => {
   const {
   const {
     pageId, revisionId, path, isDeleted, lastUpdateUsername, deletedUserName, deletedAt,
     pageId, revisionId, path, isDeleted, lastUpdateUsername, deletedUserName, deletedAt,
   } = pageContainer.state;
   } = pageContainer.state;
+
+  const { data: isAbleToShowTrashPageManagementButtons } = useIsAbleToShowTrashPageManagementButtons();
   const { data: shareLinkId } = useShareLinkId();
   const { data: shareLinkId } = useShareLinkId();
 
 
   /*
   /*
@@ -37,19 +38,10 @@ const TrashPageAlert = (props) => {
   const { data: pageInfo } = useSWRxPageInfo(pageId ?? null, shareLinkId);
   const { data: pageInfo } = useSWRxPageInfo(pageId ?? null, shareLinkId);
 
 
   const { data: updatedAt } = useCurrentUpdatedAt();
   const { data: updatedAt } = useCurrentUpdatedAt();
-  const [isEmptyTrashModalShown, setIsEmptyTrashModalShown] = useState(false);
 
 
   const { open: openDeleteModal } = usePageDeleteModal();
   const { open: openDeleteModal } = usePageDeleteModal();
   const { open: openPutBackPageModal } = usePutBackPageModal();
   const { open: openPutBackPageModal } = usePutBackPageModal();
 
 
-  function openEmptyTrashModalHandler() {
-    setIsEmptyTrashModalShown(true);
-  }
-
-  function closeEmptyTrashModalHandler() {
-    setIsEmptyTrashModalShown(false);
-  }
-
   function openPutbackPageModalHandler() {
   function openPutbackPageModalHandler() {
     const putBackedHandler = (path) => {
     const putBackedHandler = (path) => {
       window.location.reload();
       window.location.reload();
@@ -69,20 +61,6 @@ const TrashPageAlert = (props) => {
     openDeleteModal([pageToDelete], { onDeleted: onDeletedHandler });
     openDeleteModal([pageToDelete], { onDeleted: onDeletedHandler });
   }
   }
 
 
-  function renderEmptyButton() {
-    return (
-      <button
-        href="#"
-        type="button"
-        className="btn btn-danger rounded-pill btn-sm ml-auto"
-        data-target="#emptyTrash"
-        onClick={openEmptyTrashModalHandler}
-      >
-        <i className="icon-trash" aria-hidden="true"></i>{ t('modal_empty.empty_the_trash') }
-      </button>
-    );
-  }
-
   function renderTrashPageManagementButtons() {
   function renderTrashPageManagementButtons() {
     return (
     return (
       <>
       <>
@@ -106,17 +84,6 @@ const TrashPageAlert = (props) => {
     );
     );
   }
   }
 
 
-  function renderModals() {
-    return (
-      <>
-        <EmptyTrashModal
-          isOpen={isEmptyTrashModalShown}
-          onClose={closeEmptyTrashModalHandler}
-        />
-      </>
-    );
-  }
-
   return (
   return (
     <>
     <>
       <div className="alert alert-warning py-3 pl-4 d-flex flex-column flex-lg-row">
       <div className="alert alert-warning py-3 pl-4 d-flex flex-column flex-lg-row">
@@ -133,11 +100,9 @@ const TrashPageAlert = (props) => {
           )}
           )}
         </div>
         </div>
         <div className="pt-1 d-flex align-items-end align-items-lg-center">
         <div className="pt-1 d-flex align-items-end align-items-lg-center">
-          <span>{ pageContainer.isAbleToShowEmptyTrashButton && renderEmptyButton()}</span>
-          { pageContainer.isAbleToShowTrashPageManagementButtons && renderTrashPageManagementButtons()}
+          { isAbleToShowTrashPageManagementButtons && renderTrashPageManagementButtons()}
         </div>
         </div>
       </div>
       </div>
-      {renderModals()}
     </>
     </>
   );
   );
 };
 };

+ 1 - 1
packages/app/src/components/TrashPageList.jsx

@@ -24,7 +24,7 @@ const TrashPageList = () => {
 
 
   const emptyTrashButton = useMemo(() => {
   const emptyTrashButton = useMemo(() => {
     return <EmptyTrashButton />;
     return <EmptyTrashButton />;
-  }, [t]);
+  }, []);
 
 
   return (
   return (
     <div data-testid="trash-page-list" className="mt-5 d-edit-none">
     <div data-testid="trash-page-list" className="mt-5 d-edit-none">

+ 31 - 18
packages/app/src/stores/ui.tsx

@@ -17,7 +17,7 @@ import loggerFactory from '~/utils/logger';
 
 
 import {
 import {
   useCurrentPageId, useCurrentPagePath, useIsEditable, useIsTrashPage, useIsUserPage, useIsGuestUser,
   useCurrentPageId, useCurrentPagePath, useIsEditable, useIsTrashPage, useIsUserPage, useIsGuestUser,
-  useIsNotCreatable, useIsSharedUser, useNotFoundTargetPathOrId, useIsForbidden, useIsIdenticalPath, useIsNotFoundPermalink,
+  useIsNotCreatable, useIsSharedUser, useNotFoundTargetPathOrId, useIsForbidden, useIsIdenticalPath, useIsNotFoundPermalink, useCurrentUser, useIsDeleted,
 } from './context';
 } from './context';
 import { localStorageMiddleware } from './middlewares/sync-to-storage';
 import { localStorageMiddleware } from './middlewares/sync-to-storage';
 import { useStaticSWR } from './use-static-swr';
 import { useStaticSWR } from './use-static-swr';
@@ -303,6 +303,36 @@ export const useGlobalSearchFormRef = (initialData?: RefObject<IFocusable>): SWR
   return useStaticSWR('globalSearchTypeahead', initialData);
   return useStaticSWR('globalSearchTypeahead', initialData);
 };
 };
 
 
+type PageTreeDescCountMapUtils = {
+  update(newData?: UpdateDescCountData): Promise<UpdateDescCountData | undefined>
+  getDescCount(pageId?: string): number | null | undefined
+}
+
+export const usePageTreeDescCountMap = (initialData?: UpdateDescCountData): SWRResponse<UpdateDescCountData, Error> & PageTreeDescCountMapUtils => {
+  const key = 'pageTreeDescCountMap';
+
+  const swrResponse = useStaticSWR<UpdateDescCountData, Error>(key, initialData, { fallbackData: new Map() });
+
+  return {
+    ...swrResponse,
+    getDescCount: (pageId?: string) => (pageId != null ? swrResponse.data?.get(pageId) : null),
+    update: (newData: UpdateDescCountData) => swrResponse.mutate(new Map([...(swrResponse.data || new Map()), ...newData])),
+  };
+};
+
+
+/** **********************************************************
+ *                          SWR Hooks
+ *                Determined value by context
+ *********************************************************** */
+
+export const useIsAbleToShowTrashPageManagementButtons = (): SWRResponse<boolean, Error> => {
+  const { data: currentUser } = useCurrentUser();
+  const { data: isDeleted } = useIsDeleted();
+
+  return useStaticSWR('isAbleToShowTrashPageManagementButtons', isDeleted && currentUser != null);
+};
+
 export const useIsAbleToShowPageManagement = (): SWRResponse<boolean, Error> => {
 export const useIsAbleToShowPageManagement = (): SWRResponse<boolean, Error> => {
   const key = 'isAbleToShowPageManagement';
   const key = 'isAbleToShowPageManagement';
   const { data: currentPageId } = useCurrentPageId();
   const { data: currentPageId } = useCurrentPageId();
@@ -367,20 +397,3 @@ export const useIsAbleToShowPageAuthors = (): SWRResponse<boolean, Error> => {
     () => isPageExist && !isUserPage,
     () => isPageExist && !isUserPage,
   );
   );
 };
 };
-
-type PageTreeDescCountMapUtils = {
-  update(newData?: UpdateDescCountData): Promise<UpdateDescCountData | undefined>
-  getDescCount(pageId?: string): number | null | undefined
-}
-
-export const usePageTreeDescCountMap = (initialData?: UpdateDescCountData): SWRResponse<UpdateDescCountData, Error> & PageTreeDescCountMapUtils => {
-  const key = 'pageTreeDescCountMap';
-
-  const swrResponse = useStaticSWR<UpdateDescCountData, Error>(key, initialData, { fallbackData: new Map() });
-
-  return {
-    ...swrResponse,
-    getDescCount: (pageId?: string) => (pageId != null ? swrResponse.data?.get(pageId) : null),
-    update: (newData: UpdateDescCountData) => swrResponse.mutate(new Map([...(swrResponse.data || new Map()), ...newData])),
-  };
-};