Yuki Takei пре 8 месеци
родитељ
комит
7278d26589
59 измењених фајлова са 173 додато и 286 уклоњено
  1. 2 2
      apps/app/src/client/components/Admin/ElasticsearchManagement/ElasticsearchManagement.tsx
  2. 2 2
      apps/app/src/client/components/Admin/G2GDataTransfer.tsx
  3. 2 2
      apps/app/src/client/components/Admin/UserGroup/UserGroupPage.tsx
  4. 2 2
      apps/app/src/client/components/Admin/UserGroupDetail/UserGroupDetailPage.tsx
  5. 2 2
      apps/app/src/client/components/DataTransferForm.tsx
  6. 1 1
      apps/app/src/client/components/Hotkeys/Subscribers/EditPage.jsx
  7. 2 2
      apps/app/src/client/components/IdenticalPathPage.tsx
  8. 5 10
      apps/app/src/client/components/Maintenance/Maintenance.tsx
  9. 2 2
      apps/app/src/client/components/Me/AccessTokenScopeSelect.tsx
  10. 3 3
      apps/app/src/client/components/Me/ApiSettings.tsx
  11. 8 7
      apps/app/src/client/components/Navbar/GrowiContextualSubNavigation.tsx
  12. 5 5
      apps/app/src/client/components/Navbar/PageEditorModeManager.tsx
  13. 2 2
      apps/app/src/client/components/NotAvailableForReadOnlyUser.tsx
  14. 4 4
      apps/app/src/client/components/Page/DisplaySwitcher.tsx
  15. 3 4
      apps/app/src/client/components/PageAccessoriesModal/PageAccessoriesModal.tsx
  16. 4 6
      apps/app/src/client/components/PageComment/CommentEditor.tsx
  17. 2 2
      apps/app/src/client/components/PageComment/ReplyComments.tsx
  18. 5 6
      apps/app/src/client/components/PageControls/PageControls.tsx
  19. 2 3
      apps/app/src/client/components/PageCreateModal.tsx
  20. 2 2
      apps/app/src/client/components/PageDuplicateModal.tsx
  21. 8 8
      apps/app/src/client/components/PageEditor/DrawioModal.tsx
  22. 2 2
      apps/app/src/client/components/PageEditor/EditorNavbarBottom/EditorNavbarBottom.tsx
  23. 2 2
      apps/app/src/client/components/PageEditor/EditorNavbarBottom/OptionsSelector.tsx
  24. 8 7
      apps/app/src/client/components/PageEditor/EditorNavbarBottom/SavePageControls.tsx
  25. 14 12
      apps/app/src/client/components/PageEditor/PageEditor.tsx
  26. 2 2
      apps/app/src/client/components/PageEditor/Preview.tsx
  27. 2 2
      apps/app/src/client/components/PageEditor/conflict.tsx
  28. 2 2
      apps/app/src/client/components/PageHeader/PageTitleHeader.spec.tsx
  29. 2 2
      apps/app/src/client/components/PageHeader/PageTitleHeader.tsx
  30. 2 2
      apps/app/src/client/components/PagePresentationModal.tsx
  31. 2 2
      apps/app/src/client/components/PageRenameModal.tsx
  32. 2 2
      apps/app/src/client/components/PageSideContents/PageSideContents.tsx
  33. 2 2
      apps/app/src/client/components/PageStatusAlert.tsx
  34. 2 2
      apps/app/src/client/components/PrivateLegacyPages.tsx
  35. 3 3
      apps/app/src/client/components/SearchPage/SearchPageBase.tsx
  36. 2 2
      apps/app/src/client/components/Sidebar/Sidebar.tsx
  37. 2 3
      apps/app/src/client/components/Sidebar/SidebarContents.tsx
  38. 2 2
      apps/app/src/client/components/Sidebar/SidebarNav/PrimaryItems.tsx
  39. 5 5
      apps/app/src/client/services/create-page/use-create-page.tsx
  40. 6 6
      apps/app/src/client/services/side-effects/hash-changed.ts
  41. 2 2
      apps/app/src/client/services/side-effects/page-updated.ts
  42. 2 2
      apps/app/src/components/PageView/PageAlerts/FullTextSearchNotCoverAlert.tsx
  43. 2 2
      apps/app/src/components/PageView/PageAlerts/PageStaleAlert.tsx
  44. 7 8
      apps/app/src/components/PageView/PageView.tsx
  45. 3 3
      apps/app/src/components/Script/DrawioViewerScript/use-viewer-min-js-url.spec.ts
  46. 2 2
      apps/app/src/features/external-user-group/client/components/ExternalUserGroup/ExternalUserGroupManagement.tsx
  47. 2 2
      apps/app/src/features/page-bulk-export/client/components/PageBulkExportSelectModal.tsx
  48. 1 1
      apps/app/src/pages/[[...path]].page.tsx
  49. 1 1
      apps/app/src/pages/maintenance.page.tsx
  50. 2 2
      apps/app/src/services/layout/use-should-expand-content.ts
  51. 2 1
      apps/app/src/states/server-configurations/hydrate.ts
  52. 1 0
      apps/app/src/states/server-configurations/index.ts
  53. 2 2
      apps/app/src/states/ui/sidebar.ts
  54. 1 1
      apps/app/src/stores-universal/context.tsx
  55. 0 106
      apps/app/src/stores-universal/ui.tsx
  56. 1 1
      apps/app/src/stores/alert.tsx
  57. 2 3
      apps/app/src/stores/editor.tsx
  58. 2 2
      apps/app/src/stores/renderer.tsx
  59. 6 6
      apps/app/src/stores/ui.tsx

+ 2 - 2
apps/app/src/client/components/Admin/ElasticsearchManagement/ElasticsearchManagement.tsx

@@ -6,7 +6,7 @@ import { useTranslation } from 'next-i18next';
 import { apiv3Get, apiv3Post, apiv3Put } from '~/client/util/apiv3-client';
 import { toastSuccess, toastError } from '~/client/util/toastr';
 import { SocketEventName } from '~/interfaces/websocket';
-import { useIsSearchServiceReachable } from '~/stores-universal/context';
+import { useIsSearchServiceReachable } from '~/states/server-configurations';
 import { useAdminSocket } from '~/stores/socket-io';
 
 import NormalizeIndicesControls from './NormalizeIndicesControls';
@@ -16,7 +16,7 @@ import StatusTable from './StatusTable';
 
 const ElasticsearchManagement = () => {
   const { t } = useTranslation('admin');
-  const { data: isSearchServiceReachable } = useIsSearchServiceReachable();
+  const [isSearchServiceReachable] = useIsSearchServiceReachable();
   const { data: socket } = useAdminSocket();
 
   const [isInitialized, setIsInitialized] = useState(false);

+ 2 - 2
apps/app/src/client/components/Admin/G2GDataTransfer.tsx

@@ -8,7 +8,7 @@ import { useGenerateTransferKey } from '~/client/services/g2g-transfer';
 import { apiv3Get, apiv3Post } from '~/client/util/apiv3-client';
 import { toastError, toastSuccess } from '~/client/util/toastr';
 import { G2G_PROGRESS_STATUS, type G2GProgress } from '~/interfaces/g2g-transfer';
-import { useGrowiDocumentationUrl } from '~/stores-universal/context';
+import { useGrowiDocumentationUrl } from '~/states/context';
 import { useAdminSocket } from '~/stores/socket-io';
 
 import CustomCopyToClipBoard from '../Common/CustomCopyToClipBoard';
@@ -124,7 +124,7 @@ const G2GDataTransfer = (): JSX.Element => {
     }
   }, [setTransferring, startTransferKey, selectedCollections, optionsMap]);
 
-  const { data: documentationUrl } = useGrowiDocumentationUrl();
+  const [documentationUrl] = useGrowiDocumentationUrl();
 
   // File upload
   // const onChangeFileUploadTypeHandler = useCallback((e: ChangeEvent, type: string) => {

+ 2 - 2
apps/app/src/client/components/Admin/UserGroup/UserGroupPage.tsx

@@ -12,7 +12,7 @@ import { toastSuccess, toastError } from '~/client/util/toastr';
 import { ExternalGroupManagement } from '~/features/external-user-group/client/components/ExternalUserGroup/ExternalUserGroupManagement';
 import { useSWRxExternalUserGroupList } from '~/features/external-user-group/client/stores/external-user-group';
 import type { PageActionOnGroupDelete } from '~/interfaces/user-group';
-import { useIsAclEnabled } from '~/stores-universal/context';
+import { useIsAclEnabled } from '~/states/server-configurations';
 import { useSWRxUserGroupList, useSWRxChildUserGroupList, useSWRxUserGroupRelationList } from '~/stores/user-group';
 
 
