|
@@ -1,7 +1,10 @@
|
|
|
-import React from 'react';
|
|
|
|
|
|
|
+import React, {
|
|
|
|
|
+ useCallback, useEffect, useRef, useState,
|
|
|
|
|
+} from 'react';
|
|
|
|
|
|
|
|
-import { useTranslation } from 'next-i18next';
|
|
|
|
|
import PropTypes from 'prop-types';
|
|
import PropTypes from 'prop-types';
|
|
|
|
|
+import { useTranslation } from 'react-i18next';
|
|
|
|
|
+
|
|
|
|
|
|
|
|
import AppContainer from '~/client/services/AppContainer';
|
|
import AppContainer from '~/client/services/AppContainer';
|
|
|
import EditorContainer from '~/client/services/EditorContainer';
|
|
import EditorContainer from '~/client/services/EditorContainer';
|
|
@@ -9,9 +12,7 @@ import PageContainer from '~/client/services/PageContainer';
|
|
|
import { apiPost } from '~/client/util/apiv1-client';
|
|
import { apiPost } from '~/client/util/apiv1-client';
|
|
|
import { getOptionsToSave } from '~/client/util/editor';
|
|
import { getOptionsToSave } from '~/client/util/editor';
|
|
|
import { useCurrentPagePath, useCurrentPageId } from '~/stores/context';
|
|
import { useCurrentPagePath, useCurrentPageId } from '~/stores/context';
|
|
|
-import {
|
|
|
|
|
- useSWRxSlackChannels, useIsSlackEnabled, usePageTagsForEditors, useIsEnabledUnsavedWarning,
|
|
|
|
|
-} from '~/stores/editor';
|
|
|
|
|
|
|
+import { useSWRxSlackChannels, useIsSlackEnabled, usePageTagsForEditors } from '~/stores/editor';
|
|
|
import {
|
|
import {
|
|
|
useEditorMode, useSelectedGrant,
|
|
useEditorMode, useSelectedGrant,
|
|
|
} from '~/stores/ui';
|
|
} from '~/stores/ui';
|
|
@@ -22,86 +23,71 @@ import { withUnstatedContainers } from './UnstatedUtils';
|
|
|
|
|
|
|
|
const logger = loggerFactory('growi:PageEditorByHackmd');
|
|
const logger = loggerFactory('growi:PageEditorByHackmd');
|
|
|
|
|
|
|
|
-class PageEditorByHackmd extends React.Component {
|
|
|
|
|
-
|
|
|
|
|
- constructor(props) {
|
|
|
|
|
- super(props);
|
|
|
|
|
|
|
+const PageEditorByHackmd = (props) => {
|
|
|
|
|
+ const { appContainer, pageContainer, editorContainer } = props; // wip
|
|
|
|
|
|
|
|
- this.state = {
|
|
|
|
|
- isInitialized: false,
|
|
|
|
|
- isInitializing: false,
|
|
|
|
|
- // for error
|
|
|
|
|
- hasError: false,
|
|
|
|
|
- errorMessage: '',
|
|
|
|
|
- errorReason: '',
|
|
|
|
|
|
|
+ const { t } = useTranslation();
|
|
|
|
|
+ const { data: editorMode } = useEditorMode();
|
|
|
|
|
+ const { data: currentPagePath } = useCurrentPagePath();
|
|
|
|
|
+ const { data: slackChannelsData } = useSWRxSlackChannels(currentPagePath);
|
|
|
|
|
+ const { data: isSlackEnabled } = useIsSlackEnabled();
|
|
|
|
|
+ const { data: pageId } = useCurrentPageId();
|
|
|
|
|
+ const { data: pageTags } = usePageTagsForEditors(pageId);
|
|
|
|
|
+ const { data: grant } = useSelectedGrant();
|
|
|
|
|
+
|
|
|
|
|
+ const slackChannels = slackChannelsData.toString();
|
|
|
|
|
+
|
|
|
|
|
+ const [isInitialized, setIsInitialized] = useState(false);
|
|
|
|
|
+ const [isInitializing, setIsInitializing] = useState(false);
|
|
|
|
|
+ // for error
|
|
|
|
|
+ const [hasError, setHasError] = useState(false);
|
|
|
|
|
+ const [errorMessage, setErrorMessage] = useState('');
|
|
|
|
|
+ const [errorReason, setErrorReason] = useState('');
|
|
|
|
|
+
|
|
|
|
|
+ const hackmdEditorRef = useRef(null);
|
|
|
|
|
+
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ const pageEditorByHackmdInstance = {
|
|
|
|
|
+ getMarkdown: () => {
|
|
|
|
|
+ if (!isInitialized) {
|
|
|
|
|
+ return Promise.reject(new Error(t('hackmd.not_initialized')));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return hackmdEditorRef.current.getValue();
|
|
|
|
|
+ },
|
|
|
|
|
+ reset: () => {
|
|
|
|
|
+ setIsInitialized(false);
|
|
|
|
|
+ },
|
|
|
};
|
|
};
|
|
|
|
|
+ appContainer.registerComponentInstance('PageEditorByHackmd', pageEditorByHackmdInstance);
|
|
|
|
|
+ }, [appContainer, isInitialized, t]);
|
|
|
|
|
|
|
|
- this.getHackmdUri = this.getHackmdUri.bind(this);
|
|
|
|
|
- this.startToEdit = this.startToEdit.bind(this);
|
|
|
|
|
- this.resumeToEdit = this.resumeToEdit.bind(this);
|
|
|
|
|
- this.onSaveWithShortcut = this.onSaveWithShortcut.bind(this);
|
|
|
|
|
- this.hackmdEditorChangeHandler = this.hackmdEditorChangeHandler.bind(this);
|
|
|
|
|
- this.penpalErrorOccuredHandler = this.penpalErrorOccuredHandler.bind(this);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- UNSAFE_componentWillMount() {
|
|
|
|
|
- this.props.appContainer.registerComponentInstance('PageEditorByHackmd', this);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * return markdown document of HackMD
|
|
|
|
|
- * @return {Promise<string>}
|
|
|
|
|
- */
|
|
|
|
|
- getMarkdown() {
|
|
|
|
|
- const { t } = this.props;
|
|
|
|
|
- if (!this.state.isInitialized) {
|
|
|
|
|
- return Promise.reject(new Error(t('hackmd.not_initialized')));
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return this.hackmdEditor.getValue();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * reset initialized status
|
|
|
|
|
- */
|
|
|
|
|
- reset() {
|
|
|
|
|
- this.setState({ isInitialized: false });
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- getHackmdUri() {
|
|
|
|
|
- const envVars = this.props.appContainer.getConfig().env;
|
|
|
|
|
|
|
+ const getHackmdUri = useCallback(() => {
|
|
|
|
|
+ const envVars = appContainer.getConfig().env;
|
|
|
return envVars.HACKMD_URI;
|
|
return envVars.HACKMD_URI;
|
|
|
- }
|
|
|
|
|
|
|
+ }, [appContainer]);
|
|
|
|
|
|
|
|
- get isResume() {
|
|
|
|
|
- const { pageContainer } = this.props;
|
|
|
|
|
|
|
+ const isResume = useCallback(() => {
|
|
|
const {
|
|
const {
|
|
|
pageIdOnHackmd, hasDraftOnHackmd, isHackmdDraftUpdatingInRealtime,
|
|
pageIdOnHackmd, hasDraftOnHackmd, isHackmdDraftUpdatingInRealtime,
|
|
|
} = pageContainer.state;
|
|
} = pageContainer.state;
|
|
|
-
|
|
|
|
|
const isPageExistsOnHackmd = (pageIdOnHackmd != null);
|
|
const isPageExistsOnHackmd = (pageIdOnHackmd != null);
|
|
|
return (isPageExistsOnHackmd && hasDraftOnHackmd) || isHackmdDraftUpdatingInRealtime;
|
|
return (isPageExistsOnHackmd && hasDraftOnHackmd) || isHackmdDraftUpdatingInRealtime;
|
|
|
- }
|
|
|
|
|
|
|
+ }, [pageContainer.state]);
|
|
|
|
|
|
|
|
- /**
|
|
|
|
|
- * Start integration with HackMD
|
|
|
|
|
- */
|
|
|
|
|
- async startToEdit() {
|
|
|
|
|
- const { pageContainer } = this.props;
|
|
|
|
|
- const hackmdUri = this.getHackmdUri();
|
|
|
|
|
|
|
+ const startToEdit = useCallback(async() => {
|
|
|
|
|
+ const hackmdUri = getHackmdUri();
|
|
|
|
|
|
|
|
if (hackmdUri == null) {
|
|
if (hackmdUri == null) {
|
|
|
// do nothing
|
|
// do nothing
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- this.setState({
|
|
|
|
|
- isInitialized: false,
|
|
|
|
|
- isInitializing: true,
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ setIsInitialized(false);
|
|
|
|
|
+ setIsInitializing(true);
|
|
|
|
|
|
|
|
const params = {
|
|
const params = {
|
|
|
- pageId: pageContainer.state.pageId,
|
|
|
|
|
|
|
+ pageId,
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
@@ -119,31 +105,23 @@ class PageEditorByHackmd extends React.Component {
|
|
|
catch (err) {
|
|
catch (err) {
|
|
|
pageContainer.showErrorToastr(err);
|
|
pageContainer.showErrorToastr(err);
|
|
|
|
|
|
|
|
- this.setState({
|
|
|
|
|
- hasError: true,
|
|
|
|
|
- errorMessage: 'GROWI server failed to connect to HackMD.',
|
|
|
|
|
- errorReason: err.toString(),
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ setHasError(true);
|
|
|
|
|
+ setErrorMessage('GROWI server failed to connect to HackMD.');
|
|
|
|
|
+ setErrorReason(err.toString());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- this.setState({
|
|
|
|
|
- isInitialized: true,
|
|
|
|
|
- isInitializing: false,
|
|
|
|
|
- });
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ setIsInitialized(true);
|
|
|
|
|
+ setIsInitializing(false);
|
|
|
|
|
+ }, [getHackmdUri, pageContainer, pageId]);
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Start to edit w/o any api request
|
|
* Start to edit w/o any api request
|
|
|
*/
|
|
*/
|
|
|
- resumeToEdit() {
|
|
|
|
|
- this.setState({ isInitialized: true });
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ const resumeToEdit = useCallback(() => {
|
|
|
|
|
+ setIsInitialized(true);
|
|
|
|
|
+ }, []);
|
|
|
|
|
|
|
|
- /**
|
|
|
|
|
- * Reset draft
|
|
|
|
|
- */
|
|
|
|
|
- async discardChanges() {
|
|
|
|
|
- const { pageContainer } = this.props;
|
|
|
|
|
|
|
+ const discardChanges = useCallback(async() => {
|
|
|
const { pageId } = pageContainer.state;
|
|
const { pageId } = pageContainer.state;
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
@@ -153,7 +131,7 @@ class PageEditorByHackmd extends React.Component {
|
|
|
throw new Error(res.error);
|
|
throw new Error(res.error);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- this.props.pageContainer.setState({
|
|
|
|
|
|
|
+ pageContainer.setState({
|
|
|
isHackmdDraftUpdatingInRealtime: false,
|
|
isHackmdDraftUpdatingInRealtime: false,
|
|
|
hasDraftOnHackmd: false,
|
|
hasDraftOnHackmd: false,
|
|
|
pageIdOnHackmd: res.pageIdOnHackmd,
|
|
pageIdOnHackmd: res.pageIdOnHackmd,
|
|
@@ -165,24 +143,21 @@ class PageEditorByHackmd extends React.Component {
|
|
|
logger.error(err);
|
|
logger.error(err);
|
|
|
pageContainer.showErrorToastr(err);
|
|
pageContainer.showErrorToastr(err);
|
|
|
}
|
|
}
|
|
|
- }
|
|
|
|
|
|
|
+ }, [pageContainer]);
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* save and update state of containers
|
|
* save and update state of containers
|
|
|
* @param {string} markdown
|
|
* @param {string} markdown
|
|
|
*/
|
|
*/
|
|
|
- async onSaveWithShortcut(markdown) {
|
|
|
|
|
- const {
|
|
|
|
|
- isSlackEnabled, slackChannels, pageContainer, editorContainer, grant, grantGroupId, grantGroupName, pageTags, mutateIsEnabledUnsavedWarning,
|
|
|
|
|
- } = this.props;
|
|
|
|
|
- const optionsToSave = getOptionsToSave(isSlackEnabled, slackChannels, grant, grantGroupId, grantGroupName, pageTags);
|
|
|
|
|
|
|
+ const onSaveWithShortcut = useCallback(async(markdown) => {
|
|
|
|
|
+ const optionsToSave = getOptionsToSave(isSlackEnabled, slackChannels, grant, grant.grantedGroup.id, grant.grantedGroup.name, pageTags);
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
// disable unsaved warning
|
|
// disable unsaved warning
|
|
|
- mutateIsEnabledUnsavedWarning(false);
|
|
|
|
|
|
|
+ editorContainer.disableUnsavedWarning();
|
|
|
|
|
|
|
|
// eslint-disable-next-line no-unused-vars
|
|
// eslint-disable-next-line no-unused-vars
|
|
|
- const { page, tags } = await pageContainer.save(markdown, this.props.editorMode, optionsToSave);
|
|
|
|
|
|
|
+ const { page, tags } = await pageContainer.save(markdown, editorMode, optionsToSave);
|
|
|
logger.debug('success to save');
|
|
logger.debug('success to save');
|
|
|
|
|
|
|
|
pageContainer.showSuccessToastr();
|
|
pageContainer.showSuccessToastr();
|
|
@@ -194,14 +169,13 @@ class PageEditorByHackmd extends React.Component {
|
|
|
logger.error('failed to save', error);
|
|
logger.error('failed to save', error);
|
|
|
pageContainer.showErrorToastr(error);
|
|
pageContainer.showErrorToastr(error);
|
|
|
}
|
|
}
|
|
|
- }
|
|
|
|
|
|
|
+ }, [editorContainer, editorMode, grant, isSlackEnabled, pageContainer, pageTags, slackChannels]);
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* onChange event of HackmdEditor handler
|
|
* onChange event of HackmdEditor handler
|
|
|
*/
|
|
*/
|
|
|
- async hackmdEditorChangeHandler(body) {
|
|
|
|
|
- const hackmdUri = this.getHackmdUri();
|
|
|
|
|
- const { pageContainer, mutateIsEnabledUnsavedWarning } = this.props;
|
|
|
|
|
|
|
+ const hackmdEditorChangeHandler = useCallback(async(body) => {
|
|
|
|
|
+ const hackmdUri = getHackmdUri();
|
|
|
|
|
|
|
|
if (hackmdUri == null) {
|
|
if (hackmdUri == null) {
|
|
|
// do nothing
|
|
// do nothing
|
|
@@ -214,7 +188,7 @@ class PageEditorByHackmd extends React.Component {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// enable unsaved warning
|
|
// enable unsaved warning
|
|
|
- mutateIsEnabledUnsavedWarning(true);
|
|
|
|
|
|
|
+ editorContainer.enableUnsavedWarning();
|
|
|
|
|
|
|
|
const params = {
|
|
const params = {
|
|
|
pageId: pageContainer.state.pageId,
|
|
pageId: pageContainer.state.pageId,
|
|
@@ -225,23 +199,18 @@ class PageEditorByHackmd extends React.Component {
|
|
|
catch (err) {
|
|
catch (err) {
|
|
|
logger.error(err);
|
|
logger.error(err);
|
|
|
}
|
|
}
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- penpalErrorOccuredHandler(error) {
|
|
|
|
|
- const { pageContainer, t } = this.props;
|
|
|
|
|
|
|
+ }, [editorContainer, getHackmdUri, pageContainer.state.markdown, pageContainer.state.pageId]);
|
|
|
|
|
|
|
|
|
|
+ const penpalErrorOccuredHandler = useCallback((error) => {
|
|
|
pageContainer.showErrorToastr(error);
|
|
pageContainer.showErrorToastr(error);
|
|
|
|
|
|
|
|
- this.setState({
|
|
|
|
|
- hasError: true,
|
|
|
|
|
- errorMessage: t('hackmd.fail_to_connect'),
|
|
|
|
|
- errorReason: error.toString(),
|
|
|
|
|
- });
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ setHasError(true);
|
|
|
|
|
+ setErrorMessage(t('hackmd.fail_to_connect'));
|
|
|
|
|
+ setErrorReason(error.toString());
|
|
|
|
|
+ }, [pageContainer, t]);
|
|
|
|
|
|
|
|
- renderPreInitContent() {
|
|
|
|
|
- const hackmdUri = this.getHackmdUri();
|
|
|
|
|
- const { pageContainer, t } = this.props;
|
|
|
|
|
|
|
+ const renderPreInitContent = useCallback(() => {
|
|
|
|
|
+ const hackmdUri = getHackmdUri();
|
|
|
const {
|
|
const {
|
|
|
revisionId, revisionIdHackmdSynced, remoteRevisionId, pageId,
|
|
revisionId, revisionIdHackmdSynced, remoteRevisionId, pageId,
|
|
|
} = pageContainer.state;
|
|
} = pageContainer.state;
|
|
@@ -280,7 +249,7 @@ class PageEditorByHackmd extends React.Component {
|
|
|
/*
|
|
/*
|
|
|
* Resume to edit or discard changes
|
|
* Resume to edit or discard changes
|
|
|
*/
|
|
*/
|
|
|
- else if (this.isResume) {
|
|
|
|
|
|
|
+ else if (isResume()) {
|
|
|
const isHackmdDocumentOutdated = revisionIdHackmdSynced !== remoteRevisionId;
|
|
const isHackmdDocumentOutdated = revisionIdHackmdSynced !== remoteRevisionId;
|
|
|
|
|
|
|
|
content = (
|
|
content = (
|
|
@@ -299,8 +268,8 @@ class PageEditorByHackmd extends React.Component {
|
|
|
<button
|
|
<button
|
|
|
className="btn btn-link btn-view-outdated-draft p-0"
|
|
className="btn btn-link btn-view-outdated-draft p-0"
|
|
|
type="button"
|
|
type="button"
|
|
|
- disabled={this.state.isInitializing}
|
|
|
|
|
- onClick={() => { return this.resumeToEdit() }}
|
|
|
|
|
|
|
+ disabled={isInitializing}
|
|
|
|
|
+ onClick={resumeToEdit}
|
|
|
>
|
|
>
|
|
|
{t('hackmd.view_outdated_draft')}
|
|
{t('hackmd.view_outdated_draft')}
|
|
|
</button>
|
|
</button>
|
|
@@ -312,10 +281,10 @@ class PageEditorByHackmd extends React.Component {
|
|
|
{ !isHackmdDocumentOutdated && (
|
|
{ !isHackmdDocumentOutdated && (
|
|
|
<div className="text-center hackmd-resume-button-container mb-3">
|
|
<div className="text-center hackmd-resume-button-container mb-3">
|
|
|
<button
|
|
<button
|
|
|
- className="btn btn-success btn-lg"
|
|
|
|
|
|
|
+ className="btn btn-success btn-lg waves-effect waves-light"
|
|
|
type="button"
|
|
type="button"
|
|
|
- disabled={this.state.isInitializing}
|
|
|
|
|
- onClick={() => { return this.resumeToEdit() }}
|
|
|
|
|
|
|
+ disabled={isInitializing}
|
|
|
|
|
+ onClick={resumeToEdit}
|
|
|
>
|
|
>
|
|
|
<span className="btn-label"><i className="icon-fw icon-control-end"></i></span>
|
|
<span className="btn-label"><i className="icon-fw icon-control-end"></i></span>
|
|
|
<span className="btn-text">{t('hackmd.resume_to_edit')}</span>
|
|
<span className="btn-text">{t('hackmd.resume_to_edit')}</span>
|
|
@@ -325,9 +294,9 @@ class PageEditorByHackmd extends React.Component {
|
|
|
|
|
|
|
|
<div className="text-center hackmd-discard-button-container mb-3">
|
|
<div className="text-center hackmd-discard-button-container mb-3">
|
|
|
<button
|
|
<button
|
|
|
- className="btn btn-outline-secondary btn-lg"
|
|
|
|
|
|
|
+ className="btn btn-outline-secondary btn-lg waves-effect waves-light"
|
|
|
type="button"
|
|
type="button"
|
|
|
- onClick={() => { return this.discardChanges() }}
|
|
|
|
|
|
|
+ onClick={discardChanges}
|
|
|
>
|
|
>
|
|
|
<span className="btn-label"><i className="icon-fw icon-control-start"></i></span>
|
|
<span className="btn-label"><i className="icon-fw icon-control-start"></i></span>
|
|
|
<span className="btn-text">{t('hackmd.discard_changes')}</span>
|
|
<span className="btn-text">{t('hackmd.discard_changes')}</span>
|
|
@@ -348,10 +317,10 @@ class PageEditorByHackmd extends React.Component {
|
|
|
<p className="text-muted text-center hackmd-status-label"><i className="fa fa-file-text"></i> HackMD is READY!</p>
|
|
<p className="text-muted text-center hackmd-status-label"><i className="fa fa-file-text"></i> HackMD is READY!</p>
|
|
|
<div className="text-center hackmd-start-button-container mb-3">
|
|
<div className="text-center hackmd-start-button-container mb-3">
|
|
|
<button
|
|
<button
|
|
|
- className="btn btn-info btn-lg"
|
|
|
|
|
|
|
+ className="btn btn-info btn-lg waves-effect waves-light"
|
|
|
type="button"
|
|
type="button"
|
|
|
- disabled={isRevisionOutdated || this.state.isInitializing}
|
|
|
|
|
- onClick={() => { return this.startToEdit() }}
|
|
|
|
|
|
|
+ disabled={isRevisionOutdated || isInitializing}
|
|
|
|
|
+ onClick={startToEdit}
|
|
|
>
|
|
>
|
|
|
<span className="btn-label"><i className="icon-fw icon-paper-plane"></i></span>
|
|
<span className="btn-label"><i className="icon-fw icon-paper-plane"></i></span>
|
|
|
{t('hackmd.start_to_edit')}
|
|
{t('hackmd.start_to_edit')}
|
|
@@ -367,116 +336,75 @@ class PageEditorByHackmd extends React.Component {
|
|
|
{content}
|
|
{content}
|
|
|
</div>
|
|
</div>
|
|
|
);
|
|
);
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- render() {
|
|
|
|
|
- const hackmdUri = this.getHackmdUri();
|
|
|
|
|
- const { pageContainer, t } = this.props;
|
|
|
|
|
- const {
|
|
|
|
|
- markdown, pageIdOnHackmd,
|
|
|
|
|
- } = pageContainer.state;
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- let content;
|
|
|
|
|
-
|
|
|
|
|
- if (this.state.isInitialized) {
|
|
|
|
|
- content = (
|
|
|
|
|
- <HackmdEditor
|
|
|
|
|
- ref={(c) => { this.hackmdEditor = c }}
|
|
|
|
|
- hackmdUri={hackmdUri}
|
|
|
|
|
- pageIdOnHackmd={pageIdOnHackmd}
|
|
|
|
|
- initializationMarkdown={this.isResume ? null : markdown}
|
|
|
|
|
- onChange={this.hackmdEditorChangeHandler}
|
|
|
|
|
- onSaveWithShortcut={(document) => {
|
|
|
|
|
- this.onSaveWithShortcut(document);
|
|
|
|
|
- }}
|
|
|
|
|
- onPenpalErrorOccured={this.penpalErrorOccuredHandler}
|
|
|
|
|
- >
|
|
|
|
|
- </HackmdEditor>
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
- else {
|
|
|
|
|
- content = this.renderPreInitContent();
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ }, [discardChanges, getHackmdUri, isInitializing, isResume, pageContainer.state, resumeToEdit, startToEdit, t]);
|
|
|
|
|
|
|
|
|
|
+ if (editorMode == null) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- return (
|
|
|
|
|
- <div className="position-relative">
|
|
|
|
|
|
|
+ const hackmdUri = getHackmdUri();
|
|
|
|
|
+ const {
|
|
|
|
|
+ markdown, pageIdOnHackmd,
|
|
|
|
|
+ } = pageContainer.state;
|
|
|
|
|
+
|
|
|
|
|
+ let content;
|
|
|
|
|
+
|
|
|
|
|
+ if (isInitialized) {
|
|
|
|
|
+ content = (
|
|
|
|
|
+ <HackmdEditor
|
|
|
|
|
+ ref={hackmdEditorRef}
|
|
|
|
|
+ hackmdUri={hackmdUri}
|
|
|
|
|
+ pageIdOnHackmd={pageIdOnHackmd}
|
|
|
|
|
+ initializationMarkdown={isResume() ? null : markdown}
|
|
|
|
|
+ onChange={hackmdEditorChangeHandler}
|
|
|
|
|
+ onSaveWithShortcut={(document) => {
|
|
|
|
|
+ onSaveWithShortcut(document);
|
|
|
|
|
+ }}
|
|
|
|
|
+ onPenpalErrorOccured={penpalErrorOccuredHandler}
|
|
|
|
|
+ >
|
|
|
|
|
+ </HackmdEditor>
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+ else {
|
|
|
|
|
+ content = renderPreInitContent();
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- {content}
|
|
|
|
|
|
|
|
|
|
- { this.state.hasError && (
|
|
|
|
|
- <div className="hackmd-error position-absolute d-flex flex-column justify-content-center align-items-center">
|
|
|
|
|
- <div className="bg-box p-5 text-center">
|
|
|
|
|
- <h2 className="text-warning"><i className="icon-fw icon-exclamation"></i> {t('hackmd.integration_failed')}</h2>
|
|
|
|
|
- <h4>{this.state.errorMessage}</h4>
|
|
|
|
|
- <p className="card well text-danger">
|
|
|
|
|
- {this.state.errorReason}
|
|
|
|
|
- </p>
|
|
|
|
|
- {/* eslint-disable-next-line react/no-danger */}
|
|
|
|
|
- <p dangerouslySetInnerHTML={{ __html: t('hackmd.check_configuration') }} />
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ return (
|
|
|
|
|
+ <div className="position-relative">
|
|
|
|
|
+
|
|
|
|
|
+ {content}
|
|
|
|
|
+
|
|
|
|
|
+ { hasError && (
|
|
|
|
|
+ <div className="hackmd-error position-absolute d-flex flex-column justify-content-center align-items-center">
|
|
|
|
|
+ <div className="bg-box p-5 text-center">
|
|
|
|
|
+ <h2 className="text-warning"><i className="icon-fw icon-exclamation"></i> {t('hackmd.integration_failed')}</h2>
|
|
|
|
|
+ <h4>{errorMessage}</h4>
|
|
|
|
|
+ <p className="card well text-danger">
|
|
|
|
|
+ {errorReason}
|
|
|
|
|
+ </p>
|
|
|
|
|
+ {/* eslint-disable-next-line react/no-danger */}
|
|
|
|
|
+ <p dangerouslySetInnerHTML={{ __html: t('hackmd.check_configuration') }} />
|
|
|
</div>
|
|
</div>
|
|
|
- ) }
|
|
|
|
|
|
|
+ </div>
|
|
|
|
|
+ ) }
|
|
|
|
|
|
|
|
- </div>
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ </div>
|
|
|
|
|
+ );
|
|
|
|
|
|
|
|
-}
|
|
|
|
|
|
|
+};
|
|
|
|
|
|
|
|
PageEditorByHackmd.propTypes = {
|
|
PageEditorByHackmd.propTypes = {
|
|
|
- t: PropTypes.func.isRequired, // i18next
|
|
|
|
|
-
|
|
|
|
|
appContainer: PropTypes.instanceOf(AppContainer).isRequired,
|
|
appContainer: PropTypes.instanceOf(AppContainer).isRequired,
|
|
|
pageContainer: PropTypes.instanceOf(PageContainer).isRequired,
|
|
pageContainer: PropTypes.instanceOf(PageContainer).isRequired,
|
|
|
editorContainer: PropTypes.instanceOf(EditorContainer).isRequired,
|
|
editorContainer: PropTypes.instanceOf(EditorContainer).isRequired,
|
|
|
|
|
|
|
|
// TODO: remove this when omitting unstated is completed
|
|
// TODO: remove this when omitting unstated is completed
|
|
|
- editorMode: PropTypes.string.isRequired,
|
|
|
|
|
- isSlackEnabled: PropTypes.bool.isRequired,
|
|
|
|
|
pageTags: PropTypes.arrayOf(PropTypes.string),
|
|
pageTags: PropTypes.arrayOf(PropTypes.string),
|
|
|
- slackChannels: PropTypes.string.isRequired,
|
|
|
|
|
- grant: PropTypes.number.isRequired,
|
|
|
|
|
grantGroupId: PropTypes.string,
|
|
grantGroupId: PropTypes.string,
|
|
|
grantGroupName: PropTypes.string,
|
|
grantGroupName: PropTypes.string,
|
|
|
- mutateIsEnabledUnsavedWarning: PropTypes.func,
|
|
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-/**
|
|
|
|
|
- * Wrapper component for using unstated
|
|
|
|
|
- */
|
|
|
|
|
-const PageEditorByHackmdHOCWrapper = withUnstatedContainers(PageEditorByHackmd, [AppContainer, PageContainer, EditorContainer]);
|
|
|
|
|
-
|
|
|
|
|
-const PageEditorByHackmdWrapper = (props) => {
|
|
|
|
|
- const { t } = useTranslation();
|
|
|
|
|
- const { data: editorMode } = useEditorMode();
|
|
|
|
|
- const { data: currentPagePath } = useCurrentPagePath();
|
|
|
|
|
- const { data: slackChannelsData } = useSWRxSlackChannels(currentPagePath);
|
|
|
|
|
- const { data: isSlackEnabled } = useIsSlackEnabled();
|
|
|
|
|
- const { data: pageId } = useCurrentPageId();
|
|
|
|
|
- const { data: pageTags } = usePageTagsForEditors(pageId);
|
|
|
|
|
- const { data: grantData } = useSelectedGrant();
|
|
|
|
|
- const { mutate: mutateIsEnabledUnsavedWarning } = useIsEnabledUnsavedWarning();
|
|
|
|
|
-
|
|
|
|
|
- if (editorMode == null) {
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return (
|
|
|
|
|
- <PageEditorByHackmdHOCWrapper
|
|
|
|
|
- {...props}
|
|
|
|
|
- t={t}
|
|
|
|
|
- editorMode={editorMode}
|
|
|
|
|
- isSlackEnabled={isSlackEnabled}
|
|
|
|
|
- slackChannels={slackChannelsData.toString()}
|
|
|
|
|
- pageTags={pageTags}
|
|
|
|
|
- grant={grantData.grant}
|
|
|
|
|
- grantGroupId={grantData.grantGroup?.id}
|
|
|
|
|
- grantGroupName={grantData.grantedGroup?.name}
|
|
|
|
|
- mutateIsEnabledUnsavedWarning={mutateIsEnabledUnsavedWarning}
|
|
|
|
|
- />
|
|
|
|
|
- );
|
|
|
|
|
-};
|
|
|
|
|
|
|
+const PageEditorByHackmdWrapper = withUnstatedContainers(PageEditorByHackmd, [AppContainer, PageContainer, EditorContainer]);
|
|
|
|
|
|
|
|
export default PageEditorByHackmdWrapper;
|
|
export default PageEditorByHackmdWrapper;
|