]> BookStack Code Mirror - bookstack/blob - resources/js/components/page-comments.js
Merge pull request #4286 from BookStackApp/comment_threads
[bookstack] / resources / js / components / page-comments.js
1 import {Component} from './component';
2 import {getLoading, htmlToDom} from '../services/dom';
3
4 export class PageComments extends Component {
5
6     setup() {
7         this.elem = this.$el;
8         this.pageId = Number(this.$opts.pageId);
9
10         // Element references
11         this.container = this.$refs.commentContainer;
12         this.commentCountBar = this.$refs.commentCountBar;
13         this.commentsTitle = this.$refs.commentsTitle;
14         this.addButtonContainer = this.$refs.addButtonContainer;
15         this.replyToRow = this.$refs.replyToRow;
16         this.formContainer = this.$refs.formContainer;
17         this.form = this.$refs.form;
18         this.formInput = this.$refs.formInput;
19         this.formReplyLink = this.$refs.formReplyLink;
20         this.addCommentButton = this.$refs.addCommentButton;
21         this.hideFormButton = this.$refs.hideFormButton;
22         this.removeReplyToButton = this.$refs.removeReplyToButton;
23
24         // Translations
25         this.createdText = this.$opts.createdText;
26         this.countText = this.$opts.countText;
27
28         // Internal State
29         this.parentId = null;
30         this.formReplyText = this.formReplyLink.textContent;
31
32         this.setupListeners();
33     }
34
35     setupListeners() {
36         this.removeReplyToButton.addEventListener('click', this.removeReplyTo.bind(this));
37         this.hideFormButton.addEventListener('click', this.hideForm.bind(this));
38         this.addCommentButton.addEventListener('click', this.showForm.bind(this));
39
40         this.elem.addEventListener('page-comment-delete', () => {
41             this.updateCount();
42             this.hideForm();
43         });
44
45         this.elem.addEventListener('page-comment-reply', event => {
46             this.setReply(event.detail.id, event.detail.element);
47         });
48
49         if (this.form) {
50             this.form.addEventListener('submit', this.saveComment.bind(this));
51         }
52     }
53
54     saveComment(event) {
55         event.preventDefault();
56         event.stopPropagation();
57
58         const loading = getLoading();
59         loading.classList.add('px-l');
60         this.form.after(loading);
61         this.form.toggleAttribute('hidden', true);
62
63         const text = this.formInput.value;
64         const reqData = {
65             text,
66             parent_id: this.parentId || null,
67         };
68
69         window.$http.post(`/comment/${this.pageId}`, reqData).then(resp => {
70             const newElem = htmlToDom(resp.data);
71             this.formContainer.after(newElem);
72             window.$events.success(this.createdText);
73             this.hideForm();
74             this.updateCount();
75         }).catch(err => {
76             this.form.toggleAttribute('hidden', false);
77             window.$events.showValidationErrors(err);
78         });
79
80         this.form.toggleAttribute('hidden', false);
81         loading.remove();
82     }
83
84     updateCount() {
85         const count = this.getCommentCount();
86         this.commentsTitle.textContent = window.trans_plural(this.countText, count, {count});
87     }
88
89     resetForm() {
90         this.formInput.value = '';
91         this.parentId = null;
92         this.replyToRow.toggleAttribute('hidden', true);
93         this.container.append(this.formContainer);
94     }
95
96     showForm() {
97         this.formContainer.toggleAttribute('hidden', false);
98         this.addButtonContainer.toggleAttribute('hidden', true);
99         this.formContainer.scrollIntoView({behavior: 'smooth', block: 'nearest'});
100         setTimeout(() => {
101             this.formInput.focus();
102         }, 100);
103     }
104
105     hideForm() {
106         this.resetForm();
107         this.formContainer.toggleAttribute('hidden', true);
108         if (this.getCommentCount() > 0) {
109             this.elem.append(this.addButtonContainer);
110         } else {
111             this.commentCountBar.append(this.addButtonContainer);
112         }
113         this.addButtonContainer.toggleAttribute('hidden', false);
114     }
115
116     getCommentCount() {
117         return this.container.querySelectorAll('[component="page-comment"]').length;
118     }
119
120     setReply(commentLocalId, commentElement) {
121         const targetFormLocation = commentElement.closest('.comment-branch').querySelector('.comment-branch-children');
122         targetFormLocation.append(this.formContainer);
123         this.showForm();
124         this.parentId = commentLocalId;
125         this.replyToRow.toggleAttribute('hidden', false);
126         const replyLink = this.replyToRow.querySelector('a');
127         replyLink.textContent = this.formReplyText.replace('1234', this.parentId);
128         replyLink.href = `#comment${this.parentId}`;
129     }
130
131     removeReplyTo() {
132         this.parentId = null;
133         this.replyToRow.toggleAttribute('hidden', true);
134         this.container.append(this.formContainer);
135         this.showForm();
136     }
137
138 }