]> BookStack Code Mirror - bookstack/blob - resources/js/components/page-comments.js
Merge branch 'auth' of git://github.com/benrubson/BookStack into benrubson-auth
[bookstack] / resources / js / components / page-comments.js
1 import {scrollAndHighlightElement} from "../services/util";
2
3 class PageComments {
4
5     constructor(elem) {
6         this.elem = elem;
7         this.pageId = Number(elem.getAttribute('page-id'));
8         this.editingComment = null;
9         this.parentId = null;
10
11         this.container = elem.querySelector('[comment-container]');
12         this.formContainer = elem.querySelector('[comment-form-container]');
13
14         if (this.formContainer) {
15             this.form = this.formContainer.querySelector('form');
16             this.formInput = this.form.querySelector('textarea');
17             this.form.addEventListener('submit', this.saveComment.bind(this));
18         }
19
20         this.elem.addEventListener('click', this.handleAction.bind(this));
21         this.elem.addEventListener('submit', this.updateComment.bind(this));
22     }
23
24     handleAction(event) {
25         let actionElem = event.target.closest('[action]');
26
27         if (event.target.matches('a[href^="#"]')) {
28             const id = event.target.href.split('#')[1];
29             scrollAndHighlightElement(document.querySelector('#' + id));
30         }
31
32         if (actionElem === null) return;
33         event.preventDefault();
34
35         let action = actionElem.getAttribute('action');
36         if (action === 'edit') this.editComment(actionElem.closest('[comment]'));
37         if (action === 'closeUpdateForm') this.closeUpdateForm();
38         if (action === 'delete') this.deleteComment(actionElem.closest('[comment]'));
39         if (action === 'addComment') this.showForm();
40         if (action === 'hideForm') this.hideForm();
41         if (action === 'reply') this.setReply(actionElem.closest('[comment]'));
42         if (action === 'remove-reply-to') this.removeReplyTo();
43     }
44
45     closeUpdateForm() {
46         if (!this.editingComment) return;
47         this.editingComment.querySelector('[comment-content]').style.display = 'block';
48         this.editingComment.querySelector('[comment-edit-container]').style.display = 'none';
49     }
50
51     editComment(commentElem) {
52         this.hideForm();
53         if (this.editingComment) this.closeUpdateForm();
54         commentElem.querySelector('[comment-content]').style.display = 'none';
55         commentElem.querySelector('[comment-edit-container]').style.display = 'block';
56         let textArea = commentElem.querySelector('[comment-edit-container] textarea');
57         let lineCount = textArea.value.split('\n').length;
58         textArea.style.height = ((lineCount * 20) + 40) + 'px';
59         this.editingComment = commentElem;
60     }
61
62     updateComment(event) {
63         let form = event.target;
64         event.preventDefault();
65         let text = form.querySelector('textarea').value;
66         let reqData = {
67             text: text,
68             parent_id: this.parentId || null,
69         };
70         this.showLoading(form);
71         let commentId = this.editingComment.getAttribute('comment');
72         window.$http.put(`/ajax/comment/${commentId}`, reqData).then(resp => {
73             let newComment = document.createElement('div');
74             newComment.innerHTML = resp.data;
75             this.editingComment.innerHTML = newComment.children[0].innerHTML;
76             window.$events.emit('success', window.trans('entities.comment_updated_success'));
77             window.components.init(this.editingComment);
78             this.closeUpdateForm();
79             this.editingComment = null;
80             this.hideLoading(form);
81         });
82     }
83
84     deleteComment(commentElem) {
85         let id = commentElem.getAttribute('comment');
86         this.showLoading(commentElem.querySelector('[comment-content]'));
87         window.$http.delete(`/ajax/comment/${id}`).then(resp => {
88             commentElem.parentNode.removeChild(commentElem);
89             window.$events.emit('success', window.trans('entities.comment_deleted_success'));
90             this.updateCount();
91             this.hideForm();
92         });
93     }
94
95     saveComment(event) {
96         event.preventDefault();
97         event.stopPropagation();
98         let text = this.formInput.value;
99         let reqData = {
100             text: text,
101             parent_id: this.parentId || null,
102         };
103         this.showLoading(this.form);
104         window.$http.post(`/ajax/page/${this.pageId}/comment`, reqData).then(resp => {
105             let newComment = document.createElement('div');
106             newComment.innerHTML = resp.data;
107             let newElem = newComment.children[0];
108             this.container.appendChild(newElem);
109             window.components.init(newElem);
110             window.$events.emit('success', window.trans('entities.comment_created_success'));
111             this.resetForm();
112             this.updateCount();
113         });
114     }
115
116     updateCount() {
117         let count = this.container.children.length;
118         this.elem.querySelector('[comments-title]').textContent = window.trans_choice('entities.comment_count', count, {count});
119     }
120
121     resetForm() {
122         this.formInput.value = '';
123         this.formContainer.appendChild(this.form);
124         this.hideForm();
125         this.removeReplyTo();
126         this.hideLoading(this.form);
127     }
128
129     showForm() {
130         this.formContainer.style.display = 'block';
131         this.formContainer.parentNode.style.display = 'block';
132         this.elem.querySelector('[comment-add-button-container]').style.display = 'none';
133         this.formInput.focus();
134         this.formInput.scrollIntoView({behavior: "smooth"});
135     }
136
137     hideForm() {
138         this.formContainer.style.display = 'none';
139         this.formContainer.parentNode.style.display = 'none';
140         const addButtonContainer = this.elem.querySelector('[comment-add-button-container]');
141         if (this.getCommentCount() > 0) {
142             this.elem.appendChild(addButtonContainer)
143         } else {
144             const countBar = this.elem.querySelector('[comment-count-bar]');
145             countBar.appendChild(addButtonContainer);
146         }
147         addButtonContainer.style.display = 'block';
148     }
149
150     getCommentCount() {
151         return this.elem.querySelectorAll('.comment-box[comment]').length;
152     }
153
154     setReply(commentElem) {
155         this.showForm();
156         this.parentId = Number(commentElem.getAttribute('local-id'));
157         this.elem.querySelector('[comment-form-reply-to]').style.display = 'block';
158         let replyLink = this.elem.querySelector('[comment-form-reply-to] a');
159         replyLink.textContent = `#${this.parentId}`;
160         replyLink.href = `#comment${this.parentId}`;
161     }
162
163     removeReplyTo() {
164         this.parentId = null;
165         this.elem.querySelector('[comment-form-reply-to]').style.display = 'none';
166     }
167
168     showLoading(formElem) {
169         const groups = formElem.querySelectorAll('.form-group');
170         for (let group of groups) {
171             group.style.display = 'none';
172         }
173         formElem.querySelector('.form-group.loading').style.display = 'block';
174     }
175
176     hideLoading(formElem) {
177         const groups = formElem.querySelectorAll('.form-group');
178         for (let group of groups) {
179             group.style.display = 'block';
180         }
181         formElem.querySelector('.form-group.loading').style.display = 'none';
182     }
183
184 }
185
186 export default PageComments;