|
@@ -2,10 +2,11 @@ import React, {
|
|
|
useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState,
|
|
useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState,
|
|
|
} from 'react';
|
|
} from 'react';
|
|
|
|
|
|
|
|
-import EventEmitter from 'events';
|
|
|
|
|
|
|
+import type EventEmitter from 'events';
|
|
|
import nodePath from 'path';
|
|
import nodePath from 'path';
|
|
|
|
|
|
|
|
import type { IPageHasId } from '@growi/core';
|
|
import type { IPageHasId } from '@growi/core';
|
|
|
|
|
+import { useGlobalSocket } from '@growi/core/dist/swr';
|
|
|
import { pathUtils } from '@growi/core/dist/utils';
|
|
import { pathUtils } from '@growi/core/dist/utils';
|
|
|
import {
|
|
import {
|
|
|
CodeMirrorEditorMain, GlobalCodeMirrorEditorKey, AcceptedUploadFileType,
|
|
CodeMirrorEditorMain, GlobalCodeMirrorEditorKey, AcceptedUploadFileType,
|
|
@@ -16,18 +17,20 @@ import { useTranslation } from 'next-i18next';
|
|
|
import { useRouter } from 'next/router';
|
|
import { useRouter } from 'next/router';
|
|
|
import { throttle, debounce } from 'throttle-debounce';
|
|
import { throttle, debounce } from 'throttle-debounce';
|
|
|
|
|
|
|
|
|
|
+
|
|
|
import { useShouldExpandContent } from '~/client/services/layout';
|
|
import { useShouldExpandContent } from '~/client/services/layout';
|
|
|
import { useUpdateStateAfterSave, useSaveOrUpdate } from '~/client/services/page-operation';
|
|
import { useUpdateStateAfterSave, useSaveOrUpdate } from '~/client/services/page-operation';
|
|
|
import { apiv3Get, apiv3PostForm } from '~/client/util/apiv3-client';
|
|
import { apiv3Get, apiv3PostForm } from '~/client/util/apiv3-client';
|
|
|
import { toastError, toastSuccess } from '~/client/util/toastr';
|
|
import { toastError, toastSuccess } from '~/client/util/toastr';
|
|
|
-import { OptionsToSave } from '~/interfaces/page-operation';
|
|
|
|
|
|
|
+import type { OptionsToSave } from '~/interfaces/page-operation';
|
|
|
import { SocketEventName } from '~/interfaces/websocket';
|
|
import { SocketEventName } from '~/interfaces/websocket';
|
|
|
import {
|
|
import {
|
|
|
- useDefaultIndentSize,
|
|
|
|
|
|
|
+ useDefaultIndentSize, useCurrentUser,
|
|
|
useCurrentPathname, useIsEnabledAttachTitleHeader,
|
|
useCurrentPathname, useIsEnabledAttachTitleHeader,
|
|
|
useIsEditable, useIsUploadAllFileAllowed, useIsUploadEnabled, useIsIndentSizeForced,
|
|
useIsEditable, useIsUploadAllFileAllowed, useIsUploadEnabled, useIsIndentSizeForced,
|
|
|
} from '~/stores/context';
|
|
} from '~/stores/context';
|
|
|
import {
|
|
import {
|
|
|
|
|
+ useEditorSettings,
|
|
|
useCurrentIndentSize, useIsSlackEnabled, usePageTagsForEditors,
|
|
useCurrentIndentSize, useIsSlackEnabled, usePageTagsForEditors,
|
|
|
useIsEnabledUnsavedWarning,
|
|
useIsEnabledUnsavedWarning,
|
|
|
useIsConflict,
|
|
useIsConflict,
|
|
@@ -51,9 +54,9 @@ import {
|
|
|
useEditorMode, useSelectedGrant,
|
|
useEditorMode, useSelectedGrant,
|
|
|
} from '~/stores/ui';
|
|
} from '~/stores/ui';
|
|
|
import { useNextThemes } from '~/stores/use-next-themes';
|
|
import { useNextThemes } from '~/stores/use-next-themes';
|
|
|
-import { useGlobalSocket } from '~/stores/websocket';
|
|
|
|
|
import loggerFactory from '~/utils/logger';
|
|
import loggerFactory from '~/utils/logger';
|
|
|
|
|
|
|
|
|
|
+import { PageHeader } from '../PageHeader/PageHeader';
|
|
|
|
|
|
|
|
// import { ConflictDiffModal } from './PageEditor/ConflictDiffModal';
|
|
// import { ConflictDiffModal } from './PageEditor/ConflictDiffModal';
|
|
|
// import { ConflictDiffModal } from './ConflictDiffModal';
|
|
// import { ConflictDiffModal } from './ConflictDiffModal';
|
|
@@ -111,11 +114,13 @@ export const PageEditor = React.memo((props: Props): JSX.Element => {
|
|
|
const { data: isUploadAllFileAllowed } = useIsUploadAllFileAllowed();
|
|
const { data: isUploadAllFileAllowed } = useIsUploadAllFileAllowed();
|
|
|
const { data: isUploadEnabled } = useIsUploadEnabled();
|
|
const { data: isUploadEnabled } = useIsUploadEnabled();
|
|
|
const { data: conflictDiffModalStatus, close: closeConflictDiffModal } = useConflictDiffModal();
|
|
const { data: conflictDiffModalStatus, close: closeConflictDiffModal } = useConflictDiffModal();
|
|
|
|
|
+ const { data: editorSettings } = useEditorSettings();
|
|
|
const { mutate: mutateIsLatestRevision } = useIsLatestRevision();
|
|
const { mutate: mutateIsLatestRevision } = useIsLatestRevision();
|
|
|
const { mutate: mutateRemotePageId } = useRemoteRevisionId();
|
|
const { mutate: mutateRemotePageId } = useRemoteRevisionId();
|
|
|
const { mutate: mutateRemoteRevisionId } = useRemoteRevisionBody();
|
|
const { mutate: mutateRemoteRevisionId } = useRemoteRevisionBody();
|
|
|
const { mutate: mutateRemoteRevisionLastUpdatedAt } = useRemoteRevisionLastUpdatedAt();
|
|
const { mutate: mutateRemoteRevisionLastUpdatedAt } = useRemoteRevisionLastUpdatedAt();
|
|
|
const { mutate: mutateRemoteRevisionLastUpdateUser } = useRemoteRevisionLastUpdateUser();
|
|
const { mutate: mutateRemoteRevisionLastUpdateUser } = useRemoteRevisionLastUpdateUser();
|
|
|
|
|
+ const { data: user } = useCurrentUser();
|
|
|
|
|
|
|
|
const { data: socket } = useGlobalSocket();
|
|
const { data: socket } = useGlobalSocket();
|
|
|
|
|
|
|
@@ -131,7 +136,7 @@ export const PageEditor = React.memo((props: Props): JSX.Element => {
|
|
|
const updateStateAfterSave = useUpdateStateAfterSave(pageId, { supressEditingMarkdownMutation: true });
|
|
const updateStateAfterSave = useUpdateStateAfterSave(pageId, { supressEditingMarkdownMutation: true });
|
|
|
|
|
|
|
|
const { resolvedTheme } = useNextThemes();
|
|
const { resolvedTheme } = useNextThemes();
|
|
|
- mutateResolvedTheme(resolvedTheme);
|
|
|
|
|
|
|
+ mutateResolvedTheme({ themeData: resolvedTheme });
|
|
|
|
|
|
|
|
// TODO: remove workaround
|
|
// TODO: remove workaround
|
|
|
// for https://redmine.weseek.co.jp/issues/125923
|
|
// for https://redmine.weseek.co.jp/issues/125923
|
|
@@ -170,15 +175,16 @@ export const PageEditor = React.memo((props: Props): JSX.Element => {
|
|
|
const setMarkdownPreviewWithDebounce = useMemo(() => debounce(100, throttle(150, (value: string) => {
|
|
const setMarkdownPreviewWithDebounce = useMemo(() => debounce(100, throttle(150, (value: string) => {
|
|
|
setMarkdownToPreview(value);
|
|
setMarkdownToPreview(value);
|
|
|
})), []);
|
|
})), []);
|
|
|
- const mutateIsEnabledUnsavedWarningWithDebounce = useMemo(() => debounce(600, throttle(900, (value: string) => {
|
|
|
|
|
- // Displays an unsaved warning alert
|
|
|
|
|
- mutateIsEnabledUnsavedWarning(value !== initialValueRef.current);
|
|
|
|
|
- })), [mutateIsEnabledUnsavedWarning]);
|
|
|
|
|
|
|
+ // const mutateIsEnabledUnsavedWarningWithDebounce = useMemo(() => debounce(600, throttle(900, (value: string) => {
|
|
|
|
|
+ // // Displays an unsaved warning alert
|
|
|
|
|
+ // mutateIsEnabledUnsavedWarning(value !== initialValueRef.current);
|
|
|
|
|
+ // })), [mutateIsEnabledUnsavedWarning]);
|
|
|
|
|
|
|
|
const markdownChangedHandler = useCallback((value: string) => {
|
|
const markdownChangedHandler = useCallback((value: string) => {
|
|
|
setMarkdownPreviewWithDebounce(value);
|
|
setMarkdownPreviewWithDebounce(value);
|
|
|
- mutateIsEnabledUnsavedWarningWithDebounce(value);
|
|
|
|
|
- }, [mutateIsEnabledUnsavedWarningWithDebounce, setMarkdownPreviewWithDebounce]);
|
|
|
|
|
|
|
+ // mutateIsEnabledUnsavedWarningWithDebounce(value);
|
|
|
|
|
+ // }, [mutateIsEnabledUnsavedWarningWithDebounce, setMarkdownPreviewWithDebounce]);
|
|
|
|
|
+ }, [setMarkdownPreviewWithDebounce]);
|
|
|
|
|
|
|
|
|
|
|
|
|
const { data: codeMirrorEditor } = useCodeMirrorEditorIsolated(GlobalCodeMirrorEditorKey.MAIN);
|
|
const { data: codeMirrorEditor } = useCodeMirrorEditorIsolated(GlobalCodeMirrorEditorKey.MAIN);
|
|
@@ -403,17 +409,6 @@ export const PageEditor = React.memo((props: Props): JSX.Element => {
|
|
|
|
|
|
|
|
}, [mutateCurrentPage, mutateEditingMarkdown, mutateIsConflict, mutateTagsInfo, syncTagsInfoForEditor]);
|
|
}, [mutateCurrentPage, mutateEditingMarkdown, mutateIsConflict, mutateTagsInfo, syncTagsInfoForEditor]);
|
|
|
|
|
|
|
|
-
|
|
|
|
|
- // initialize
|
|
|
|
|
- useEffect(() => {
|
|
|
|
|
- if (initialValue == null) {
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- codeMirrorEditor?.initDoc(initialValue);
|
|
|
|
|
- setMarkdownToPreview(initialValue);
|
|
|
|
|
- mutateIsEnabledUnsavedWarning(false);
|
|
|
|
|
- }, [codeMirrorEditor, initialValue, mutateIsEnabledUnsavedWarning]);
|
|
|
|
|
-
|
|
|
|
|
// initial caret line
|
|
// initial caret line
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
|
codeMirrorEditor?.setCaretLine();
|
|
codeMirrorEditor?.setCaretLine();
|
|
@@ -452,19 +447,21 @@ export const PageEditor = React.memo((props: Props): JSX.Element => {
|
|
|
}
|
|
}
|
|
|
}, [initialValue, isIndentSizeForced, mutateCurrentIndentSize]);
|
|
}, [initialValue, isIndentSizeForced, mutateCurrentIndentSize]);
|
|
|
|
|
|
|
|
- // when transitioning to a different page, if the initialValue is the same,
|
|
|
|
|
- // UnControlled CodeMirror value does not reset, so explicitly set the value to initialValue
|
|
|
|
|
- const onRouterChangeComplete = useCallback(() => {
|
|
|
|
|
- codeMirrorEditor?.initDoc(initialValue);
|
|
|
|
|
- codeMirrorEditor?.setCaretLine();
|
|
|
|
|
- }, [codeMirrorEditor, initialValue]);
|
|
|
|
|
|
|
|
|
|
- useEffect(() => {
|
|
|
|
|
- router.events.on('routeChangeComplete', onRouterChangeComplete);
|
|
|
|
|
- return () => {
|
|
|
|
|
- router.events.off('routeChangeComplete', onRouterChangeComplete);
|
|
|
|
|
- };
|
|
|
|
|
- }, [onRouterChangeComplete, router.events]);
|
|
|
|
|
|
|
+ // TODO: Check the reproduction conditions that made this code necessary and confirm reproduction
|
|
|
|
|
+ // // when transitioning to a different page, if the initialValue is the same,
|
|
|
|
|
+ // // UnControlled CodeMirror value does not reset, so explicitly set the value to initialValue
|
|
|
|
|
+ // const onRouterChangeComplete = useCallback(() => {
|
|
|
|
|
+ // codeMirrorEditor?.initDoc(ydoc?.getText('codemirror').toString());
|
|
|
|
|
+ // codeMirrorEditor?.setCaretLine();
|
|
|
|
|
+ // }, [codeMirrorEditor, ydoc]);
|
|
|
|
|
+
|
|
|
|
|
+ // useEffect(() => {
|
|
|
|
|
+ // router.events.on('routeChangeComplete', onRouterChangeComplete);
|
|
|
|
|
+ // return () => {
|
|
|
|
|
+ // router.events.off('routeChangeComplete', onRouterChangeComplete);
|
|
|
|
|
+ // };
|
|
|
|
|
+ // }, [onRouterChangeComplete, router.events]);
|
|
|
|
|
|
|
|
if (!isEditable) {
|
|
if (!isEditable) {
|
|
|
return <></>;
|
|
return <></>;
|
|
@@ -476,8 +473,8 @@ export const PageEditor = React.memo((props: Props): JSX.Element => {
|
|
|
|
|
|
|
|
return (
|
|
return (
|
|
|
<div data-testid="page-editor" id="page-editor" className={`flex-expand-vert ${props.visibility ? '' : 'd-none'}`}>
|
|
<div data-testid="page-editor" id="page-editor" className={`flex-expand-vert ${props.visibility ? '' : 'd-none'}`}>
|
|
|
- <div className="flex-expand-vert justify-content-center align-items-center" style={{ minHeight: '72px' }}>
|
|
|
|
|
- <div>Header</div>
|
|
|
|
|
|
|
+ <div className="flex-expand-vert justify-content-center" style={{ minHeight: '72px' }}>
|
|
|
|
|
+ <PageHeader />
|
|
|
</div>
|
|
</div>
|
|
|
<div className={`flex-expand-horiz ${props.visibility ? '' : 'd-none'}`}>
|
|
<div className={`flex-expand-horiz ${props.visibility ? '' : 'd-none'}`}>
|
|
|
<div className="page-editor-editor-container flex-expand-vert">
|
|
<div className="page-editor-editor-container flex-expand-vert">
|
|
@@ -497,9 +494,14 @@ export const PageEditor = React.memo((props: Props): JSX.Element => {
|
|
|
onChange={markdownChangedHandler}
|
|
onChange={markdownChangedHandler}
|
|
|
onSave={saveWithShortcut}
|
|
onSave={saveWithShortcut}
|
|
|
onUpload={uploadHandler}
|
|
onUpload={uploadHandler}
|
|
|
|
|
+ acceptedFileType={acceptedFileType}
|
|
|
onScroll={scrollEditorHandlerThrottle}
|
|
onScroll={scrollEditorHandlerThrottle}
|
|
|
indentSize={currentIndentSize ?? defaultIndentSize}
|
|
indentSize={currentIndentSize ?? defaultIndentSize}
|
|
|
- acceptedFileType={acceptedFileType}
|
|
|
|
|
|
|
+ userName={user?.name}
|
|
|
|
|
+ pageId={pageId ?? undefined}
|
|
|
|
|
+ initialValue={initialValue}
|
|
|
|
|
+ onOpenEditor={markdown => setMarkdownToPreview(markdown)}
|
|
|
|
|
+ editorTheme={editorSettings?.theme}
|
|
|
/>
|
|
/>
|
|
|
</div>
|
|
</div>
|
|
|
<div ref={previewRef} onScroll={scrollPreviewHandlerThrottle} className="page-editor-preview-container flex-expand-vert d-none d-lg-flex">
|
|
<div ref={previewRef} onScroll={scrollPreviewHandlerThrottle} className="page-editor-preview-container flex-expand-vert d-none d-lg-flex">
|