PageEditorByHackmd.jsx 5.1 KB

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