Просмотр исходного кода

tidy up universal stores and services

Yuki Takei 1 год назад
Родитель
Сommit
60ad7f944f

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

@@ -6,9 +6,10 @@ import { useTranslation } from 'react-i18next';
 import { exist, getIsNonUserRelatedGroupsGranted } from '~/client/services/page-operation';
 import { toastWarning } from '~/client/util/toastr';
 import type { IApiv3PageCreateParams } from '~/interfaces/apiv3';
+import { EditorMode, useEditorMode } from '~/stores-universal/ui';
 import { useGrantedGroupsInheritanceSelectModal } from '~/stores/modal';
 import { useCurrentPagePath } from '~/stores/page';
-import { EditorMode, useEditorMode, useIsUntitledPage } from '~/stores/ui';
+import { useIsUntitledPage } from '~/stores/ui';
 
 import { createPage } from './create-page';
 

+ 1 - 1
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 { useEditorMode, determineEditorModeByHash } from '~/stores-universal/ui';
 import { useIsEditable } from '~/stores/context';
-import { useEditorMode, determineEditorModeByHash } from '~/stores/ui';
 
 /**
  * Change editorMode by browser forward/back operation

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

@@ -3,10 +3,10 @@ import { useCallback, useEffect } from 'react';
 import { useGlobalSocket } from '@growi/core/dist/swr';
 
 import { SocketEventName } from '~/interfaces/websocket';
+import { useEditorMode, EditorMode } from '~/stores-universal/ui';
 import { usePageStatusAlert } from '~/stores/alert';
 import { useSWRxCurrentPage, useSWRMUTxCurrentPage } from '~/stores/page';
 import { useSetRemoteLatestPageData, type RemoteRevisionData } from '~/stores/remote-latest-page';
-import { useEditorMode, EditorMode } from '~/stores/ui';
 
 
 export const usePageUpdatedEffect = (): void => {

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

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

+ 3 - 3
apps/app/src/components/Navbar/GrowiContextualSubNavigation.tsx

@@ -14,10 +14,11 @@ import { useRouter } from 'next/router';
 import Sticky from 'react-stickynode';
 import { DropdownItem } from 'reactstrap';
 
-import { useShouldExpandContent } from '~/client/services/layout';
 import { exportAsMarkdown, updateContentWidth } from '~/client/services/page-operation';
 import { GroundGlassBar } from '~/components-universal/Navbar/GroundGlassBar';
 import type { OnDuplicatedFunction, OnRenamedFunction, OnDeletedFunction } from '~/interfaces/ui';
+import { useShouldExpandContent } from '~/services/layout/use-should-expand-content';
+import { useEditorMode } from '~/stores-universal/ui';
 import {
   useCurrentPathname,
   useCurrentUser, useIsGuestUser, useIsReadOnlyUser, useIsSharedUser, useShareLinkId,
@@ -31,12 +32,11 @@ import {
 } from '~/stores/page';
 import { mutatePageTree } from '~/stores/page-listing';
 import {
-  useEditorMode, useIsAbleToShowPageManagement,
+  useIsAbleToShowPageManagement,
   useIsAbleToChangeEditorMode,
   useIsDeviceLargerThanMd,
 } from '~/stores/ui';
 
-
 import { NotAvailable } from '../NotAvailable';
 import { Skeleton } from '../Skeleton';
 

+ 2 - 1
apps/app/src/components/Navbar/PageEditorModeManager.tsx

@@ -6,8 +6,9 @@ import { useTranslation } from 'next-i18next';
 
 import { useCreatePage } from '~/client/services/create-page';
 import { toastError } from '~/client/util/toastr';
+import { EditorMode, useEditorMode } from '~/stores-universal/ui';
 import { useIsNotFound } from '~/stores/page';
-import { EditorMode, useEditorMode, useIsDeviceLargerThanMd } from '~/stores/ui';
+import { useIsDeviceLargerThanMd } from '~/stores/ui';
 import { useCurrentPageYjsData } from '~/stores/yjs';
 
 import { shouldCreateWipPage } from '../../utils/should-create-wip-page';

+ 1 - 1
apps/app/src/components/Page/DisplaySwitcher.tsx

@@ -1,9 +1,9 @@
 import dynamic from 'next/dynamic';
 
 import { useHashChangedEffect } from '~/client/services/side-effects/hash-changed';
+import { EditorMode, useEditorMode } from '~/stores-universal/ui';
 import { useIsEditable } from '~/stores/context';
 import { useIsLatestRevision } from '~/stores/page';
-import { EditorMode, useEditorMode } from '~/stores/ui';
 
 import { LazyRenderer } from '../Common/LazyRenderer';
 

+ 1 - 1
apps/app/src/components/Page/PageView.tsx

@@ -7,8 +7,8 @@ import { isUsersHomepage } from '@growi/core/dist/utils/page-path-utils';
 import { useSlidesByFrontmatter } from '@growi/presentation/dist/services';
 import dynamic from 'next/dynamic';
 
-import { useShouldExpandContent } from '~/client/services/layout';
 import type { RendererConfig } from '~/interfaces/services/renderer';
+import { useShouldExpandContent } from '~/services/layout/use-should-expand-content';
 import { generateSSRViewOptions } from '~/services/renderer/renderer';
 import {
   useIsForbidden, useIsIdenticalPath, useIsNotCreatable,

+ 4 - 1
apps/app/src/components/PageControls/PageControls.tsx

@@ -17,10 +17,13 @@ import {
   toggleLike, toggleSubscribe,
 } from '~/client/services/page-operation';
 import { toastError } from '~/client/util/toastr';
+import {
+  EditorMode, useEditorMode,
+} from '~/stores-universal/ui';
 import { useIsGuestUser, useIsReadOnlyUser, useIsSearchPage } from '~/stores/context';
 import { useTagEditModal, type IPageForPageDuplicateModal } from '~/stores/modal';
 import {
-  EditorMode, useEditorMode, useIsDeviceLargerThanMd, usePageControlsX,
+  useIsDeviceLargerThanMd, usePageControlsX,
 } from '~/stores/ui';
 import loggerFactory from '~/utils/logger';
 

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

@@ -18,11 +18,12 @@ import detectIndent from 'detect-indent';
 import { useTranslation } from 'next-i18next';
 import { throttle, debounce } from 'throttle-debounce';
 
-import { useShouldExpandContent } from '~/client/services/layout';
 import { useUpdateStateAfterSave } from '~/client/services/page-operation';
 import { updatePage, extractRemoteRevisionDataFromErrorObj } from '~/client/services/update-page';
 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 { EditorMode, useEditorMode } from '~/stores-universal/ui';
 import { useNextThemes } from '~/stores-universal/use-next-themes';
 import {
   useDefaultIndentSize, useCurrentUser,
@@ -41,10 +42,7 @@ import {
 } from '~/stores/page';
 import { mutatePageTree } from '~/stores/page-listing';
 import { usePreviewOptions } from '~/stores/renderer';
-import {
-  EditorMode,
-  useEditorMode, useIsUntitledPage, useSelectedGrant,
-} from '~/stores/ui';
+import { useIsUntitledPage, useSelectedGrant } from '~/stores/ui';
 import { useEditingUsers } from '~/stores/use-editing-users';
 import loggerFactory from '~/utils/logger';
 

+ 1 - 1
apps/app/src/components/PageEditor/PageEditorReadOnly.tsx

@@ -4,7 +4,7 @@ import { GlobalCodeMirrorEditorKey } from '@growi/editor';
 import { CodeMirrorEditorReadOnly } from '@growi/editor/dist/client/components/CodeMirrorEditorReadOnly';
 import { throttle } from 'throttle-debounce';
 
-import { useShouldExpandContent } from '~/client/services/layout';
+import { useShouldExpandContent } from '~/services/layout/use-should-expand-content';
 import { useSWRxCurrentPage, useIsLatestRevision } from '~/stores/page';
 import { usePreviewOptions } from '~/stores/renderer';
 

+ 1 - 1
apps/app/src/components/PageEditor/conflict.tsx

@@ -10,11 +10,11 @@ import { useUpdateStateAfterSave } from '~/client/services/page-operation';
 import { toastSuccess } from '~/client/util/toastr';
 import type { Save, SaveOptions } from '~/components/PageEditor/PageEditor';
 import { SocketEventName } from '~/interfaces/websocket';
+import { EditorMode, useEditorMode } from '~/stores-universal/ui';
 import { usePageStatusAlert } from '~/stores/alert';
 import { useConflictDiffModal } from '~/stores/modal';
 import { useCurrentPageId, useSWRxCurrentPage } from '~/stores/page';
 import { type RemoteRevisionData, useSetRemoteLatestPageData } from '~/stores/remote-latest-page';
-import { EditorMode, useEditorMode } from '~/stores/ui';
 
 
 export type ConflictHandler = (

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

@@ -11,7 +11,8 @@ 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, useIsUntitledPage } from '~/stores/ui';
+import { EditorMode, useEditorMode } from '~/stores-universal/ui';
+import { useIsUntitledPage } from '~/stores/ui';
 
 import { CopyDropdown } from '../Common/CopyDropdown';
 import { AutosizeSubmittableInput, getAdjustedMaxWidthForAutosizeInput } from '../Common/SubmittableInput';

+ 1 - 1
apps/app/src/components/PageStatusAlert.tsx

@@ -2,11 +2,11 @@ import React, { useCallback } from 'react';
 
 import { useTranslation } from 'next-i18next';
 
+import { useEditorMode } from '~/stores-universal/ui';
 import { usePageStatusAlert } from '~/stores/alert';
 import { useIsGuestUser, useIsReadOnlyUser } from '~/stores/context';
 import { useSWRxCurrentPage } from '~/stores/page';
 import { useRemoteRevisionId, useRemoteRevisionLastUpdateUser } from '~/stores/remote-latest-page';
-import { useEditorMode } from '~/stores/ui';
 
 import { Username } from './User/Username';
 

+ 2 - 4
apps/app/src/components/SavePageControls.tsx

@@ -10,16 +10,14 @@ import {
   DropdownToggle, DropdownMenu, DropdownItem, Modal,
 } from 'reactstrap';
 
+import { useEditorMode } from '~/stores-universal/ui';
 import {
   useIsEditable, useIsAclEnabled,
   useIsSlackConfigured,
 } from '~/stores/context';
 import { useWaitingSaveProcessing, useSWRxSlackChannels, useIsSlackEnabled } from '~/stores/editor';
 import { useSWRxCurrentPage, useCurrentPagePath } from '~/stores/page';
-import {
-  useEditorMode, useIsDeviceLargerThanMd,
-
-} from '~/stores/ui';
+import { useIsDeviceLargerThanMd } from '~/stores/ui';
 import loggerFactory from '~/utils/logger';
 
 import { GrantSelector } from './SavePageControls/GrantSelector';

+ 1 - 1
apps/app/src/components/SearchPage/SearchResultContent.tsx

@@ -11,11 +11,11 @@ import { animateScroll } from 'react-scroll';
 import { DropdownItem } from 'reactstrap';
 import { debounce } from 'throttle-debounce';
 
-import { useShouldExpandContent } from '~/client/services/layout';
 import { exportAsMarkdown, updateContentWidth } from '~/client/services/page-operation';
 import { toastSuccess } from '~/client/util/toastr';
 import type { IPageWithSearchMeta } from '~/interfaces/search';
 import type { OnDuplicatedFunction, OnRenamedFunction, OnDeletedFunction } from '~/interfaces/ui';
+import { useShouldExpandContent } from '~/services/layout/use-should-expand-content';
 import { useCurrentUser } from '~/stores/context';
 import {
   usePageDuplicateModal, usePageRenameModal, usePageDeleteModal,

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

@@ -4,11 +4,10 @@ import type { IPagePopulatedToShowRevision } from '@growi/core';
 import { useSlidesByFrontmatter } from '@growi/presentation/dist/services';
 import dynamic from 'next/dynamic';
 
-import { useShouldExpandContent } from '~/client/services/layout';
 import type { RendererConfig } from '~/interfaces/services/renderer';
 import type { IShareLinkHasId } from '~/interfaces/share-link';
+import { useShouldExpandContent } from '~/services/layout/use-should-expand-content';
 import { generateSSRViewOptions } from '~/services/renderer/renderer';
-import { useIsEnabledMarp } from '~/stores/context';
 import { useIsNotFound } from '~/stores/page';
 import { useViewOptions } from '~/stores/renderer';
 import loggerFactory from '~/utils/logger';

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

@@ -20,7 +20,6 @@ import Head from 'next/head';
 import { useRouter } from 'next/router';
 import superjson from 'superjson';
 
-import { useEditorModeClassName } from '~/client/services/layout';
 import { BasicLayout } from '~/components-universal/Layout/BasicLayout';
 import { DrawioViewerScript } from '~/components-universal/Script/DrawioViewerScript';
 import { PageView } from '~/components/Page/PageView';
@@ -31,6 +30,7 @@ import type { ISidebarConfig } from '~/interfaces/sidebar-config';
 import type { CurrentPageYjsData } from '~/interfaces/yjs';
 import type { PageModel, PageDocument } from '~/server/models/page';
 import type { PageRedirectModel } from '~/server/models/page-redirect';
+import { useEditorModeClassName } from '~/services/layout/use-editor-mode-class-name';
 import {
   useCurrentUser,
   useIsForbidden, useIsSharedUser,

+ 7 - 0
apps/app/src/services/layout/use-editor-mode-class-name.ts

@@ -0,0 +1,7 @@
+import { useEditorMode } from '~/stores-universal/ui';
+
+export const useEditorModeClassName = (): string => {
+  const { getClassNamesByEditorMode } = useEditorMode();
+
+  return `${getClassNamesByEditorMode().join(' ') ?? ''}`;
+};

+ 0 - 7
apps/app/src/client/services/layout.ts → apps/app/src/services/layout/use-should-expand-content.ts

@@ -1,13 +1,6 @@
 import type { IPage, IPagePopulatedToShowRevision } from '@growi/core';
 
 import { useIsContainerFluid } from '~/stores/context';
-import { useEditorMode } from '~/stores/ui';
-
-export const useEditorModeClassName = (): string => {
-  const { getClassNamesByEditorMode } = useEditorMode();
-
-  return `${getClassNamesByEditorMode().join(' ') ?? ''}`;
-};
 
 const useDetermineExpandContent = (expandContentWidth?: boolean | null): boolean => {
   const { data: dataIsContainerFluid } = useIsContainerFluid();

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

@@ -0,0 +1,106 @@
+/** **********************************************************
+ *                          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 { useIsNotFound } from '~/stores/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 { data: isNotFound } = useIsNotFound();
+
+  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,
+  });
+};

+ 2 - 99
apps/app/src/stores/ui.tsx

@@ -5,12 +5,11 @@ import {
 
 import { PageGrant, type Nullable } from '@growi/core';
 import { type SWRResponseWithUtils, useSWRStatic, withUtils } from '@growi/core/dist/swr';
-import { pagePathUtils, isClient, isServer } from '@growi/core/dist/utils';
+import { pagePathUtils, isClient } from '@growi/core/dist/utils';
 import { Breakpoint } from '@growi/ui/dist/interfaces';
 import { addBreakpointListener, cleanupBreakpointListener } from '@growi/ui/dist/utils';
 import { useRouter } from 'next/router';
 import type { HtmlElementNode } from 'rehype-toc';
-import type SimpleBar from 'simplebar-react';
 import type { MutatorOptions } from 'swr';
 import {
   useSWRConfig, type SWRResponse, type Key,
@@ -21,6 +20,7 @@ import { scheduleToPut } from '~/client/services/user-ui-settings';
 import type { IPageSelectedGrant } from '~/interfaces/page';
 import { SidebarContentsType, SidebarMode } from '~/interfaces/ui';
 import type { UpdateDescCountData } from '~/interfaces/websocket';
+import { EditorMode, useEditorMode } from '~/stores-universal/ui';
 import {
   useIsNotFound, useCurrentPagePath, useIsTrashPage, useCurrentPageId,
 } from '~/stores/page';
@@ -37,17 +37,6 @@ const { isTrashTopPage, isUsersTopPage } = pagePathUtils;
 const logger = loggerFactory('growi:stores:ui');
 
 
-/** **********************************************************
- *                          Unions
- *********************************************************** */
-
-export const EditorMode = {
-  View: 'view',
-  Editor: 'editor',
-} as const;
-export type EditorMode = typeof EditorMode[keyof typeof EditorMode];
-
-
 /** **********************************************************
  *                     Storing objects to ref
  *********************************************************** */
@@ -81,92 +70,6 @@ export const useIsMobile = (): SWRResponse<boolean, Error> => {
   return useStaticSWR<boolean, Error>(key, undefined, configuration);
 };
 
-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 { data: isNotFound } = useIsNotFound();
-
-  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,
-  });
-};
-
 export const useIsDeviceLargerThanMd = (): SWRResponse<boolean, Error> => {
   const key: Key = isClient() ? 'isDeviceLargerThanMd' : null;