PageComments.js 6.6 KB

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