2
0

PageComments.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  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. crowi={this.props.crowi}
  104. crowiRenderer={this.props.crowiRenderer}
  105. pagePath={this.props.pagePath} />
  106. );
  107. });
  108. }
  109. render() {
  110. let currentComments = [];
  111. let newerComments = [];
  112. let olderComments = [];
  113. let comments = this.state.comments;
  114. if (this.state.isLayoutTypeGrowi) {
  115. // replace with asc order array
  116. comments = comments.slice().reverse(); // non-destructive reverse
  117. }
  118. // divide by revisionId and createdAt
  119. const revisionId = this.props.revisionId;
  120. const revisionCreatedAt = this.props.revisionCreatedAt;
  121. comments.forEach((comment) => {
  122. if (comment.revision == revisionId) {
  123. currentComments.push(comment);
  124. }
  125. else if (Date.parse(comment.createdAt)/1000 > revisionCreatedAt) {
  126. newerComments.push(comment);
  127. }
  128. else {
  129. olderComments.push(comment);
  130. }
  131. });
  132. // generate elements
  133. const currentElements = this.generateCommentElements(currentComments);
  134. const newerElements = this.generateCommentElements(newerComments);
  135. const olderElements = this.generateCommentElements(olderComments);
  136. // generate blocks
  137. const currentBlock = (
  138. <div className="page-comments-list-current" id="page-comments-list-current">
  139. {currentElements}
  140. </div>
  141. );
  142. const newerBlock = (
  143. <div className="page-comments-list-newer collapse in" id="page-comments-list-newer">
  144. {newerElements}
  145. </div>
  146. );
  147. const olderBlock = (
  148. <div className="page-comments-list-older collapse in" id="page-comments-list-older">
  149. {olderElements}
  150. </div>
  151. );
  152. // generate toggle elements
  153. const iconForNewer = (this.state.isLayoutTypeGrowi)
  154. ? <i className="fa fa-angle-double-down"></i>
  155. : <i className="fa fa-angle-double-up"></i>;
  156. const toggleNewer = (newerElements.length === 0)
  157. ? <div></div>
  158. : (
  159. <a className="page-comments-list-toggle-newer text-center" data-toggle="collapse" href="#page-comments-list-newer">
  160. {iconForNewer} Comments for Newer Revision {iconForNewer}
  161. </a>
  162. );
  163. const iconForOlder = (this.state.isLayoutTypeGrowi)
  164. ? <i className="fa fa-angle-double-up"></i>
  165. : <i className="fa fa-angle-double-down"></i>;
  166. const toggleOlder = (olderElements.length === 0)
  167. ? <div></div>
  168. : (
  169. <a className="page-comments-list-toggle-older text-center" data-toggle="collapse" href="#page-comments-list-older">
  170. {iconForOlder} Comments for Older Revision {iconForOlder}
  171. </a>
  172. );
  173. // layout blocks
  174. const commentsElements = (this.state.isLayoutTypeGrowi)
  175. ? (
  176. <div>
  177. {olderBlock}
  178. {toggleOlder}
  179. {currentBlock}
  180. {toggleNewer}
  181. {newerBlock}
  182. </div>
  183. )
  184. : (
  185. <div>
  186. {newerBlock}
  187. {toggleNewer}
  188. {currentBlock}
  189. {toggleOlder}
  190. {olderBlock}
  191. </div>
  192. );
  193. return (
  194. <div>
  195. {commentsElements}
  196. <DeleteCommentModal
  197. isShown={this.state.isDeleteConfirmModalShown}
  198. comment={this.state.commentToDelete}
  199. errorMessage={this.state.errorMessageForDeleting}
  200. cancel={this.closeDeleteConfirmModal}
  201. confirmedToDelete={this.deleteComment}
  202. />
  203. </div>
  204. );
  205. }
  206. }
  207. PageComments.propTypes = {
  208. pageId: PropTypes.string,
  209. revisionId: PropTypes.string,
  210. revisionCreatedAt: PropTypes.number,
  211. crowi: PropTypes.object.isRequired,
  212. crowiRenderer: PropTypes.object.isRequired,
  213. pagePath: PropTypes.string.isRequired,
  214. highlightKeywords: PropTypes.string,
  215. };