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