PageComments.js 6.8 KB

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