PageEditorByHackmd.jsx 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. import React from 'react';
  2. import PropTypes from 'prop-types';
  3. import * as toastr from 'toastr';
  4. import HackmdEditor from './PageEditorByHackmd/HackmdEditor';
  5. export default class PageEditorByHackmd extends React.PureComponent {
  6. constructor(props) {
  7. super(props);
  8. this.state = {
  9. markdown: this.props.markdown,
  10. isInitialized: false,
  11. isInitializing: false,
  12. revisionId: this.props.revisionId,
  13. pageIdOnHackmd: this.props.pageIdOnHackmd,
  14. };
  15. this.getHackmdUri = this.getHackmdUri.bind(this);
  16. this.startToEdit = this.startToEdit.bind(this);
  17. this.resumeToEdit = this.resumeToEdit.bind(this);
  18. this.hackmdEditorChangeHandler = this.hackmdEditorChangeHandler.bind(this);
  19. this.apiErrorHandler = this.apiErrorHandler.bind(this);
  20. }
  21. componentWillMount() {
  22. }
  23. setMarkdown(markdown) {
  24. this.setState({ markdown });
  25. if (this.refs.hackmdEditor != null) {
  26. this.refs.hackmdEditor.setValue(markdown);
  27. }
  28. }
  29. /**
  30. * update revisionId of state
  31. * @param {string} revisionId
  32. */
  33. setRevisionId(revisionId) {
  34. this.setState({revisionId});
  35. }
  36. getHackmdUri() {
  37. const envVars = this.props.crowi.config.env;
  38. return envVars.HACKMD_URI;
  39. }
  40. /**
  41. * Start integration with HackMD
  42. */
  43. startToEdit() {
  44. const hackmdUri = this.getHackmdUri();
  45. if (hackmdUri == null) {
  46. // do nothing
  47. return;
  48. }
  49. this.setState({
  50. isInitialized: false,
  51. isInitializing: true,
  52. });
  53. const params = {
  54. pageId: this.props.pageId,
  55. };
  56. this.props.crowi.apiPost('/hackmd.integrate', params)
  57. .then(res => {
  58. if (!res.ok) {
  59. throw new Error(res.error);
  60. }
  61. this.setState({
  62. isInitialized: true,
  63. pageIdOnHackmd: res.pageIdOnHackmd,
  64. revisionIdHackmdSynced: res.revisionIdHackmdSynced,
  65. });
  66. })
  67. .catch(this.apiErrorHandler)
  68. .then(() => {
  69. this.setState({isInitializing: false});
  70. });
  71. }
  72. /**
  73. * Start to edit w/o any api request
  74. */
  75. resumeToEdit() {
  76. this.setState({isInitialized: true});
  77. }
  78. /**
  79. * onChange event of HackmdEditor handler
  80. */
  81. hackmdEditorChangeHandler(body) {
  82. const hackmdUri = this.getHackmdUri();
  83. if (hackmdUri == null) {
  84. // do nothing
  85. return;
  86. }
  87. // do nothing if contents are same
  88. if (this.props.markdown === body) {
  89. return;
  90. }
  91. const params = {
  92. pageId: this.props.pageId,
  93. };
  94. this.props.crowi.apiPost('/hackmd.saveOnHackmd', params)
  95. .then(res => {
  96. // do nothing
  97. })
  98. .catch(err => {
  99. // do nothing
  100. });
  101. }
  102. apiErrorHandler(error) {
  103. toastr.error(error.message, 'Error occured', {
  104. closeButton: true,
  105. progressBar: true,
  106. newestOnTop: false,
  107. showDuration: '100',
  108. hideDuration: '100',
  109. timeOut: '3000',
  110. });
  111. }
  112. render() {
  113. const hackmdUri = this.getHackmdUri();
  114. const isPageExistsOnHackmd = (this.state.pageIdOnHackmd != null);
  115. const isRevisionMatch = (this.state.revisionId === this.props.revisionIdHackmdSynced);
  116. const isResume = isPageExistsOnHackmd && isRevisionMatch && this.props.hasDraftOnHackmd;
  117. if (this.state.isInitialized) {
  118. return (
  119. <HackmdEditor
  120. ref='hackmdEditor'
  121. hackmdUri={hackmdUri}
  122. pageIdOnHackmd={this.state.pageIdOnHackmd}
  123. initializationMarkdown={isResume ? null : this.state.markdown}
  124. onChange={this.hackmdEditorChangeHandler}
  125. onSaveWithShortcut={(document) => {
  126. this.props.onSaveWithShortcut(document);
  127. }}
  128. >
  129. </HackmdEditor>
  130. );
  131. }
  132. let content = undefined;
  133. // HackMD is not setup
  134. if (hackmdUri == null) {
  135. content = (
  136. <div>
  137. <p className="text-center hackmd-status-label"><i className="fa fa-file-text"></i> HackMD is not set up.</p>
  138. </div>
  139. );
  140. }
  141. else if (isResume) {
  142. content = (
  143. <div>
  144. <p className="text-center hackmd-status-label"><i className="fa fa-file-text"></i> HackMD is READY!</p>
  145. <p className="text-center">
  146. <button className="btn btn-success btn-lg waves-effect waves-light" type="button"
  147. onClick={() => this.resumeToEdit()}>
  148. <span className="btn-label"><i className="icon-control-end"></i></span>
  149. Resume to edit with HackMD
  150. </button>
  151. </p>
  152. <p className="text-center">Click to edit from the previous continuation.</p>
  153. </div>
  154. );
  155. }
  156. else {
  157. content = (
  158. <div>
  159. <p className="text-center hackmd-status-label"><i className="fa fa-file-text"></i> HackMD is READY!</p>
  160. <p className="text-center">
  161. <button className="btn btn-info btn-lg waves-effect waves-light" type="button"
  162. onClick={() => this.startToEdit()} disabled={this.state.isInitializing}>
  163. <span className="btn-label"><i className="icon-paper-plane"></i></span>
  164. Start to edit with HackMD
  165. </button>
  166. </p>
  167. <p className="text-center">Click to clone page content and start to edit.</p>
  168. </div>
  169. );
  170. }
  171. return (
  172. <div className="hackmd-preinit d-flex justify-content-center align-items-center">
  173. {content}
  174. </div>
  175. );
  176. }
  177. }
  178. PageEditorByHackmd.propTypes = {
  179. crowi: PropTypes.object.isRequired,
  180. markdown: PropTypes.string.isRequired,
  181. onSaveWithShortcut: PropTypes.func.isRequired,
  182. pageId: PropTypes.string,
  183. revisionId: PropTypes.string,
  184. pageIdOnHackmd: PropTypes.string,
  185. revisionIdHackmdSynced: PropTypes.string,
  186. hasDraftOnHackmd: PropTypes.bool,
  187. };