PageComments.js 6.3 KB

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