@@ -23,7 +23,7 @@ const UserGroupTable = dynamic(() => import('./UserGroupTable').then(mod => mod.
 export const UserGroupPage: FC = () => {
   const { t } = useTranslation();
 
-  const { data: isAclEnabled } = useIsAclEnabled();
+  const [isAclEnabled] = useIsAclEnabled();
 
   /*
    * Fetch

+ 2 - 2
apps/app/src/client/components/Admin/UserGroupDetail/UserGroupDetailPage.tsx

@@ -18,7 +18,7 @@ import { toastSuccess, toastError } from '~/client/util/toastr';
 import type { IExternalUserGroupHasId } from '~/features/external-user-group/interfaces/external-user-group';
 import type { PageActionOnGroupDelete, SearchType } from '~/interfaces/user-group';
 import { SearchTypes } from '~/interfaces/user-group';
-import { useIsAclEnabled } from '~/stores-universal/context';
+import { useIsAclEnabled } from '~/states/server-configurations';
 import { useUpdateUserGroupConfirmModal } from '~/stores/modal';
 import { useSWRxUserGroupPages, useSWRxSelectableParentUserGroups, useSWRxSelectableChildUserGroups } from '~/stores/user-group';
 import loggerFactory from '~/utils/logger';
@@ -104,7 +104,7 @@ const UserGroupDetailPage = (props: Props): JSX.Element => {
 
   const { data: ancestorUserGroups, mutate: mutateAncestorUserGroups } = useAncestorUserGroups(currentUserGroupId, isExternalGroup);
 
-  const { data: isAclEnabled } = useIsAclEnabled();
+  const [isAclEnabled] = useIsAclEnabled();
 
   const { open: openUpdateParentConfirmModal } = useUpdateUserGroupConfirmModal();
 

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

@@ -3,14 +3,14 @@ import React, { type JSX } from 'react';
 import { useTranslation } from 'next-i18next';
 
 import { useGenerateTransferKey } from '~/client/services/g2g-transfer';
-import { useGrowiDocumentationUrl } from '~/stores-universal/context';
+import { useGrowiDocumentationUrl } from '~/states/context';
 
 import CustomCopyToClipBoard from './Common/CustomCopyToClipBoard';
 
 const DataTransferForm = (): JSX.Element => {
   const { t } = useTranslation('commons');
   const { transferKey, generateTransferKey } = useGenerateTransferKey();
-  const { data: documentationUrl } = useGrowiDocumentationUrl();
+  const [documentationUrl] = useGrowiDocumentationUrl();
 
   return (
     <div data-testid="installerForm" className="py-3 px-4">

+ 1 - 1
apps/app/src/client/components/Hotkeys/Subscribers/EditPage.jsx

@@ -3,7 +3,7 @@ import { useEffect } from 'react';
 import PropTypes from 'prop-types';
 
 import { useIsEditable } from '~/stores-universal/context';
-import { EditorMode, useEditorMode } from '~/stores-universal/ui';
+import { EditorMode, useEditorMode } from '~/states/ui/editor';
 
 const EditPage = (props) => {
   const { data: isEditable } = useIsEditable();

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

@@ -4,7 +4,7 @@ import React from 'react';
 import { DevidedPagePath } from '@growi/core/dist/models';
 import { useTranslation } from 'next-i18next';
 
-import { useCurrentPathname } from '~/stores-universal/context';
+import { useCurrentPathname } from '~/states/global';
 import { useSWRxPageInfoForList, useSWRxPagesByPath } from '~/stores/page-listing';
 
 import { PageListItemL } from './PageList/PageListItemL';
@@ -50,7 +50,7 @@ const IdenticalPathAlert : FC<IdenticalPathAlertProps> = (props: IdenticalPathAl
 
 export const IdenticalPathPage = (): JSX.Element => {
 
-  const { data: currentPath } = useCurrentPathname();
+  const [currentPath] = useCurrentPathname();
 
   const { data: pages } = useSWRxPagesByPath(currentPath);
   const { injectTo } = useSWRxPageInfoForList(null, currentPath, true, true);

+ 5 - 10
apps/app/src/client/components/Maintenance/Maintenance.tsx

@@ -1,21 +1,16 @@
 import type { JSX } from 'react';
 
-import type { IUserHasId } from '@growi/core';
 import { useTranslation } from 'next-i18next';
 
 import { apiv3Post } from '~/client/util/apiv3-client';
 import { toastError } from '~/client/util/toastr';
-import { useCurrentUser } from '~/stores-universal/context';
+import { useCurrentUser } from '~/states/global';
 
 
-type Props = {
-  currentUser: IUserHasId,
-};
-
-export const Maintenance = (props: Props): JSX.Element => {
+export const Maintenance = (): JSX.Element => {
   const { t } = useTranslation();
 
-  useCurrentUser(props.currentUser ?? null);
+  const [currentUser] = useCurrentUser();
 
   const logoutHandler = async() => {
     try {
@@ -34,14 +29,14 @@ export const Maintenance = (props: Props): JSX.Element => {
       <h3>{ t('maintenance_mode.growi_is_under_maintenance') }</h3>
       <hr />
       <div className="text-start">
-        {props.currentUser?.admin
+        {currentUser?.admin
               && (
                 <p>
                   <span className="material-symbols-outlined">arrow_circle_right</span>
                   <a className="btn btn-link" href="/admin">{ t('maintenance_mode.admin_page') }</a>
                 </p>
               )}
-        {props.currentUser != null
+        {currentUser != null
           ? (
             <p>
               <span className="material-symbols-outlined">arrow_circle_right</span>

+ 2 - 2
apps/app/src/client/components/Me/AccessTokenScopeSelect.tsx

@@ -5,7 +5,7 @@ import { SCOPE } from '@growi/core/dist/interfaces';
 import type { UseFormRegisterReturn } from 'react-hook-form';
 
 import { extractScopes, getDisabledScopes, parseScopes } from '~/client/util/scope-util';
-import { useIsAdmin } from '~/stores-universal/context';
+import { useIsAdmin } from '~/states/context';
 
 import { AccessTokenScopeList } from './AccessTokenScopeList';
 
@@ -23,7 +23,7 @@ type AccessTokenScopeSelectProps = {
  */
 export const AccessTokenScopeSelect: React.FC<AccessTokenScopeSelectProps> = ({ register, selectedScopes }) => {
   const [disabledScopes, setDisabledScopes] = useState<Set<Scope>>(new Set());
-  const { data: isAdmin } = useIsAdmin();
+  const [isAdmin] = useIsAdmin();
 
   const ScopesMap = useMemo(() => parseScopes({ scopes: SCOPE, isAdmin }), [isAdmin]);
   const extractedScopes = useMemo(() => extractScopes(ScopesMap), [ScopesMap]);

+ 3 - 3
apps/app/src/client/components/Me/ApiSettings.tsx

@@ -2,7 +2,7 @@ import React from 'react';
 
 import { useTranslation } from 'next-i18next';
 
-import { useCurrentUser } from '~/stores-universal/context';
+import { useCurrentUser } from '~/states/global';
 
 import { AccessTokenSettings } from './AccessTokenSettings';
 import { ApiTokenSettings } from './ApiTokenSettings';
@@ -11,9 +11,9 @@ import { ApiTokenSettings } from './ApiTokenSettings';
 const ApiSettings = React.memo((): JSX.Element => {
 
   const { t } = useTranslation();
-  const { data: currentUser, isLoading: isLoadingCurrentUserData } = useCurrentUser();
+  const [currentUser] = useCurrentUser();
 
-  const shouldHideAccessTokenSettings = isLoadingCurrentUserData || !currentUser?.readOnly;
+  const shouldHideAccessTokenSettings = currentUser == null || !currentUser?.readOnly;
 
   return (
     <>

+ 8 - 7
apps/app/src/client/components/Navbar/GrowiContextualSubNavigation.tsx

@@ -28,9 +28,10 @@ import { useIsGuestUser, useIsReadOnlyUser } from '~/states/context';
 import { useCurrentPathname, useCurrentUser } from '~/states/global';
 import { useCurrentPageId, useFetchCurrentPage } from '~/states/page';
 import {
-  useIsBulkExportPagesEnabled, useIsLocalAccountRegistrationEnabled, useIsSharedUser, useShareLinkId, useIsUploadEnabled,
-} from '~/stores-universal/context';
-import { useEditorMode } from '~/stores-universal/ui';
+  useIsBulkExportPagesEnabled, useIsLocalAccountRegistrationEnabled, useIsUploadEnabled,
+} from '~/states/server-configurations';
+import { useEditorMode } from '~/states/ui/editor';
+import { useIsSharedUser, useShareLinkId } from '~/stores-universal/context';
 import {
   usePageAccessoriesModal, PageAccessoriesModalContents, type IPageForPageDuplicateModal,
   usePageDuplicateModal, usePageRenameModal, usePageDeleteModal, usePagePresentationModal,
@@ -80,8 +81,8 @@ const PageOperationMenuItems = (props: PageOperationMenuItemsProps): JSX.Element
   const [isGuestUser] = useIsGuestUser();
   const [isReadOnlyUser] = useIsReadOnlyUser();
   const { data: isSharedUser } = useIsSharedUser();
-  const { data: isBulkExportPagesEnabled } = useIsBulkExportPagesEnabled();
-  const { data: isUploadEnabled } = useIsUploadEnabled();
+  const [isBulkExportPagesEnabled] = useIsBulkExportPagesEnabled();
+  const [isUploadEnabled] = useIsUploadEnabled();
 
   const { open: openPresentationModal } = usePagePresentationModal();
   const { open: openAccessoriesModal } = usePageAccessoriesModal();
@@ -263,12 +264,12 @@ const GrowiContextualSubNavigation = (props: GrowiContextualSubNavigationProps):
   const revision = currentPage?.revision;
   const revisionId = (revision != null && isPopulated(revision)) ? revision._id : undefined;
 
-  const { data: editorMode } = useEditorMode();
+  const { editorMode } = useEditorMode();
   const [pageId] = useCurrentPageId();
   const [currentUser] = useCurrentUser();
   const [isGuestUser] = useIsGuestUser();
   const [isReadOnlyUser] = useIsReadOnlyUser();
-  const { data: isLocalAccountRegistrationEnabled } = useIsLocalAccountRegistrationEnabled();
+  const [isLocalAccountRegistrationEnabled] = useIsLocalAccountRegistrationEnabled();
   const { data: isSharedUser } = useIsSharedUser();
 
   const shouldExpandContent = useShouldExpandContent(currentPage);

+ 5 - 5
apps/app/src/client/components/Navbar/PageEditorModeManager.tsx

@@ -9,7 +9,7 @@ import { useTranslation } from 'next-i18next';
 import { useCreatePage } from '~/client/services/create-page';
 import { toastError } from '~/client/util/toastr';
 import { usePageNotFound } from '~/states/page';
-import { EditorMode, useEditorMode } from '~/stores-universal/ui';
+import { useEditorMode, EditorMode } from '~/states/ui/editor';
 import { useIsDeviceLargerThanMd } from '~/stores/ui';
 import { useCurrentPageYjsData } from '~/stores/yjs';
 
@@ -67,7 +67,7 @@ export const PageEditorModeManager = (props: Props): JSX.Element => {
   const { t } = useTranslation('commons');
 
   const [isNotFound] = usePageNotFound();
-  const { mutate: mutateEditorMode } = useEditorMode();
+  const { setEditorMode } = useEditorMode();
   const { data: isDeviceLargerThanMd } = useIsDeviceLargerThanMd();
   const { data: currentPageYjsData } = useCurrentPageYjsData();
 
@@ -75,7 +75,7 @@ export const PageEditorModeManager = (props: Props): JSX.Element => {
 
   const editButtonClickedHandler = useCallback(async() => {
     if (isNotFound == null || isNotFound === false) {
-      mutateEditorMode(EditorMode.Editor);
+      setEditorMode(EditorMode.Editor);
       return;
     }
 
@@ -90,7 +90,7 @@ export const PageEditorModeManager = (props: Props): JSX.Element => {
     catch (err) {
       toastError(t('toaster.create_failed', { target: path }));
     }
-  }, [create, isNotFound, mutateEditorMode, path, t]);
+  }, [create, isNotFound, setEditorMode, path, t]);
 
   const _isBtnDisabled = isCreating || isBtnDisabled;
 
@@ -118,7 +118,7 @@ export const PageEditorModeManager = (props: Props): JSX.Element => {
             currentEditorMode={editorMode}
             editorMode={EditorMode.View}
             isBtnDisabled={_isBtnDisabled}
-            onClick={() => mutateEditorMode(EditorMode.View)}
+            onClick={() => setEditorMode(EditorMode.View)}
           >
             <span className="material-symbols-outlined fs-4">play_arrow</span>{t('View')}
           </PageEditorModeButton>

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

@@ -3,7 +3,7 @@ import React, { type JSX } from 'react';
 import { useTranslation } from 'next-i18next';
 
 import { useIsReadOnlyUser } from '~/states/context';
-import { useIsRomUserAllowedToComment } from '~/stores-universal/context';
+import { useIsRomUserAllowedToComment } from '~/states/server-configurations';
 
 import { NotAvailable } from './NotAvailable';
 
@@ -34,7 +34,7 @@ export const NotAvailableIfReadOnlyUserNotAllowedToComment: React.FC<{
   const { t } = useTranslation();
   const [isReadOnlyUser] = useIsReadOnlyUser();
 
-  const { data: isRomUserAllowedToComment } = useIsRomUserAllowedToComment();
+  const [isRomUserAllowedToComment] = useIsRomUserAllowedToComment();
 
   const isDisabled = !!isReadOnlyUser && !isRomUserAllowedToComment;
   const title = t('page_comment.comment_management_is_not_allowed');

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

@@ -3,9 +3,9 @@ import type { JSX } from 'react';
 import dynamic from 'next/dynamic';
 
 import { useHashChangedEffect } from '~/client/services/side-effects/hash-changed';
+import { useIsEditable } from '~/states/context';
 import { useLatestRevision } from '~/states/page';
-import { useIsEditable } from '~/stores-universal/context';
-import { EditorMode, useEditorMode } from '~/stores-universal/ui';
+import { EditorMode, useEditorMode } from '~/states/ui/editor';
 import { useReservedNextCaretLine } from '~/stores/editor';
 
 import { LazyRenderer } from '../Common/LazyRenderer';
@@ -17,8 +17,8 @@ const PageEditorReadOnly = dynamic(() => import('../PageEditor/PageEditorReadOnl
 
 export const DisplaySwitcher = (): JSX.Element => {
 
-  const { data: editorMode = EditorMode.View } = useEditorMode();
-  const { data: isEditable } = useIsEditable();
+  const { editorMode } = useEditorMode();
+  const [isEditable] = useIsEditable();
   const [isLatestRevision] = useLatestRevision();
 
   useHashChangedEffect();

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

@@ -7,9 +7,8 @@ import {
 } from 'reactstrap';
 
 import { useIsGuestUser, useIsReadOnlyUser } from '~/states/context';
-import {
-  useDisableLinkSharing, useIsSharedUser,
-} from '~/stores-universal/context';
+import { useDisableLinkSharing } from '~/states/server-configurations';
+import { useIsSharedUser } from '~/stores-universal/context';
 import { usePageAccessoriesModal, PageAccessoriesModalContents } from '~/stores/modal';
 import { useIsDeviceLargerThanLg } from '~/stores/ui';
 
@@ -36,7 +35,7 @@ export const PageAccessoriesModal = (): JSX.Element => {
   const { data: isSharedUser } = useIsSharedUser();
   const [isGuestUser] = useIsGuestUser();
   const [isReadOnlyUser] = useIsReadOnlyUser();
-  const { data: isLinkSharingDisabled } = useDisableLinkSharing();
+  const [isLinkSharingDisabled] = useDisableLinkSharing();
   const { data: isDeviceLargerThanLg } = useIsDeviceLargerThanLg();
 
   const { data: status, close, selectContents } = usePageAccessoriesModal();

+ 4 - 6
apps/app/src/client/components/PageComment/CommentEditor.tsx

@@ -18,10 +18,10 @@ import {
 
 import { uploadAttachments } from '~/client/services/upload-attachments';
 import { toastError } from '~/client/util/toastr';
+import { useCurrentUser } from '~/states/global';
 import { useCurrentPagePath } from '~/states/page';
-import {
-  useIsSlackConfigured, useAcceptedUploadFileType,
-} from '~/stores-universal/context';
+import { useIsSlackConfigured } from '~/states/server-configurations';
+import { useAcceptedUploadFileType } from '~/stores-universal/context';
 import { useNextThemes } from '~/stores-universal/use-next-themes';
 import { useSWRxPageComment } from '~/stores/comment';
 import {
@@ -40,8 +40,6 @@ import { SwitchingButtonGroup } from './SwitchingButtonGroup';
 import '@growi/editor/dist/style.css';
 import styles from './CommentEditor.module.scss';
 
-import { useCurrentUser } from '~/states/global';
-
 
 const logger = loggerFactory('growi:components:CommentEditor');
 
@@ -85,7 +83,7 @@ export const CommentEditor = (props: CommentEditorProps): JSX.Element => {
   const { data: isSlackEnabled, mutate: mutateIsSlackEnabled } = useIsSlackEnabled();
   const { data: acceptedUploadFileType } = useAcceptedUploadFileType();
   const { data: slackChannelsData } = useSWRxSlackChannels(currentPagePath);
-  const { data: isSlackConfigured } = useIsSlackConfigured();
+  const [isSlackConfigured] = useIsSlackConfigured();
   const { data: editorSettings } = useEditorSettings();
   const { mutate: mutateIsEnabledUnsavedWarning } = useIsEnabledUnsavedWarning();
   const {

+ 2 - 2
apps/app/src/client/components/PageComment/ReplyComments.tsx

@@ -6,7 +6,7 @@ import { Collapse } from 'reactstrap';
 
 import type { ICommentHasId, ICommentHasIdList } from '~/interfaces/comment';
 import type { RendererOptions } from '~/interfaces/renderer-options';
-import { useIsAllReplyShown } from '~/stores-universal/context';
+import { useIsAllReplyShown } from '~/states/server-configurations';
 
 
 import { Comment } from './Comment';
@@ -34,7 +34,7 @@ export const ReplyComments = (props: ReplycommentsProps): JSX.Element => {
     pageId, pagePath, deleteBtnClicked, onComment,
   } = props;
 
-  const { data: isAllReplyShown } = useIsAllReplyShown();
+  const [isAllReplyShown] = useIsAllReplyShown();
 
   const [isOlderRepliesShown, setIsOlderRepliesShown] = useState(false);
 

+ 5 - 6
apps/app/src/client/components/PageControls/PageControls.tsx

@@ -20,12 +20,11 @@ import { toastError } from '~/client/util/toastr';
 import OpenDefaultAiAssistantButton from '~/features/openai/client/components/AiAssistant/OpenDefaultAiAssistantButton';
 import { useIsGuestUser, useIsReadOnlyUser } from '~/states/context';
 import { useCurrentPagePath } from '~/states/page';
-import {
-  useIsSearchPage, useIsUsersHomepageDeletionEnabled,
-} from '~/stores-universal/context';
+import { useIsUsersHomepageDeletionEnabled } from '~/states/server-configurations';
 import {
   EditorMode, useEditorMode,
-} from '~/stores-universal/ui';
+} from '~/states/ui/editor';
+import { useIsSearchPage } from '~/stores-universal/context';
 import { useTagEditModal, type IPageForPageDuplicateModal } from '~/stores/modal';
 import {
   useIsDeviceLargerThanMd, usePageControlsX,
@@ -136,10 +135,10 @@ const PageControlsSubstance = (props: PageControlsSubstanceProps): JSX.Element =
 
   const [isGuestUser] = useIsGuestUser();
   const [isReadOnlyUser] = useIsReadOnlyUser();
-  const { data: editorMode } = useEditorMode();
+  const { editorMode } = useEditorMode();
   const { data: isDeviceLargerThanMd } = useIsDeviceLargerThanMd();
   const { data: isSearchPage } = useIsSearchPage();
-  const { data: isUsersHomepageDeletionEnabled } = useIsUsersHomepageDeletionEnabled();
+  const [isUsersHomepageDeletionEnabled] = useIsUsersHomepageDeletionEnabled();
   const [currentPagePath] = useCurrentPagePath();
 
   const isUsersHomepage = currentPagePath == null ? false : pagePathUtils.isUsersHomepage(currentPagePath);

+ 2 - 3
apps/app/src/client/components/PageCreateModal.tsx

@@ -19,10 +19,9 @@ import { useCreateTemplatePage } from '~/client/services/create-page';
 import { useCreatePage } from '~/client/services/create-page/use-create-page';
 import { useToastrOnError } from '~/client/services/use-toastr-on-error';
 import { useCurrentUser } from '~/states/global';
-import { useIsSearchServiceReachable } from '~/stores-universal/context';
+import { useIsSearchServiceReachable } from '~/states/server-configurations';
 import { usePageCreateModal } from '~/stores/modal';
 
-
 import PagePathAutoComplete from './PagePathAutoComplete';
 
 import styles from './PageCreateModal.module.scss';
@@ -43,7 +42,7 @@ const PageCreateModal: React.FC = () => {
   const { create } = useCreatePage();
   const { createTemplate } = useCreateTemplatePage();
 
-  const { data: isReachable } = useIsSearchServiceReachable();
+  const [isReachable] = useIsSearchServiceReachable();
   const pathname = pageCreateModalData?.path ?? '';
   const userHomepagePath = pagePathUtils.userHomepagePath(currentUser);
   const isCreatable = isCreatablePage(pathname) || isUsersHomepage(pathname);

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

@@ -11,7 +11,7 @@ import { debounce } from 'throttle-debounce';
 import { apiv3Get, apiv3Post } from '~/client/util/apiv3-client';
 import { toastError } from '~/client/util/toastr';
 import { useSiteUrl } from '~/states/global';
-import { useIsSearchServiceReachable } from '~/stores-universal/context';
+import { useIsSearchServiceReachable } from '~/states/server-configurations';
 import { usePageDuplicateModal } from '~/stores/modal';
 
 import DuplicatePathsTable from './DuplicatedPathsTable';
@@ -23,7 +23,7 @@ const PageDuplicateModal = (): JSX.Element => {
   const { t } = useTranslation();
 
   const [siteUrl] = useSiteUrl();
-  const { data: isReachable } = useIsSearchServiceReachable();
+  const [isReachable] = useIsSearchServiceReachable();
 
   const { data: duplicateModalData, close: closeDuplicateModal } = usePageDuplicateModal();
 

+ 8 - 8
apps/app/src/client/components/PageEditor/DrawioModal.tsx

@@ -12,7 +12,7 @@ import {
 } from 'reactstrap';
 
 import { replaceFocusedDrawioWithEditor, getMarkdownDrawioMxfile } from '~/client/components/PageEditor/markdown-drawio-util-for-editor';
-import { useRendererConfig } from '~/stores-universal/context';
+import { useRendererConfig } from '~/states/server-configurations';
 import { useDrawioModal } from '~/stores/modal';
 import { usePersonalSettings } from '~/stores/personal-settings';
 import loggerFactory from '~/utils/logger';
@@ -55,7 +55,7 @@ const drawioConfig: DrawioConfig = {
 
 
 export const DrawioModal = (): JSX.Element => {
-  const { data: rendererConfig } = useRendererConfig();
+  const [{ drawioUri }] = useRendererConfig();
   const { data: personalSettingsInfo } = usePersonalSettings({
     // make immutable
     revalidateIfStale: false,
@@ -72,13 +72,13 @@ export const DrawioModal = (): JSX.Element => {
   const isOpened = drawioModalData?.isOpened ?? false;
 
   const drawioUriWithParams = useMemo(() => {
-    if (rendererConfig == null) {
+    if (drawioUri === '') {
       return undefined;
     }
 
     let url;
     try {
-      url = new URL(rendererConfig.drawioUri);
+      url = new URL(drawioUri);
     }
     catch (err) {
       logger.debug(err);
@@ -93,10 +93,10 @@ export const DrawioModal = (): JSX.Element => {
     url.searchParams.append('configure', '1');
 
     return url;
-  }, [rendererConfig, personalSettingsInfo?.lang]);
+  }, [drawioUri, personalSettingsInfo?.lang]);
 
   const drawioCommunicationHelper = useMemo(() => {
-    if (rendererConfig == null) {
+    if (drawioUri === '') {
       return undefined;
     }
 
@@ -105,11 +105,11 @@ export const DrawioModal = (): JSX.Element => {
     } : drawioModalData?.onSave;
 
     return new DrawioCommunicationHelper(
-      rendererConfig.drawioUri,
+      drawioUri,
       drawioConfig,
       { onClose: isOpened ? closeDrawioModal : closeDrawioModalInEditor, onSave: save },
     );
-  }, [closeDrawioModal, closeDrawioModalInEditor, drawioModalData?.onSave, editor, isOpened, rendererConfig]);
+  }, [closeDrawioModal, closeDrawioModalInEditor, drawioModalData?.onSave, editor, isOpened, drawioUri]);
 
   const receiveMessageHandler = useCallback((event: MessageEvent) => {
     if (drawioModalData == null) {

+ 2 - 2
apps/app/src/client/components/PageEditor/EditorNavbarBottom/EditorNavbarBottom.tsx

@@ -2,8 +2,8 @@ import type { JSX } from 'react';
 
 import dynamic from 'next/dynamic';
 
+import { useIsAiEnabled } from '~/states/server-configurations';
 import { useDrawerOpened } from '~/states/ui/sidebar';
-import { useIsAiEnabled } from '~/stores-universal/context';
 
 import { EditorAssistantToggleButton } from './EditorAssistantToggleButton';
 
@@ -16,7 +16,7 @@ const SavePageControls = dynamic(() => import('./SavePageControls').then(mod =>
 const OptionsSelector = dynamic(() => import('./OptionsSelector').then(mod => mod.OptionsSelector), { ssr: false });
 
 export const EditorNavbarBottom = (): JSX.Element => {
-  const { data: isAiEnabled } = useIsAiEnabled();
+  const [isAiEnabled] = useIsAiEnabled();
   const [, setIsDrawerOpened] = useDrawerOpened();
 
   return (

+ 2 - 2
apps/app/src/client/components/PageEditor/EditorNavbarBottom/OptionsSelector.tsx

@@ -11,7 +11,7 @@ import {
   Dropdown, DropdownToggle, DropdownMenu, Input, FormGroup,
 } from 'reactstrap';
 
-import { useIsIndentSizeForced } from '~/stores-universal/context';
+import { useIsIndentSizeForced } from '~/states/server-configurations';
 import { useEditorSettings, useCurrentIndentSize } from '~/stores/editor';
 import {
   useIsDeviceLargerThanMd,
@@ -305,7 +305,7 @@ export const OptionsSelector = (): JSX.Element => {
   const [status, setStatus] = useState<OptionStatus>(OptionsStatus.Home);
   const { data: editorSettings } = useEditorSettings();
   const { data: currentIndentSize } = useCurrentIndentSize();
-  const { data: isIndentSizeForced } = useIsIndentSizeForced();
+  const [isIndentSizeForced] = useIsIndentSizeForced();
   const { data: isDeviceLargerThanMd } = useIsDeviceLargerThanMd();
 
   if (editorSettings == null || currentIndentSize == null || isIndentSizeForced == null) {

+ 8 - 7
apps/app/src/client/components/PageEditor/EditorNavbarBottom/SavePageControls.tsx

@@ -13,12 +13,13 @@ import {
   DropdownToggle, DropdownMenu, DropdownItem, Modal,
 } from 'reactstrap';
 
+import { useIsEditable } from '~/states/context';
 import { useCurrentPageData, useCurrentPagePath } from '~/states/page';
 import {
-  useIsEditable, useIsAclEnabled,
+  useIsAclEnabled,
   useIsSlackConfigured,
-} from '~/stores-universal/context';
-import { useEditorMode } from '~/stores-universal/ui';
+} from '~/states/server-configurations';
+import { useEditorMode } from '~/states/ui/editor';
 import { useWaitingSaveProcessing, useSWRxSlackChannels, useIsSlackEnabled } from '~/stores/editor';
 import { useIsDeviceLargerThanMd, useSelectedGrant } from '~/stores/ui';
 import loggerFactory from '~/utils/logger';
@@ -146,12 +147,12 @@ const SavePageButton = (props: {slackChannels: string, isSlackEnabled?: boolean,
 export const SavePageControls = (): JSX.Element | null => {
   const { t } = useTranslation('commons');
   const [currentPage] = useCurrentPageData();
-  const { data: isEditable } = useIsEditable();
-  const { data: isAclEnabled } = useIsAclEnabled();
+  const [isEditable] = useIsEditable();
+  const [isAclEnabled] = useIsAclEnabled();
 
-  const { data: editorMode } = useEditorMode();
+  const { editorMode } = useEditorMode();
   const [currentPagePath] = useCurrentPagePath();
-  const { data: isSlackConfigured } = useIsSlackConfigured();
+  const [isSlackConfigured] = useIsSlackConfigured();
   const { data: isSlackEnabled, mutate: mutateIsSlackEnabled } = useIsSlackEnabled();
   const { data: slackChannelsData } = useSWRxSlackChannels(currentPagePath);
   const { data: isDeviceLargerThanMd } = useIsDeviceLargerThanMd();

+ 14 - 12
apps/app/src/client/components/PageEditor/PageEditor.tsx

@@ -23,19 +23,24 @@ import { useUpdatePage, extractRemoteRevisionDataFromErrorObj } from '~/client/s
 import { uploadAttachments } from '~/client/services/upload-attachments';
 import { toastError, toastSuccess, toastWarning } from '~/client/util/toastr';
 import { useShouldExpandContent } from '~/services/layout/use-should-expand-content';
+import { useIsEditable } from '~/states/context';
+import { useCurrentPathname, useCurrentUser } from '~/states/global';
 import {
   useCurrentPagePath,
   useCurrentPageData,
   useCurrentPageId,
   usePageNotFound,
 } from '~/states/page';
+import { useTemplateBody } from '~/states/page/hooks';
 import {
   useDefaultIndentSize,
   useIsEnabledAttachTitleHeader,
-  useIsEditable, useIsIndentSizeForced,
+  useIsIndentSizeForced,
+} from '~/states/server-configurations';
+import { useEditorMode, EditorMode } from '~/states/ui/editor';
+import {
   useAcceptedUploadFileType, useIsEnableUnifiedMergeView,
 } from '~/stores-universal/context';
-import { EditorMode, useEditorMode } from '~/stores-universal/ui';
 import { useNextThemes } from '~/stores-universal/use-next-themes';
 import {
   useReservedNextCaretLine,
@@ -60,9 +65,6 @@ import { useScrollSync } from './ScrollSyncHelper';
 import { useConflictResolver, useConflictEffect, type ConflictHandler } from './conflict';
 
 import '@growi/editor/dist/style.css';
-import { useTemplateBody } from '~/states/page/hooks';
-import { useCurrentPathname, useCurrentUser } from '~/states/global';
-
 
 const logger = loggerFactory('growi:PageEditor');
 
@@ -103,15 +105,15 @@ export const PageEditorSubstance = (props: Props): JSX.Element => {
   const [currentPage] = useCurrentPageData();
   const { data: selectedGrant } = useSelectedGrant();
   const { data: editingMarkdown } = useEditingMarkdown();
-  const { data: isEnabledAttachTitleHeader } = useIsEnabledAttachTitleHeader();
+  const [isEnabledAttachTitleHeader] = useIsEnabledAttachTitleHeader();
   const [templateBody] = useTemplateBody();
-  const { data: isEditable } = useIsEditable();
+  const [isEditable] = useIsEditable();
   const { mutate: mutateWaitingSaveProcessing } = useWaitingSaveProcessing();
-  const { data: editorMode, mutate: mutateEditorMode } = useEditorMode();
+  const { editorMode, setEditorMode } = useEditorMode();
   const { data: isUntitledPage } = useIsUntitledPage();
-  const { data: isIndentSizeForced } = useIsIndentSizeForced();
+  const [isIndentSizeForced] = useIsIndentSizeForced();
   const { data: currentIndentSize, mutate: mutateCurrentIndentSize } = useCurrentIndentSize();
-  const { data: defaultIndentSize } = useDefaultIndentSize();
+  const [defaultIndentSize] = useDefaultIndentSize();
   const { data: acceptedUploadFileType } = useAcceptedUploadFileType();
   const { data: editorSettings } = useEditorSettings();
   const { mutate: mutateIsGrantNormalized } = useSWRxCurrentGrantData(currentPage?._id);
@@ -232,9 +234,9 @@ export const PageEditorSubstance = (props: Props): JSX.Element => {
       return;
     }
 
-    mutateEditorMode(EditorMode.View);
+    setEditorMode(EditorMode.View);
     updateStateAfterSave?.();
-  }, [codeMirrorEditor, currentRevisionId, isRevisionIdRequiredForPageUpdate, mutateEditorMode, onConflict, save, updateStateAfterSave]);
+  }, [codeMirrorEditor, currentRevisionId, isRevisionIdRequiredForPageUpdate, setEditorMode, onConflict, save, updateStateAfterSave]);
 
   const saveWithShortcut = useCallback(async() => {
     const markdown = codeMirrorEditor?.getDocString();

+ 2 - 2
apps/app/src/client/components/PageEditor/Preview.tsx

@@ -4,7 +4,7 @@ import { useSlidesByFrontmatter } from '@growi/presentation/dist/services';
 
 import RevisionRenderer from '~/components/PageView/RevisionRenderer';
 import type { RendererOptions } from '~/interfaces/renderer-options';
-import { useIsEnabledMarp } from '~/stores-universal/context';
+import { useRendererConfig } from '~/states/server-configurations';
 
 import { SlideRenderer } from '../Page/SlideRenderer';
 
@@ -30,7 +30,7 @@ const Preview = (props: Props): JSX.Element => {
     expandContentWidth,
   } = props;
 
-  const { data: isEnabledMarp } = useIsEnabledMarp();
+  const [{ isEnabledMarp }] = useRendererConfig();
   const isSlide = useSlidesByFrontmatter(markdown, isEnabledMarp);
 
   const fluidLayoutClass = expandContentWidth ? 'fluid-layout' : '';

+ 2 - 2
apps/app/src/client/components/PageEditor/conflict.tsx

@@ -11,7 +11,7 @@ import { useUpdateStateAfterSave } from '~/client/services/page-operation';
 import { toastSuccess } from '~/client/util/toastr';
 import { SocketEventName } from '~/interfaces/websocket';
 import { useCurrentPageData, useCurrentPageId } from '~/states/page';
-import { EditorMode, useEditorMode } from '~/stores-universal/ui';
+import { EditorMode, useEditorMode } from '~/states/ui/editor';
 import { usePageStatusAlert } from '~/stores/alert';
 import { useConflictDiffModal } from '~/stores/modal';
 import { type RemoteRevisionData, useSetRemoteLatestPageData } from '~/stores/remote-latest-page';
@@ -87,7 +87,7 @@ export const useConflictEffect = (): void => {
   const { open: openPageStatusAlert } = usePageStatusAlert();
   const { open: openConflictDiffModal } = useConflictDiffModal();
   const { data: socket } = useGlobalSocket();
-  const { data: editorMode } = useEditorMode();
+  const { editorMode } = useEditorMode();
 
   const conflictHandler = useCallback(() => {
     const onResolveConflict = () => {

+ 2 - 2
apps/app/src/client/components/PageHeader/PageTitleHeader.spec.tsx

@@ -8,7 +8,7 @@ import {
 import { mock } from 'vitest-mock-extended';
 
 
-import { EditorMode } from '~/stores-universal/ui';
+import { EditorMode } from '~/states/ui/editor';
 
 import { PageTitleHeader } from './PageTitleHeader';
 
@@ -20,7 +20,7 @@ const mocks = vi.hoisted(() => ({
 vi.mock('~/stores/ui', () => ({
   useIsUntitledPage: mocks.useIsUntitledPageMock,
 }));
-vi.mock('~/stores-universal/ui', async importOriginal => ({
+vi.mock('~/states/ui/editor', async importOriginal => ({
   ...await importOriginal(),
   useEditorMode: mocks.useEditorModeMock,
 }));

+ 2 - 2
apps/app/src/client/components/PageHeader/PageTitleHeader.tsx

@@ -13,7 +13,7 @@ import { useTranslation } from 'next-i18next';
 
 import type { InputValidationResult } from '~/client/util/use-input-validator';
 import { ValidationTarget, useInputValidator } from '~/client/util/use-input-validator';
-import { EditorMode, useEditorMode } from '~/stores-universal/ui';
+import { EditorMode, useEditorMode } from '~/states/ui/editor';
 import { useIsUntitledPage } from '~/stores/ui';
 
 import { CopyDropdown } from '../Common/CopyDropdown';
@@ -52,7 +52,7 @@ export const PageTitleHeader = (props: Props): JSX.Element => {
 
   const editedPageTitle = nodePath.basename(editedPagePath);
 
-  const { data: editorMode } = useEditorMode();
+  const { editorMode } = useEditorMode();
   const { data: isUntitledPage } = useIsUntitledPage();
 
   const changeHandler = useCallback(async(e: ChangeEvent<HTMLInputElement>) => {

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

@@ -11,7 +11,7 @@ import {
 } from 'reactstrap';
 
 import { useCurrentPageData } from '~/states/page';
-import { useIsEnabledMarp } from '~/stores-universal/context';
+import { useRendererConfig } from '~/states/server-configurations';
 import { useNextThemes } from '~/stores-universal/use-next-themes';
 import { usePagePresentationModal } from '~/stores/modal';
 import { usePresentationViewOptions } from '~/stores/renderer';
@@ -41,7 +41,7 @@ const PagePresentationModal = (): JSX.Element => {
   const [currentPage] = useCurrentPageData();
   const { data: rendererOptions, isLoading } = usePresentationViewOptions();
 
-  const { data: isEnabledMarp } = useIsEnabledMarp();
+  const [{ isEnabledMarp }] = useRendererConfig();
 
   const markdown = currentPage?.revision?.body;
 

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

@@ -13,7 +13,7 @@ import { debounce } from 'throttle-debounce';
 import { apiv3Get, apiv3Put } from '~/client/util/apiv3-client';
 import { toastError } from '~/client/util/toastr';
 import { useSiteUrl } from '~/states/global';
-import { useIsSearchServiceReachable } from '~/stores-universal/context';
+import { useIsSearchServiceReachable } from '~/states/server-configurations';
 import { usePageRenameModal } from '~/stores/modal';
 import { useSWRxPageInfo } from '~/stores/page';
 
@@ -32,7 +32,7 @@ const PageRenameModal = (): JSX.Element => {
   const { isUsersHomepage } = pagePathUtils;
   const [siteUrl] = useSiteUrl();
   const { data: renameModalData, close: closeRenameModal } = usePageRenameModal();
-  const { data: isReachable } = useIsSearchServiceReachable();
+  const [isReachable] = useIsSearchServiceReachable();
 
   const isOpened = renameModalData?.isOpened ?? false;
   const page = renameModalData?.page;

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

@@ -9,7 +9,7 @@ import dynamic from 'next/dynamic';
 import { scroller } from 'react-scroll';
 
 import { useIsGuestUser, useIsReadOnlyUser } from '~/states/context';
-import { useShowPageSideAuthors } from '~/stores-universal/context';
+import { useShowPageSideAuthors } from '~/states/server-configurations';
 import { useDescendantsPageListModal, useTagEditModal } from '~/stores/modal';
 import { useSWRxPageInfo, useSWRxTagsInfo } from '~/stores/page';
 import { useIsAbleToShowTagLabel } from '~/stores/ui';
@@ -88,7 +88,7 @@ export const PageSideContents = (props: PageSideContentsProps): JSX.Element => {
   const tagsRef = useRef<HTMLDivElement>(null);
 
   const { data: pageInfo } = useSWRxPageInfo(page._id);
-  const { data: showPageSideAuthors } = useShowPageSideAuthors();
+  const [showPageSideAuthors] = useShowPageSideAuthors();
 
   const {
     creator, lastUpdateUser, createdAt, updatedAt,

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

@@ -4,7 +4,7 @@ import { useTranslation } from 'next-i18next';
 
 import { useIsGuestUser, useIsReadOnlyUser } from '~/states/context';
 import { useCurrentPageData, useRemoteRevisionId } from '~/states/page';
-import { useEditorMode } from '~/stores-universal/ui';
+import { useEditorMode } from '~/states/ui/editor';
 import { usePageStatusAlert } from '~/stores/alert';
 import { useRemoteRevisionLastUpdateUser } from '~/stores/remote-latest-page';
 
@@ -15,7 +15,7 @@ import styles from './PageStatusAlert.module.scss';
 export const PageStatusAlert = (): JSX.Element => {
   const { t } = useTranslation();
 
-  const { data: editorMode } = useEditorMode();
+  const { editorMode } = useEditorMode();
   const [isGuestUser] = useIsGuestUser();
   const [isReadOnlyUser] = useIsReadOnlyUser();
   const { data: pageStatusAlertData } = usePageStatusAlert();

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

@@ -17,7 +17,7 @@ import type { V5MigrationStatus } from '~/interfaces/page-listing-results';
 import type { IFormattedSearchResult } from '~/interfaces/search';
 import type { PageMigrationErrorData } from '~/interfaces/websocket';
 import { SocketEventName } from '~/interfaces/websocket';
-import { useIsAdmin } from '~/stores-universal/context';
+import { useIsAdmin } from '~/states/context';
 import type { ILegacyPrivatePage } from '~/stores/modal';
 import { usePrivateLegacyPagesMigrationModal } from '~/stores/modal';
 import { mutatePageTree, useSWRxV5MigrationStatus } from '~/stores/page-listing';
@@ -194,7 +194,7 @@ ConvertByPathModal.displayName = 'ConvertByPathModal';
 const PrivateLegacyPages = (): JSX.Element => {
   const { t } = useTranslation();
 
-  const { data: isAdmin } = useIsAdmin();
+  const [isAdmin] = useIsAdmin();
 
   const [keyword, setKeyword] = useState<string>(initQ);
   const [offset, setOffset] = useState<number>(0);

+ 3 - 3
apps/app/src/client/components/SearchPage/SearchPageBase.tsx

@@ -14,7 +14,7 @@ import type { OnDeletedFunction } from '~/interfaces/ui';
 import { useIsGuestUser, useIsReadOnlyUser } from '~/states/context';
 import {
   useIsSearchServiceConfigured, useIsSearchServiceReachable,
-} from '~/stores-universal/context';
+} from '~/states/server-configurations';
 import { usePageDeleteModal } from '~/stores/modal';
 import { mutatePageTree, mutateRecentlyUpdated } from '~/stores/page-listing';
 
@@ -68,8 +68,8 @@ const SearchPageBaseSubstance: ForwardRefRenderFunction<ISelectableAll & IReturn
 
   const [isGuestUser] = useIsGuestUser();
   const [isReadOnlyUser] = useIsReadOnlyUser();
-  const { data: isSearchServiceConfigured } = useIsSearchServiceConfigured();
-  const { data: isSearchServiceReachable } = useIsSearchServiceReachable();
+  const [isSearchServiceConfigured] = useIsSearchServiceConfigured();
+  const [isSearchServiceReachable] = useIsSearchServiceReachable();
 
   const [selectedPageIdsByCheckboxes] = useState<Set<string>>(new Set());
   // const [allPageIds] = useState<Set<string>>(new Set());

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

@@ -17,7 +17,7 @@ import {
   useCurrentProductNavWidth,
 } from '~/states/ui/sidebar';
 import { useIsSearchPage } from '~/stores-universal/context';
-import { EditorMode, useEditorMode } from '~/stores-universal/ui';
+import { EditorMode, useEditorMode } from '~/states/ui/editor';
 import {
   useSidebarScrollerRef,
   useIsDeviceLargerThanMd,
@@ -233,7 +233,7 @@ export const Sidebar = (): JSX.Element => {
   } = useSidebarMode();
 
   const { data: isSearchPage } = useIsSearchPage();
-  const { data: editorMode } = useEditorMode();
+  const { editorMode } = useEditorMode();
   const { data: isMdSize } = useIsDeviceLargerThanMd();
   const [isXlSize] = useDeviceLargerThanXl();
 

+ 2 - 3
apps/app/src/client/components/Sidebar/SidebarContents.tsx

@@ -4,8 +4,7 @@ import { AiAssistant } from '~/features/openai/client/components/AiAssistant/Sid
 import { SidebarContentsType } from '~/interfaces/ui';
 import { useIsGuestUser } from '~/states/context';
 import { useSidebarMode, useCollapsedContentsOpened, useCurrentSidebarContents } from '~/states/ui/sidebar';
-import { useIsAiEnabled } from '~/stores-universal/context';
-
+import { useIsAiEnabled } from '~/states/server-configurations';
 
 import { Bookmarks } from './Bookmarks';
 import { CustomSidebar } from './Custom';
@@ -20,7 +19,7 @@ import styles from './SidebarContents.module.scss';
 export const SidebarContents = memo(() => {
   const { isCollapsedMode } = useSidebarMode();
   const [isGuestUser] = useIsGuestUser();
-  const { data: isAiEnabled } = useIsAiEnabled();
+  const [isAiEnabled] = useIsAiEnabled();
 
   const [isCollapsedContentsOpened] = useCollapsedContentsOpened();
   const [currentSidebarContents] = useCurrentSidebarContents();

+ 2 - 2
apps/app/src/client/components/Sidebar/SidebarNav/PrimaryItems.tsx

@@ -5,7 +5,7 @@ import dynamic from 'next/dynamic';
 import { SidebarContentsType } from '~/interfaces/ui';
 import { useIsGuestUser } from '~/states/context';
 import { useSidebarMode } from '~/states/ui/sidebar';
-import { useIsAiEnabled } from '~/stores-universal/context';
+import { useIsAiEnabled } from '~/states/server-configurations';
 
 import { PrimaryItem } from './PrimaryItem';
 
@@ -25,7 +25,7 @@ export const PrimaryItems = memo((props: Props) => {
   const { onItemHover } = props;
 
   const { sidebarMode } = useSidebarMode();
-  const { data: isAiEnabled } = useIsAiEnabled();
+  const [isAiEnabled] = useIsAiEnabled();
   const [isGuestUser] = useIsGuestUser();
 
   if (sidebarMode == null) {

+ 5 - 5
apps/app/src/client/services/create-page/use-create-page.tsx

@@ -7,7 +7,7 @@ import { exist, getIsNonUserRelatedGroupsGranted } from '~/client/services/page-
 import { toastWarning } from '~/client/util/toastr';
 import type { IApiv3PageCreateParams } from '~/interfaces/apiv3';
 import { useCurrentPagePath } from '~/states/page';
-import { EditorMode, useEditorMode } from '~/stores-universal/ui';
+import { useEditorMode, EditorMode } from '~/states/ui/editor';
 import { useGrantedGroupsInheritanceSelectModal } from '~/stores/modal';
 import { useIsUntitledPage } from '~/stores/ui';
 
@@ -51,7 +51,7 @@ export const useCreatePage: UseCreatePage = () => {
   const { t } = useTranslation();
 
   const [currentPagePath] = useCurrentPagePath();
-  const { mutate: mutateEditorMode } = useEditorMode();
+  const { setEditorMode } = useEditorMode();
   const { mutate: mutateIsUntitledPage } = useIsUntitledPage();
   const { open: openGrantedGroupsInheritanceSelectModal, close: closeGrantedGroupsInheritanceSelectModal } = useGrantedGroupsInheritanceSelectModal();
 
@@ -77,7 +77,7 @@ export const useCreatePage: UseCreatePage = () => {
             if (pagePath !== currentPagePath) {
               await router.push(`${pagePath}#edit`);
             }
-            mutateEditorMode(EditorMode.Editor);
+            setEditorMode(EditorMode.Editor);
           }
           else {
             toastWarning(t('duplicated_page_alert.same_page_name_exists', { pageName: pagePath }));
@@ -106,7 +106,7 @@ export const useCreatePage: UseCreatePage = () => {
 
         if (!skipTransition) {
           await router.push(`/${response.page._id}#edit`);
-          mutateEditorMode(EditorMode.Editor);
+          setEditorMode(EditorMode.Editor);
         }
 
         if (params.path == null) {
@@ -135,7 +135,7 @@ export const useCreatePage: UseCreatePage = () => {
     }
 
     await _create();
-  }, [currentPagePath, mutateEditorMode, router, t, closeGrantedGroupsInheritanceSelectModal, mutateIsUntitledPage, openGrantedGroupsInheritanceSelectModal]);
+  }, [currentPagePath, setEditorMode, router, t, closeGrantedGroupsInheritanceSelectModal, mutateIsUntitledPage, openGrantedGroupsInheritanceSelectModal]);
 
   return {
     isCreating,

+ 6 - 6
apps/app/src/client/services/side-effects/hash-changed.ts

@@ -2,8 +2,8 @@ import { useCallback, useEffect } from 'react';
 
 import { useRouter } from 'next/router';
 
-import { useIsEditable } from '~/stores-universal/context';
-import { useEditorMode, determineEditorModeByHash } from '~/stores-universal/ui';
+import { useIsEditable } from '~/states/context';
+import { useEditorMode, determineEditorModeByHash } from '~/states/ui/editor';
 
 /**
  * Change editorMode by browser forward/back operation
@@ -11,16 +11,16 @@ import { useEditorMode, determineEditorModeByHash } from '~/stores-universal/ui'
 export const useHashChangedEffect = (): void => {
   const router = useRouter();
 
-  const { data: isEditable } = useIsEditable();
-  const { data: editorMode, mutate: mutateEditorMode } = useEditorMode();
+  const [isEditable] = useIsEditable();
+  const { editorMode, setEditorMode } = useEditorMode();
 
   const hashchangeHandler = useCallback(() => {
     const newEditorMode = determineEditorModeByHash();
 
     if (editorMode !== newEditorMode) {
-      mutateEditorMode(newEditorMode);
+      setEditorMode(newEditorMode);
     }
-  }, [editorMode, mutateEditorMode]);
+  }, [editorMode, setEditorMode]);
 
   // setup effect
   useEffect(() => {

+ 2 - 2
apps/app/src/client/services/side-effects/page-updated.ts

@@ -4,7 +4,7 @@ import { useGlobalSocket } from '@growi/core/dist/swr';
 
 import { SocketEventName } from '~/interfaces/websocket';
 import { useCurrentPageData, useFetchCurrentPage } from '~/states/page';
-import { useEditorMode, EditorMode } from '~/stores-universal/ui';
+import { useEditorMode, EditorMode } from '~/states/ui/editor';
 import { usePageStatusAlert } from '~/stores/alert';
 import { useSetRemoteLatestPageData, type RemoteRevisionData } from '~/stores/remote-latest-page';
 
@@ -14,7 +14,7 @@ export const usePageUpdatedEffect = (): void => {
   const { setRemoteLatestPageData } = useSetRemoteLatestPageData();
 
   const { data: socket } = useGlobalSocket();
-  const { data: editorMode } = useEditorMode();
+  const { editorMode } = useEditorMode();
   const [currentPage] = useCurrentPageData();
   const { fetchCurrentPage } = useFetchCurrentPage();
   const { open: openPageStatusAlert, close: closePageStatusAlert } = usePageStatusAlert();

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

@@ -3,13 +3,13 @@ import type { JSX } from 'react';
 import { useTranslation } from 'react-i18next';
 
 import { useCurrentPageData } from '~/states/page';
-import { useElasticsearchMaxBodyLengthToIndex } from '~/stores-universal/context';
+import { useElasticsearchMaxBodyLengthToIndex } from '~/states/server-configurations';
 
 
 export const FullTextSearchNotCoverAlert = (): JSX.Element => {
   const { t } = useTranslation();
 
-  const { data: elasticsearchMaxBodyLengthToIndex } = useElasticsearchMaxBodyLengthToIndex();
+  const [elasticsearchMaxBodyLengthToIndex] = useElasticsearchMaxBodyLengthToIndex();
   const [data] = useCurrentPageData();
 
   const markdownLength = data?.revision?.body?.length;

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

@@ -5,13 +5,13 @@ import { useTranslation } from 'next-i18next';
 
 
 import { useCurrentPageData } from '~/states/page';
-import { useIsEnabledStaleNotification } from '~/stores-universal/context';
+import { useIsEnabledStaleNotification } from '~/states/server-configurations';
 import { useSWRxPageInfo } from '~/stores/page';
 
 
 export const PageStaleAlert = ():JSX.Element => {
   const { t } = useTranslation();
-  const { data: isEnabledStaleNotification } = useIsEnabledStaleNotification();
+  const [isEnabledStaleNotification] = useIsEnabledStaleNotification();
 
   // Todo: determine if it should fetch or not like useSWRxPageInfo below after https://redmine.weseek.co.jp/issues/96788
   const [pageData] = useCurrentPageData();

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

@@ -1,5 +1,5 @@
 import React, {
-  useEffect, useMemo, useRef, useState, type JSX,
+  useEffect, useMemo, useRef, type JSX,
 } from 'react';
 
 import { isUsersHomepage } from '@growi/core/dist/utils/page-path-utils';
@@ -10,10 +10,8 @@ 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 { useIsForbidden, useIsIdenticalPath, useIsNotCreatable } from '~/states/context';
+import { useCurrentPageData, useCurrentPageId, usePageNotFound } from '~/states/page';
 import { useViewOptions } from '~/stores/renderer';
 
 import { UserInfo } from '../User/UserInfo';
@@ -50,9 +48,10 @@ export const PageView = (props: Props): JSX.Element => {
     pagePath, rendererConfig, className,
   } = props;
 
-  const { data: isIdenticalPathPage } = useIsIdenticalPath();
-  const { data: isForbidden } = useIsForbidden();
-  const { data: isNotCreatable } = useIsNotCreatable();
+  const [currentPageId] = useCurrentPageId();
+  const [isIdenticalPathPage] = useIsIdenticalPath();
+  const [isForbidden] = useIsForbidden();
+  const [isNotCreatable] = useIsNotCreatable();
   const [isNotFoundMeta] = usePageNotFound();
 
   const [page] = useCurrentPageData();

+ 3 - 3
apps/app/src/components/Script/DrawioViewerScript/use-viewer-min-js-url.spec.ts

@@ -1,6 +1,6 @@
-import { useViewerMinJsUrl } from './use-viewer-min-js-url';
+import { generateViewerMinJsUrl } from './use-viewer-min-js-url';
 
-describe('useViewerMinJsUrl', () => {
+describe('generateViewerMinJsUrl', () => {
   it.each`
     drawioUri                                     | expected
     ${'http://localhost:8080'}                    | ${'http://localhost:8080/js/viewer-static.min.js'}
@@ -9,7 +9,7 @@ describe('useViewerMinJsUrl', () => {
     ${'http://example.com/?offline=1&https=0'}    | ${'http://example.com/js/viewer-static.min.js?offline=1&https=0'}
   `('should return the expected URL "$expected" when drawioUri is "$drawioUrk"', ({ drawioUri, expected }: {drawioUri: string, expected: string}) => {
     // Act
-    const url = useViewerMinJsUrl(drawioUri);
+    const url = generateViewerMinJsUrl(drawioUri);
 
     // Assert
     expect(url).toBe(expected);

+ 2 - 2
apps/app/src/features/external-user-group/client/components/ExternalUserGroup/ExternalUserGroupManagement.tsx

@@ -14,7 +14,7 @@ import { apiv3Delete, apiv3Put } from '~/client/util/apiv3-client';
 import { toastError, toastSuccess } from '~/client/util/toastr';
 import type { IExternalUserGroupHasId } from '~/features/external-user-group/interfaces/external-user-group';
 import type { PageActionOnGroupDelete } from '~/interfaces/user-group';
-import { useIsAclEnabled } from '~/stores-universal/context';
+import { useIsAclEnabled } from '~/states/server-configurations';
 import { useSWRxUserGroupList } from '~/stores/user-group';
 
 import { useSWRxChildExternalUserGroupList, useSWRxExternalUserGroupList, useSWRxExternalUserGroupRelationList } from '../../stores/external-user-group';
@@ -40,7 +40,7 @@ export const ExternalGroupManagement: FC = () => {
   const { data: childExternalUserGroupsList } = useSWRxChildExternalUserGroupList(externalUserGroupIds);
   const childExternalUserGroups = childExternalUserGroupsList?.childUserGroups != null ? childExternalUserGroupsList.childUserGroups : [];
 
-  const { data: isAclEnabled } = useIsAclEnabled();
+  const [isAclEnabled] = useIsAclEnabled();
 
   const [activeTab, setActiveTab] = useState('ldap');
   const [activeComponents, setActiveComponents] = useState(new Set(['ldap']));

+ 2 - 2
apps/app/src/features/page-bulk-export/client/components/PageBulkExportSelectModal.tsx

@@ -9,13 +9,13 @@ import { toastError, toastSuccess } from '~/client/util/toastr';
 import { usePageBulkExportSelectModal } from '~/features/page-bulk-export/client/stores/modal';
 import { PageBulkExportFormat } from '~/features/page-bulk-export/interfaces/page-bulk-export';
 import { useCurrentPagePath } from '~/states/page';
-import { useIsPdfBulkExportEnabled } from '~/stores-universal/context';
+import { useIsPdfBulkExportEnabled } from '~/states/server-configurations';
 
 const PageBulkExportSelectModal = (): JSX.Element => {
   const { t } = useTranslation();
   const { data: status, close } = usePageBulkExportSelectModal();
   const [currentPagePath] = useCurrentPagePath();
-  const { data: isPdfBulkExportEnabled } = useIsPdfBulkExportEnabled();
+  const [isPdfBulkExportEnabled] = useIsPdfBulkExportEnabled();
 
   const [isRestartModalOpened, setIsRestartModalOpened] = useState(false);
   const [formatMemoForRestart, setFormatMemoForRestart] = useState<PageBulkExportFormat | undefined>(undefined);

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

@@ -30,7 +30,7 @@ import { useHydratePageAtoms } from '~/states/page/hydrate';
 import {
   useDisableLinkSharing,
   useRendererConfig,
-} from '~/states/server-configurations/server-configurations';
+} from '~/states/server-configurations';
 import {
   useIsSharedUser,
   useIsSearchPage,

+ 1 - 1
apps/app/src/pages/maintenance.page.tsx

@@ -25,7 +25,7 @@ const MaintenancePage: NextPage<CommonProps> = (props: Props) => {
       <div className="container">
         <div className="row justify-content-md-center">
           <div className="col-md-6 mt-5">
-            <Maintenance currentUser={props.currentUser} />
+            <Maintenance />
           </div>
         </div>
       </div>

+ 2 - 2
apps/app/src/services/layout/use-should-expand-content.ts

@@ -1,9 +1,9 @@
 import type { IPage, IPagePopulatedToShowRevision } from '@growi/core';
 
-import { useIsContainerFluid } from '~/stores-universal/context';
+import { useIsContainerFluid } from '~/states/server-configurations';
 
 const useDetermineExpandContent = (expandContentWidth?: boolean | null): boolean => {
-  const { data: dataIsContainerFluid } = useIsContainerFluid();
+  const [dataIsContainerFluid] = useIsContainerFluid();
 
   const isContainerFluidDefault = dataIsContainerFluid;
   return expandContentWidth ?? isContainerFluidDefault ?? false;

+ 2 - 1
apps/app/src/states/server-configurations/hydrate.ts

@@ -1,6 +1,7 @@
 import { useHydrateAtoms } from 'jotai/utils';
 
 import type { RendererConfig } from '~/interfaces/services/renderer';
+
 import {
   aiEnabledAtom,
   limitLearnablePageCountPerAssistantAtom,
@@ -27,7 +28,7 @@ import {
   isPdfBulkExportEnabledAtom,
   isLocalAccountRegistrationEnabledAtom,
   rendererConfigAtom,
-} from '~/states/server-configurations/server-configurations';
+} from './server-configurations';
 
 /**
  * Type for server configuration initial props

+ 1 - 0
apps/app/src/states/server-configurations/index.ts

@@ -0,0 +1 @@
+export * from './server-configurations';

+ 2 - 2
apps/app/src/states/ui/sidebar.ts

@@ -2,10 +2,10 @@ import { atom, useAtom } from 'jotai';
 
 import { scheduleToPut } from '~/client/services/user-ui-settings';
 import { SidebarContentsType, SidebarMode } from '~/interfaces/ui';
-import { EditorMode } from '~/stores-universal/ui';
+import { EditorMode } from '~/states/ui/editor';
+import { editorModeAtom } from '~/states/ui/editor/atoms'; // import the atom directly
 
 import { isDeviceLargerThanXlAtom } from './device';
-import { editorModeAtom } from './editor';
 import type { UseAtom } from './helper';
 
 

+ 1 - 1
apps/app/src/stores-universal/context.tsx

@@ -6,7 +6,7 @@ import type { SWRResponse } from 'swr';
 import useSWRImmutable from 'swr/immutable';
 
 import type { SupportedActionType } from '~/interfaces/activity';
-import { useIsUploadEnabled, useIsUploadAllFileAllowed } from '~/states/server-configurations/server-configurations';
+import { useIsUploadEnabled, useIsUploadAllFileAllowed } from '~/states/server-configurations';
 
 import { useContextSWR } from './use-context-swr';
 

+ 0 - 106
apps/app/src/stores-universal/ui.tsx

@@ -1,106 +0,0 @@
-/** **********************************************************
- *                          Unions
- *********************************************************** */
-
-import { useCallback } from 'react';
-
-import type { SWRResponseWithUtils } from '@growi/core/dist/swr';
-import { isServer } from '@growi/core/dist/utils';
-import useSWRImmutable from 'swr/immutable';
-
-import { usePageNotFound } from '~/states/page';
-
-import { useIsEditable } from './context';
-
-
-export const EditorMode = {
-  View: 'view',
-  Editor: 'editor',
-} as const;
-export type EditorMode = typeof EditorMode[keyof typeof EditorMode];
-
-const getClassNamesByEditorMode = (editorMode: EditorMode | undefined): string[] => {
-  const classNames: string[] = [];
-  switch (editorMode) {
-    case EditorMode.Editor:
-      classNames.push('editing', 'builtin-editor');
-      break;
-  }
-
-  return classNames;
-};
-
-export const EditorModeHash = {
-  View: '',
-  Edit: '#edit',
-} as const;
-export type EditorModeHash = typeof EditorModeHash[keyof typeof EditorModeHash];
-
-const updateHashByEditorMode = (newEditorMode: EditorMode) => {
-  const { pathname, search } = window.location;
-
-  switch (newEditorMode) {
-    case EditorMode.View:
-      window.history.replaceState(null, '', `${pathname}${search}${EditorModeHash.View}`);
-      break;
-    case EditorMode.Editor:
-      window.history.replaceState(null, '', `${pathname}${search}${EditorModeHash.Edit}`);
-      break;
-  }
-};
-
-export const determineEditorModeByHash = (): EditorMode => {
-  if (isServer()) {
-    return EditorMode.View;
-  }
-
-  const { hash } = window.location;
-
-  switch (hash) {
-    case EditorModeHash.Edit:
-      return EditorMode.Editor;
-    default:
-      return EditorMode.View;
-  }
-};
-
-type EditorModeUtils = {
-  getClassNamesByEditorMode: () => string[],
-}
-
-export const useEditorMode = (): SWRResponseWithUtils<EditorModeUtils, EditorMode> => {
-  const { data: _isEditable } = useIsEditable();
-  const [isNotFound] = usePageNotFound();
-
-  const editorModeByHash = determineEditorModeByHash();
-
-  const isLoading = _isEditable === undefined;
-  const isEditable = !isLoading && _isEditable;
-  const preventModeEditor = !isEditable || isNotFound === undefined || isNotFound === true;
-  const initialData = preventModeEditor ? EditorMode.View : editorModeByHash;
-
-  const swrResponse = useSWRImmutable(
-    isLoading ? null : ['editorMode', isEditable, preventModeEditor],
-    null,
-    { fallbackData: initialData },
-  );
-
-  // construct overriding mutate method
-  const mutateOriginal = swrResponse.mutate;
-  const mutate = useCallback((editorMode: EditorMode, shouldRevalidate?: boolean) => {
-    if (preventModeEditor) {
-      return Promise.resolve(EditorMode.View); // fixed if not editable
-    }
-    updateHashByEditorMode(editorMode);
-    return mutateOriginal(editorMode, shouldRevalidate);
-  }, [preventModeEditor, mutateOriginal]);
-
-  const getClassNames = useCallback(() => {
-    return getClassNamesByEditorMode(swrResponse.data);
-  }, [swrResponse.data]);
-
-  return Object.assign(swrResponse, {
-    mutate,
-    getClassNamesByEditorMode: getClassNames,
-  });
-};

+ 1 - 1
apps/app/src/stores/alert.tsx

@@ -3,7 +3,7 @@ import { useCallback } from 'react';
 import { useSWRStatic } from '@growi/core/dist/swr';
 import type { SWRResponse } from 'swr';
 
-import type { EditorMode } from '../stores-universal/ui';
+import type { EditorMode } from '../states/ui/editor';
 
 /*
 * PageStatusAlert

+ 2 - 3
apps/app/src/stores/editor.tsx

@@ -11,9 +11,8 @@ import { apiv3Get, apiv3Put } from '~/client/util/apiv3-client';
 import type { SlackChannels } from '~/interfaces/user-trigger-notification';
 import { useIsGuestUser, useIsReadOnlyUser } from '~/states/context';
 import { useCurrentUser } from '~/states/global';
-import { useDefaultIndentSize } from '~/stores-universal/context';
+import { useDefaultIndentSize } from '~/states/server-configurations';
 
-// import { localStorageMiddleware } from './middlewares/sync-to-storage';
 import { useSWRxTagsInfo } from './page';
 
 
@@ -67,7 +66,7 @@ export const useEditorSettings = (): SWRResponseWithUtils<EditorSettingsOperatio
 };
 
 export const useCurrentIndentSize = (): SWRResponse<number, Error> => {
-  const { data: defaultIndentSize } = useDefaultIndentSize();
+  const [defaultIndentSize] = useDefaultIndentSize();
   return useSWRStatic<number, Error>(
     defaultIndentSize == null ? null : 'currentIndentSize',
     undefined,

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

@@ -7,7 +7,7 @@ import { getGrowiFacade } from '~/features/growi-plugin/client/utils/growi-facad
 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 { useRendererConfig } from '~/states/server-configurations';
 import { useNextThemes } from '~/stores-universal/use-next-themes';
 import loggerFactory from '~/utils/logger';
 
@@ -16,7 +16,7 @@ import { useCurrentPageTocNode } from './ui';
 const logger = loggerFactory('growi:cli:services:renderer');
 
 const useRendererConfigExt = (): RendererConfigExt | null => {
-  const { data: rendererConfig } = useRendererConfig();
+  const [rendererConfig] = useRendererConfig();
   const { isDarkMode } = useNextThemes();
 
   return rendererConfig == null ? null : {

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

@@ -17,15 +17,15 @@ import useSWRImmutable from 'swr/immutable';
 
 import type { IPageSelectedGrant } from '~/interfaces/page';
 import type { UpdateDescCountData } from '~/interfaces/websocket';
-import { useIsReadOnlyUser } from '~/states/context';
+import { useIsEditable, useIsIdenticalPath, useIsReadOnlyUser } from '~/states/context';
 import { useCurrentUser } from '~/states/global';
 import {
   usePageNotFound, useCurrentPagePath, useIsTrashPage, useCurrentPageId,
 } from '~/states/page';
+import { EditorMode, useEditorMode } from '~/states/ui/editor';
 import {
-  useIsEditable, useIsSharedUser, useIsIdenticalPath, useShareLinkId,
+  useIsSharedUser, useShareLinkId,
 } from '~/stores-universal/context';
-import { EditorMode, useEditorMode } from '~/stores-universal/ui';
 import loggerFactory from '~/utils/logger';
 
 import { useStaticSWR } from './use-static-swr';
@@ -277,8 +277,8 @@ export const useIsAbleToShowTagLabel = (): SWRResponse<boolean, Error> => {
   const [pageId] = useCurrentPageId();
   const [isNotFound] = usePageNotFound();
   const [currentPagePath] = useCurrentPagePath();
-  const { data: isIdenticalPath } = useIsIdenticalPath();
-  const { data: editorMode } = useEditorMode();
+  const [isIdenticalPath] = useIsIdenticalPath();
+  const { editorMode } = useEditorMode();
   const { data: shareLinkId } = useShareLinkId();
 
   const includesUndefined = [currentPagePath, isIdenticalPath, isNotFound, editorMode].some(v => v === undefined);
@@ -295,7 +295,7 @@ export const useIsAbleToShowTagLabel = (): SWRResponse<boolean, Error> => {
 
 export const useIsAbleToChangeEditorMode = (): SWRResponse<boolean, Error> => {
   const key = 'isAbleToChangeEditorMode';
-  const { data: isEditable } = useIsEditable();
+  const [isEditable] = useIsEditable();
   const { data: isSharedUser } = useIsSharedUser();
 
   const includesUndefined = [isEditable, isSharedUser].some(v => v === undefined);