PageComments.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. /* eslint-disable react/no-access-state-in-setstate */
  2. import React from 'react';
  3. import PropTypes from 'prop-types';
  4. import GrowiRenderer from '../util/GrowiRenderer';
  5. import Comment from './PageComment/Comment';
  6. import DeleteCommentModal from './PageComment/DeleteCommentModal';
  7. /**
  8. * Load data of comments and render the list of <Comment />
  9. *
  10. * @author Yuki Takei <yuki@weseek.co.jp>
  11. *
  12. * @export
  13. * @class PageComments
  14. * @extends {React.Component}
  15. */
  16. export default class PageComments extends React.Component {
  17. constructor(props) {
  18. super(props);
  19. this.state = {
  20. // desc order array
  21. comments: [],
  22. isLayoutTypeGrowi: false,
  23. // for deleting comment
  24. commentToDelete: undefined,
  25. isDeleteConfirmModalShown: false,
  26. errorMessageForDeleting: undefined,
  27. };
  28. this.growiRenderer = new GrowiRenderer(this.props.crowi, this.props.crowiOriginRenderer, { mode: 'comment' });
  29. this.init = this.init.bind(this);
  30. this.confirmToDeleteComment = this.confirmToDeleteComment.bind(this);
  31. this.deleteComment = this.deleteComment.bind(this);
  32. this.showDeleteConfirmModal = this.showDeleteConfirmModal.bind(this);
  33. this.closeDeleteConfirmModal = this.closeDeleteConfirmModal.bind(this);
  34. }
  35. componentWillMount() {
  36. this.init();
  37. this.retrieveData = this.retrieveData.bind(this);
  38. }
  39. init() {
  40. if (!this.props.pageId) {
  41. return;
  42. }
  43. const layoutType = this.props.crowi.getConfig().layoutType;
  44. this.setState({ isLayoutTypeGrowi: layoutType === 'crowi-plus' || layoutType === 'growi' });
  45. this.retrieveData();
  46. }
  47. /**
  48. * Load data of comments and store them in state
  49. */
  50. retrieveData() {
  51. // get data (desc order array)
  52. this.props.crowi.apiGet('/comments.get', { page_id: this.props.pageId })
  53. .then((res) => {
  54. if (res.ok) {
  55. this.setState({ comments: res.comments });
  56. }
  57. });
  58. }
  59. confirmToDeleteComment(comment) {
  60. this.setState({ commentToDelete: comment });
  61. this.showDeleteConfirmModal();
  62. }
  63. deleteComment() {
  64. const comment = this.state.commentToDelete;
  65. this.props.crowi.apiPost('/comments.remove', { comment_id: comment._id })
  66. .then((res) => {
  67. if (res.ok) {
  68. this.findAndSplice(comment);
  69. }
  70. this.closeDeleteConfirmModal();
  71. })
  72. .catch((err) => {
  73. this.setState({ errorMessageForDeleting: err.message });
  74. });
  75. }
  76. findAndSplice(comment) {
  77. const comments = this.state.comments;
  78. const index = comments.indexOf(comment);
  79. if (index < 0) {
  80. return;
  81. }
  82. comments.splice(index, 1);
  83. this.setState({ comments });
  84. }
  85. showDeleteConfirmModal() {
  86. this.setState({ isDeleteConfirmModalShown: true });
  87. }
  88. closeDeleteConfirmModal() {
  89. this.setState({
  90. commentToDelete: undefined,
  91. isDeleteConfirmModalShown: false,
  92. errorMessageForDeleting: undefined,
  93. });
  94. }
  95. /**
  96. * generate Elements of Comment
  97. *
  98. * @param {any} comments Array of Comment Model Obj
  99. *
  100. * @memberOf PageComments
  101. */
  102. generateCommentElements(comments) {
  103. return comments.map((comment) => {
  104. return (
  105. <Comment
  106. key={comment._id}
  107. comment={comment}
  108. currentUserId={this.props.crowi.me}
  109. currentRevisionId={this.props.revisionId}
  110. deleteBtnClicked={this.confirmToDeleteComment}
  111. crowi={this.props.crowi}
  112. crowiRenderer={this.growiRenderer}
  113. />
  114. );
  115. });
  116. }
  117. render() {
  118. const currentComments = [];
  119. const newerComments = [];
  120. const olderComments = [];
  121. let comments = this.state.comments;
  122. if (this.state.isLayoutTypeGrowi) {
  123. // replace with asc order array
  124. comments = comments.slice().reverse(); // non-destructive reverse
  125. }
  126. // divide by revisionId and createdAt
  127. const revisionId = this.props.revisionId;
  128. const revisionCreatedAt = this.props.revisionCreatedAt;
  129. comments.forEach((comment) => {
  130. // comparing ObjectId
  131. // eslint-disable-next-line eqeqeq
  132. if (comment.revision == revisionId) {
  133. currentComments.push(comment);
  134. }
  135. else if (Date.parse(comment.createdAt) / 1000 > revisionCreatedAt) {
  136. newerComments.push(comment);
  137. }
  138. else {
  139. olderComments.push(comment);
  140. }
  141. });
  142. // generate elements
  143. const currentElements = this.generateCommentElements(currentComments);
  144. const newerElements = this.generateCommentElements(newerComments);
  145. const olderElements = this.generateCommentElements(olderComments);
  146. // generate blocks
  147. const currentBlock = (
  148. <div className="page-comments-list-current" id="page-comments-list-current">
  149. {currentElements}
  150. </div>
  151. );
  152. const newerBlock = (
  153. <div className="page-comments-list-newer collapse in" id="page-comments-list-newer">
  154. {newerElements}
  155. </div>
  156. );
  157. const olderBlock = (
  158. <div className="page-comments-list-older collapse in" id="page-comments-list-older">
  159. {olderElements}
  160. </div>
  161. );
  162. // generate toggle elements
  163. const iconForNewer = (this.state.isLayoutTypeGrowi)
  164. ? <i className="fa fa-angle-double-down"></i>
  165. : <i className="fa fa-angle-double-up"></i>;
  166. const toggleNewer = (newerElements.length === 0)
  167. ? <div></div>
  168. : (
  169. <a className="page-comments-list-toggle-newer text-center" data-toggle="collapse" href="#page-comments-list-newer">
  170. {iconForNewer} Comments for Newer Revision {iconForNewer}
  171. </a>
  172. );
  173. const iconForOlder = (this.state.isLayoutTypeGrowi)
  174. ? <i className="fa fa-angle-double-up"></i>
  175. : <i className="fa fa-angle-double-down"></i>;
  176. const toggleOlder = (olderElements.length === 0)
  177. ? <div></div>
  178. : (
  179. <a className="page-comments-list-toggle-older text-center" data-toggle="collapse" href="#page-comments-list-older">
  180. {iconForOlder} Comments for Older Revision {iconForOlder}
  181. </a>
  182. );
  183. // layout blocks
  184. const commentsElements = (this.state.isLayoutTypeGrowi)
  185. ? (
  186. <div>
  187. {olderBlock}
  188. {toggleOlder}
  189. {currentBlock}
  190. {toggleNewer}
  191. {newerBlock}
  192. </div>
  193. )
  194. : (
  195. <div>
  196. {newerBlock}
  197. {toggleNewer}
  198. {currentBlock}
  199. {toggleOlder}
  200. {olderBlock}
  201. </div>
  202. );
  203. return (
  204. <div>
  205. {commentsElements}
  206. <DeleteCommentModal
  207. isShown={this.state.isDeleteConfirmModalShown}
  208. comment={this.state.commentToDelete}
  209. errorMessage={this.state.errorMessageForDeleting}
  210. cancel={this.closeDeleteConfirmModal}
  211. confirmedToDelete={this.deleteComment}
  212. />
  213. </div>
  214. );
  215. }
  216. }
  217. PageComments.propTypes = {
  218. pageId: PropTypes.string,
  219. revisionId: PropTypes.string,
  220. revisionCreatedAt: PropTypes.number,
  221. crowi: PropTypes.object.isRequired,
  222. crowiOriginRenderer: PropTypes.object.isRequired,
  223. };