PageHistory.jsx 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. import React from 'react';
  2. import PropTypes from 'prop-types';
  3. import loggerFactory from '@alias/logger';
  4. import { withTranslation } from 'react-i18next';
  5. import PageRevisionList from './PageHistory/PageRevisionList';
  6. const logger = loggerFactory('growi:PageHistory');
  7. class PageHistory extends React.Component {
  8. constructor(props) {
  9. super(props);
  10. this.state = {
  11. isLoaded: false,
  12. isLoading: false,
  13. errorMessage: null,
  14. revisions: [],
  15. diffOpened: {},
  16. };
  17. this.getPreviousRevision = this.getPreviousRevision.bind(this);
  18. this.onDiffOpenClicked = this.onDiffOpenClicked.bind(this);
  19. }
  20. async componentWillMount() {
  21. const pageId = this.props.pageId;
  22. const shareLinkId = this.props.shareLinkId || null;
  23. if (!pageId) {
  24. return;
  25. }
  26. let res;
  27. try {
  28. this.setState({ isLoading: true });
  29. res = await this.props.crowi.apiGet('/revisions.ids', { page_id: pageId, share_link_id: shareLinkId });
  30. }
  31. catch (err) {
  32. logger.error(err);
  33. this.setState({ errorMessage: err });
  34. return;
  35. }
  36. finally {
  37. this.setState({ isLoading: false });
  38. }
  39. const rev = res.revisions;
  40. const diffOpened = {};
  41. const lastId = rev.length - 1;
  42. res.revisions.forEach((revision, i) => {
  43. const user = revision.author;
  44. if (user) {
  45. rev[i].author = user;
  46. }
  47. if (i === 0 || i === lastId) {
  48. diffOpened[revision._id] = true;
  49. }
  50. else {
  51. diffOpened[revision._id] = false;
  52. }
  53. });
  54. this.setState({
  55. isLoaded: true,
  56. revisions: rev,
  57. diffOpened,
  58. });
  59. // load 0, and last default
  60. if (rev[0]) {
  61. this.fetchPageRevisionBody(rev[0]);
  62. }
  63. if (rev[1]) {
  64. this.fetchPageRevisionBody(rev[1]);
  65. }
  66. if (lastId !== 0 && lastId !== 1 && rev[lastId]) {
  67. this.fetchPageRevisionBody(rev[lastId]);
  68. }
  69. }
  70. getPreviousRevision(currentRevision) {
  71. let cursor = null;
  72. for (const revision of this.state.revisions) {
  73. // comparing ObjectId
  74. // eslint-disable-next-line eqeqeq
  75. if (cursor && cursor._id == currentRevision._id) {
  76. cursor = revision;
  77. break;
  78. }
  79. cursor = revision;
  80. }
  81. return cursor;
  82. }
  83. onDiffOpenClicked(revision) {
  84. const diffOpened = this.state.diffOpened;
  85. const revisionId = revision._id;
  86. diffOpened[revisionId] = !(diffOpened[revisionId]);
  87. this.setState({
  88. diffOpened,
  89. });
  90. this.fetchPageRevisionBody(revision);
  91. this.fetchPageRevisionBody(this.getPreviousRevision(revision));
  92. }
  93. fetchPageRevisionBody(revision) {
  94. const shareLinkId = this.props.shareLinkId || null;
  95. if (revision.body) {
  96. return;
  97. }
  98. this.props.crowi.apiGet('/revisions.get',
  99. { page_id: this.props.pageId, revision_id: revision._id, share_link_id: shareLinkId })
  100. .then((res) => {
  101. if (res.ok) {
  102. this.setState({
  103. revisions: this.state.revisions.map((rev) => {
  104. // comparing ObjectId
  105. // eslint-disable-next-line eqeqeq
  106. if (rev._id == res.revision._id) {
  107. return res.revision;
  108. }
  109. return rev;
  110. }),
  111. });
  112. }
  113. })
  114. .catch((err) => {
  115. });
  116. }
  117. render() {
  118. return (
  119. <div className="mt-4">
  120. { this.state.isLoading && (
  121. <div className="my-5 text-center">
  122. <i className="fa fa-lg fa-spinner fa-pulse mx-auto text-muted"></i>
  123. </div>
  124. ) }
  125. { this.state.errorMessage && (
  126. <div className="my-5">
  127. <div className="text-danger">{this.state.errorMessage}</div>
  128. </div>
  129. ) }
  130. { this.state.isLoaded && (
  131. <PageRevisionList
  132. t={this.props.t}
  133. revisions={this.state.revisions}
  134. diffOpened={this.state.diffOpened}
  135. getPreviousRevision={this.getPreviousRevision}
  136. onDiffOpenClicked={this.onDiffOpenClicked}
  137. />
  138. ) }
  139. </div>
  140. );
  141. }
  142. }
  143. PageHistory.propTypes = {
  144. t: PropTypes.func.isRequired, // i18next
  145. shareLinkId: PropTypes.string,
  146. pageId: PropTypes.string,
  147. crowi: PropTypes.object.isRequired,
  148. };
  149. export default withTranslation()(PageHistory);