import React from 'react'; import PropTypes from 'prop-types'; import SplitButton from 'react-bootstrap/es/SplitButton'; import MenuItem from 'react-bootstrap/es/MenuItem'; import * as toastr from 'toastr'; import HackmdEditor from './PageEditorByHackmd/HackmdEditor'; export default class PageEditorByHackmd extends React.PureComponent { constructor(props) { super(props); this.state = { markdown: this.props.markdown, isInitialized: false, isInitializing: false, revisionId: this.props.revisionId, pageIdOnHackmd: this.props.pageIdOnHackmd, hasDraftOnHackmd: this.props.hasDraftOnHackmd, }; this.getHackmdUri = this.getHackmdUri.bind(this); this.startToEdit = this.startToEdit.bind(this); this.resumeToEdit = this.resumeToEdit.bind(this); this.hackmdEditorChangeHandler = this.hackmdEditorChangeHandler.bind(this); this.apiErrorHandler = this.apiErrorHandler.bind(this); } componentWillMount() { } /** * return markdown document of HackMD * @return {Promise} */ getMarkdown() { if (!this.state.isInitialized) { return Promise.reject(new Error('HackmdEditor component has not initialized')); } return this.refs.hackmdEditor.getValue() .then(document => { this.setState({ markdown: document }); return document; }); } setMarkdown(markdown, updateEditorValue = true) { this.setState({ markdown }); if (this.state.isInitialized && updateEditorValue) { this.refs.hackmdEditor.setValue(markdown); } } /** * update revisionId of state * @param {string} revisionId */ setRevisionId(revisionId) { this.setState({revisionId}); } /** * update hasDraftOnHackmd of state * @param {bool} hasDraftOnHackmd */ setHasDraftOnHackmd(hasDraftOnHackmd) { this.setState({hasDraftOnHackmd}); } getHackmdUri() { const envVars = this.props.crowi.config.env; return envVars.HACKMD_URI; } /** * Start integration with HackMD */ startToEdit() { const hackmdUri = this.getHackmdUri(); if (hackmdUri == null) { // do nothing return; } this.setState({ isInitialized: false, isInitializing: true, }); const params = { pageId: this.props.pageId, }; this.props.crowi.apiPost('/hackmd.integrate', params) .then(res => { if (!res.ok) { throw new Error(res.error); } this.setState({ isInitialized: true, pageIdOnHackmd: res.pageIdOnHackmd, revisionIdHackmdSynced: res.revisionIdHackmdSynced, }); }) .catch(this.apiErrorHandler) .then(() => { this.setState({isInitializing: false}); }); } /** * Start to edit w/o any api request */ resumeToEdit() { this.setState({isInitialized: true}); } /** * Reset draft */ discardChanges() { this.setState({hasDraftOnHackmd: false}); } /** * onChange event of HackmdEditor handler */ hackmdEditorChangeHandler(body) { const hackmdUri = this.getHackmdUri(); if (hackmdUri == null) { // do nothing return; } // do nothing if contents are same if (this.props.markdown === body) { return; } const params = { pageId: this.props.pageId, }; this.props.crowi.apiPost('/hackmd.saveOnHackmd', params) .then(res => { // do nothing }) .catch(err => { // do nothing }); } apiErrorHandler(error) { toastr.error(error.message, 'Error occured', { closeButton: true, progressBar: true, newestOnTop: false, showDuration: '100', hideDuration: '100', timeOut: '3000', }); } render() { const hackmdUri = this.getHackmdUri(); const isPageExistsOnHackmd = (this.state.pageIdOnHackmd != null); const isRevisionMatch = (this.state.revisionId === this.props.revisionIdHackmdSynced); const isResume = isPageExistsOnHackmd && isRevisionMatch && this.state.hasDraftOnHackmd; if (this.state.isInitialized) { return ( { this.props.onSaveWithShortcut(document); }} > ); } let content = undefined; // HackMD is not setup if (hackmdUri == null) { content = (

HackMD is not set up.

); } else if (isResume) { const title = ( Resume to edit with HackMD ); content = (

HackMD is READY!

this.resumeToEdit()}> this.discardChanges()}> Discard changes

Click to edit from the previous continuation.

); } else { content = (

HackMD is READY!

Click to clone page content and start to edit.

); } return (
{content}
); } } PageEditorByHackmd.propTypes = { crowi: PropTypes.object.isRequired, markdown: PropTypes.string.isRequired, onSaveWithShortcut: PropTypes.func.isRequired, pageId: PropTypes.string, revisionId: PropTypes.string, pageIdOnHackmd: PropTypes.string, revisionIdHackmdSynced: PropTypes.string, hasDraftOnHackmd: PropTypes.bool, };