PageEditorByHackmd.jsx 5.2 KB

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