|
|
@@ -1,37 +1,39 @@
|
|
|
import React, {
|
|
|
useCallback,
|
|
|
- useEffect, useRef, useState,
|
|
|
+ useEffect, useRef,
|
|
|
} from 'react';
|
|
|
|
|
|
import EventEmitter from 'events';
|
|
|
|
|
|
+import { DrawioEditByViewerProps } from '@growi/remark-drawio-plugin';
|
|
|
+import { useTranslation } from 'next-i18next';
|
|
|
import dynamic from 'next/dynamic';
|
|
|
-// import { debounce } from 'throttle-debounce';
|
|
|
-
|
|
|
import { HtmlElementNode } from 'rehype-toc';
|
|
|
|
|
|
+import { useSaveOrUpdate } from '~/client/services/page-operation';
|
|
|
import { toastSuccess, toastError } from '~/client/util/apiNotification';
|
|
|
-import { getOptionsToSave } from '~/client/util/editor';
|
|
|
+import { OptionsToSave } from '~/interfaces/page-operation';
|
|
|
import {
|
|
|
useIsGuestUser, useShareLinkId,
|
|
|
} from '~/stores/context';
|
|
|
-import {
|
|
|
- useSWRxSlackChannels, useIsSlackEnabled, usePageTagsForEditors, useIsEnabledUnsavedWarning,
|
|
|
-} from '~/stores/editor';
|
|
|
-import { useSWRxCurrentPage } from '~/stores/page';
|
|
|
+import { useEditingMarkdown } from '~/stores/editor';
|
|
|
+import { useDrawioModal } from '~/stores/modal';
|
|
|
+import { useSWRxCurrentPage, useSWRxTagsInfo } from '~/stores/page';
|
|
|
import { useViewOptions } from '~/stores/renderer';
|
|
|
import {
|
|
|
useCurrentPageTocNode,
|
|
|
- useEditorMode, useIsMobile,
|
|
|
+ useIsMobile,
|
|
|
} from '~/stores/ui';
|
|
|
import loggerFactory from '~/utils/logger';
|
|
|
|
|
|
import RevisionRenderer from './Page/RevisionRenderer';
|
|
|
-import { DrawioModal } from './PageEditor/DrawioModal';
|
|
|
import mdu from './PageEditor/MarkdownDrawioUtil';
|
|
|
|
|
|
|
|
|
-declare const globalEmitter: EventEmitter;
|
|
|
+declare global {
|
|
|
+ // eslint-disable-next-line vars-on-top, no-var
|
|
|
+ var globalEmitter: EventEmitter;
|
|
|
+}
|
|
|
|
|
|
// const DrawioModal = dynamic(() => import('./PageEditor/DrawioModal'), { ssr: false });
|
|
|
const GridEditModal = dynamic(() => import('./PageEditor/GridEditModal'), { ssr: false });
|
|
|
@@ -39,118 +41,10 @@ const LinkEditModal = dynamic(() => import('./PageEditor/LinkEditModal'), { ssr:
|
|
|
|
|
|
const logger = loggerFactory('growi:Page');
|
|
|
|
|
|
-type PageSubstanceProps = {
|
|
|
- rendererOptions: any,
|
|
|
- page: any,
|
|
|
- pageTags?: string[],
|
|
|
- editorMode: string,
|
|
|
- isGuestUser: boolean,
|
|
|
- isMobile?: boolean,
|
|
|
- isSlackEnabled: boolean,
|
|
|
- slackChannels: string,
|
|
|
-};
|
|
|
-
|
|
|
-class PageSubstance extends React.Component<PageSubstanceProps> {
|
|
|
-
|
|
|
- gridEditModal: any;
|
|
|
-
|
|
|
- linkEditModal: any;
|
|
|
-
|
|
|
- drawioModal: any;
|
|
|
-
|
|
|
- constructor(props: PageSubstanceProps) {
|
|
|
- super(props);
|
|
|
-
|
|
|
- this.state = {
|
|
|
- currentTargetTableArea: null,
|
|
|
- currentTargetDrawioArea: null,
|
|
|
- };
|
|
|
-
|
|
|
- this.gridEditModal = React.createRef();
|
|
|
- this.linkEditModal = React.createRef();
|
|
|
- this.drawioModal = React.createRef();
|
|
|
-
|
|
|
- this.saveHandlerForDrawioModal = this.saveHandlerForDrawioModal.bind(this);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * launch DrawioModal with data specified by arguments
|
|
|
- * @param beginLineNumber
|
|
|
- * @param endLineNumber
|
|
|
- */
|
|
|
- launchDrawioModal(beginLineNumber, endLineNumber) {
|
|
|
- // const markdown = this.props.pageContainer.state.markdown;
|
|
|
- // const drawioMarkdownArray = markdown.split(/\r\n|\r|\n/).slice(beginLineNumber - 1, endLineNumber);
|
|
|
- // const drawioData = drawioMarkdownArray.slice(1, drawioMarkdownArray.length - 1).join('\n').trim();
|
|
|
- // this.setState({ currentTargetDrawioArea: { beginLineNumber, endLineNumber } });
|
|
|
- // this.drawioModal.current.show(drawioData);
|
|
|
- }
|
|
|
-
|
|
|
- async saveHandlerForDrawioModal(drawioData) {
|
|
|
- // const {
|
|
|
- // isSlackEnabled, slackChannels, pageContainer, pageTags, grant, grantGroupId, grantGroupName, mutateIsEnabledUnsavedWarning,
|
|
|
- // } = this.props;
|
|
|
- // const optionsToSave = getOptionsToSave(isSlackEnabled, slackChannels, grant, grantGroupId, grantGroupName, pageTags);
|
|
|
-
|
|
|
- // const newMarkdown = mdu.replaceDrawioInMarkdown(
|
|
|
- // drawioData,
|
|
|
- // this.props.pageContainer.state.markdown,
|
|
|
- // this.state.currentTargetDrawioArea.beginLineNumber,
|
|
|
- // this.state.currentTargetDrawioArea.endLineNumber,
|
|
|
- // );
|
|
|
-
|
|
|
- // try {
|
|
|
- // // disable unsaved warning
|
|
|
- // mutateIsEnabledUnsavedWarning(false);
|
|
|
-
|
|
|
- // // eslint-disable-next-line no-unused-vars
|
|
|
- // const { page, tags } = await pageContainer.save(newMarkdown, this.props.editorMode, optionsToSave);
|
|
|
- // logger.debug('success to save');
|
|
|
-
|
|
|
- // // Todo: add translation
|
|
|
- // toastSuccess(t(''));
|
|
|
- // }
|
|
|
- // catch (error) {
|
|
|
- // logger.error('failed to save', error);
|
|
|
- // toastError(error);
|
|
|
- // }
|
|
|
- // finally {
|
|
|
- // this.setState({ currentTargetDrawioArea: null });
|
|
|
- // }
|
|
|
- }
|
|
|
-
|
|
|
- override render() {
|
|
|
- const {
|
|
|
- rendererOptions, page, isMobile, isGuestUser,
|
|
|
- } = this.props;
|
|
|
- const { path } = page;
|
|
|
- const { _id: revisionId, body: markdown } = page.revision;
|
|
|
-
|
|
|
- return (
|
|
|
- <div className={`mb-5 ${isMobile ? 'page-mobile' : ''}`}>
|
|
|
-
|
|
|
- { revisionId != null && (
|
|
|
- <RevisionRenderer rendererOptions={rendererOptions} markdown={markdown} />
|
|
|
- )}
|
|
|
-
|
|
|
- { !isGuestUser && (
|
|
|
- <>
|
|
|
- <GridEditModal ref={this.gridEditModal} />
|
|
|
- <LinkEditModal ref={this.linkEditModal} />
|
|
|
- {/* TODO: use global DrawioModal https://redmine.weseek.co.jp/issues/105981 */}
|
|
|
- {/* <DrawioModal
|
|
|
- ref={this.drawioModal}
|
|
|
- onSave={this.saveHandlerForDrawioModal}
|
|
|
- /> */}
|
|
|
- </>
|
|
|
- )}
|
|
|
- </div>
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
-}
|
|
|
|
|
|
export const Page = (props) => {
|
|
|
+ const { t } = useTranslation();
|
|
|
+
|
|
|
// Pass tocRef to generateViewOptions (=> rehypePlugin => customizeTOC) to call mutateCurrentPageTocNode when tocRef.current changes.
|
|
|
// The toc node passed by customizeTOC is assigned to tocRef.current.
|
|
|
const tocRef = useRef<HtmlElementNode>();
|
|
|
@@ -160,41 +54,76 @@ export const Page = (props) => {
|
|
|
}, []);
|
|
|
|
|
|
const { data: shareLinkId } = useShareLinkId();
|
|
|
- const { data: currentPage } = useSWRxCurrentPage(shareLinkId ?? undefined);
|
|
|
- const { data: editorMode } = useEditorMode();
|
|
|
+ const { data: currentPage, mutate: mutateCurrentPage } = useSWRxCurrentPage(shareLinkId ?? undefined);
|
|
|
+ const { mutate: mutateEditingMarkdown } = useEditingMarkdown();
|
|
|
+ const { data: tagsInfo } = useSWRxTagsInfo(currentPage?._id);
|
|
|
const { data: isGuestUser } = useIsGuestUser();
|
|
|
const { data: isMobile } = useIsMobile();
|
|
|
- const { data: slackChannelsData } = useSWRxSlackChannels(currentPage?.path);
|
|
|
- const { data: isSlackEnabled } = useIsSlackEnabled();
|
|
|
- const { data: pageTags } = usePageTagsForEditors(null); // TODO: pass pageId
|
|
|
const { data: rendererOptions } = useViewOptions(storeTocNodeHandler);
|
|
|
- const { mutate: mutateIsEnabledUnsavedWarning } = useIsEnabledUnsavedWarning();
|
|
|
const { mutate: mutateCurrentPageTocNode } = useCurrentPageTocNode();
|
|
|
+ const { open: openDrawioModal } = useDrawioModal();
|
|
|
+
|
|
|
+ const saveOrUpdate = useSaveOrUpdate();
|
|
|
|
|
|
- const pageRef = useRef(null);
|
|
|
|
|
|
useEffect(() => {
|
|
|
mutateCurrentPageTocNode(tocRef.current);
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
|
}, [mutateCurrentPageTocNode, tocRef.current]); // include tocRef.current to call mutateCurrentPageTocNode when tocRef.current changes
|
|
|
|
|
|
- // // set handler to open DrawioModal
|
|
|
- // useEffect(() => {
|
|
|
- // const handler = (beginLineNumber, endLineNumber) => {
|
|
|
- // if (pageRef?.current != null) {
|
|
|
- // pageRef.current.launchDrawioModal(beginLineNumber, endLineNumber);
|
|
|
- // }
|
|
|
- // };
|
|
|
- // window.globalEmitter.on('launchDrawioModal', handler);
|
|
|
-
|
|
|
- // return function cleanup() {
|
|
|
- // window.globalEmitter.removeListener('launchDrawioModal', handler);
|
|
|
- // };
|
|
|
- // }, []);
|
|
|
-
|
|
|
- if (currentPage == null || editorMode == null || isGuestUser == null || rendererOptions == null) {
|
|
|
+
|
|
|
+ const saveByDrawioModal = useCallback(async(drawioMxFile: string, bol: number, eol: number) => {
|
|
|
+ if (currentPage == null || tagsInfo == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const currentMarkdown = currentPage.revision.body;
|
|
|
+ const optionsToSave: OptionsToSave = {
|
|
|
+ isSlackEnabled: false,
|
|
|
+ slackChannels: '',
|
|
|
+ grant: currentPage.grant,
|
|
|
+ grantUserGroupId: currentPage.grantedGroup?._id,
|
|
|
+ grantUserGroupName: currentPage.grantedGroup?.name,
|
|
|
+ pageTags: tagsInfo.tags,
|
|
|
+ };
|
|
|
+
|
|
|
+ const newMarkdown = mdu.replaceDrawioInMarkdown(drawioMxFile, currentMarkdown, bol, eol);
|
|
|
+
|
|
|
+ try {
|
|
|
+ const currentRevisionId = currentPage.revision._id;
|
|
|
+ await saveOrUpdate(
|
|
|
+ newMarkdown,
|
|
|
+ { pageId: currentPage._id, path: currentPage.path, revisionId: currentRevisionId },
|
|
|
+ optionsToSave,
|
|
|
+ );
|
|
|
+
|
|
|
+ toastSuccess(t('toaster.save_succeeded'));
|
|
|
+
|
|
|
+ // rerender
|
|
|
+ mutateCurrentPage();
|
|
|
+ mutateEditingMarkdown(newMarkdown);
|
|
|
+ }
|
|
|
+ catch (error) {
|
|
|
+ logger.error('failed to save', error);
|
|
|
+ toastError(error);
|
|
|
+ }
|
|
|
+ }, [currentPage, mutateCurrentPage, mutateEditingMarkdown, saveOrUpdate, t, tagsInfo]);
|
|
|
+
|
|
|
+ // set handler to open DrawioModal
|
|
|
+ useEffect(() => {
|
|
|
+ const handler = (data: DrawioEditByViewerProps) => {
|
|
|
+ openDrawioModal(data.drawioMxFile, drawioMxFile => saveByDrawioModal(drawioMxFile, data.bol, data.eol));
|
|
|
+ };
|
|
|
+ globalEmitter.on('launchDrawioModal', handler);
|
|
|
+
|
|
|
+ return function cleanup() {
|
|
|
+ globalEmitter.removeListener('launchDrawioModal', handler);
|
|
|
+ };
|
|
|
+ }, [openDrawioModal, saveByDrawioModal]);
|
|
|
+
|
|
|
+ if (currentPage == null || isGuestUser == null || rendererOptions == null) {
|
|
|
const entries = Object.entries({
|
|
|
- currentPage, editorMode, isGuestUser, rendererOptions,
|
|
|
+ currentPage, isGuestUser, rendererOptions,
|
|
|
})
|
|
|
.map(([key, value]) => [key, value == null ? 'null' : undefined])
|
|
|
.filter(([, value]) => value != null);
|
|
|
@@ -203,19 +132,22 @@ export const Page = (props) => {
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
+ const { _id: revisionId, body: markdown } = currentPage.revision;
|
|
|
+
|
|
|
return (
|
|
|
- <PageSubstance
|
|
|
- {...props}
|
|
|
- ref={pageRef}
|
|
|
- rendererOptions={rendererOptions}
|
|
|
- page={currentPage}
|
|
|
- editorMode={editorMode}
|
|
|
- isGuestUser={isGuestUser}
|
|
|
- isMobile={isMobile}
|
|
|
- isSlackEnabled={isSlackEnabled}
|
|
|
- pageTags={pageTags}
|
|
|
- slackChannels={slackChannelsData?.toString()}
|
|
|
- mutateIsEnabledUnsavedWarning={mutateIsEnabledUnsavedWarning}
|
|
|
- />
|
|
|
+ <div className={`mb-5 ${isMobile ? 'page-mobile' : ''}`}>
|
|
|
+
|
|
|
+ { revisionId != null && (
|
|
|
+ <RevisionRenderer rendererOptions={rendererOptions} markdown={markdown} />
|
|
|
+ )}
|
|
|
+
|
|
|
+ { !isGuestUser && (
|
|
|
+ <>
|
|
|
+ <GridEditModal />
|
|
|
+ <LinkEditModal />
|
|
|
+ </>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
);
|
|
|
+
|
|
|
};
|