]> BookStack Code Mirror - bookstack/blob - resources/assets/js/components/page-comments.js
Updated the design of the comments section
[bookstack] / resources / assets / js / components / page-comments.js
1 import MarkdownIt from "markdown-it";
2 const md = new MarkdownIt({ html: false });
3
4 class PageComments {
5
6     constructor(elem) {
7         this.elem = elem;
8         this.pageId = Number(elem.getAttribute('page-id'));
9         this.editingComment = null;
10         this.parentId = null;
11
12         this.container = elem.querySelector('[comment-container]');
13         this.formContainer = elem.querySelector('[comment-form-container]');
14
15         if (this.formContainer) {
16             this.form = this.formContainer.querySelector('form');
17             this.formInput = this.form.querySelector('textarea');
18             this.form.addEventListener('submit', this.saveComment.bind(this));
19         }
20
21         this.elem.addEventListener('click', this.handleAction.bind(this));
22         this.elem.addEventListener('submit', this.updateComment.bind(this));
23     }
24
25     handleAction(event) {
26         let actionElem = event.target.closest('[action]');
27         if (event.target.matches('a[href^="#"]')) {
28             let id = event.target.href.split('#')[1];
29             window.scrollAndHighlight(document.querySelector('#' + id));
30         }
31         if (actionElem === null) return;
32         event.preventDefault();
33
34         let action = actionElem.getAttribute('action');
35         if (action === 'edit') this.editComment(actionElem.closest('[comment]'));
36         if (action === 'closeUpdateForm') this.closeUpdateForm();
37         if (action === 'delete') this.deleteComment(actionElem.closest('[comment]'));
38         if (action === 'addComment') this.showForm();
39         if (action === 'hideForm') this.hideForm();
40         if (action === 'reply') this.setReply(actionElem.closest('[comment]'));
41         if (action === 'remove-reply-to') this.removeReplyTo();
42     }
43
44     closeUpdateForm() {
45         if (!this.editingComment) return;
46         this.editingComment.querySelector('[comment-content]').style.display = 'block';
47         this.editingComment.querySelector('[comment-edit-container]').style.display = 'none';
48     }
49
50     editComment(commentElem) {
51         this.hideForm();
52         if (this.editingComment) this.closeUpdateForm();
53         commentElem.querySelector('[comment-content]').style.display = 'none';
54         commentElem.querySelector('[comment-edit-container]').style.display = 'block';
55         let textArea = commentElem.querySelector('[comment-edit-container] textarea');
56         let lineCount = textArea.value.split('\n').length;
57         textArea.style.height = ((lineCount * 20) + 40) + 'px';
58         this.editingComment = commentElem;
59     }
60
61     updateComment(event) {
62         let form = event.target;
63         event.preventDefault();
64         let text = form.querySelector('textarea').value;
65         let reqData = {
66             text: text,
67             html: md.render(text),
68             parent_id: this.parentId || null,
69         };
70         this.showLoading(form);
71         let commentId = this.editingComment.getAttribute('comment');
72         window.$http.put(window.baseUrl(`/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(window.baseUrl(`/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         });
92     }
93
94     saveComment(event) {
95         event.preventDefault();
96         event.stopPropagation();
97         let text = this.formInput.value;
98         let reqData = {
99             text: text,
100             html: md.render(text),
101             parent_id: this.parentId || null,
102         };
103         this.showLoading(this.form);
104         window.$http.post(window.baseUrl(`/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]').style.display = 'none';
133         this.formInput.focus();
134         window.scrollToElement(this.formInput);
135     }
136
137     hideForm() {
138         this.formContainer.style.display = 'none';
139         this.formContainer.parentNode.style.display = 'none';
140         this.elem.querySelector('[comment-add-button]').style.display = 'block';
141     }
142
143     setReply(commentElem) {
144         this.showForm();
145         this.parentId = Number(commentElem.getAttribute('local-id'));
146         this.elem.querySelector('[comment-form-reply-to]').style.display = 'block';
147         let replyLink = this.elem.querySelector('[comment-form-reply-to] a');
148         replyLink.textContent = `#${this.parentId}`;
149         replyLink.href = `#comment${this.parentId}`;
150     }
151
152     removeReplyTo() {
153         this.parentId = null;
154         this.elem.querySelector('[comment-form-reply-to]').style.display = 'none';
155     }
156
157     showLoading(formElem) {
158         let groups = formElem.querySelectorAll('.form-group');
159         for (let i = 0, len = groups.length; i < len; i++) {
160             groups[i].style.display = 'none';
161         }
162         formElem.querySelector('.form-group.loading').style.display = 'block';
163     }
164
165     hideLoading(formElem) {
166         let groups = formElem.querySelectorAll('.form-group');
167         for (let i = 0, len = groups.length; i < len; i++) {
168             groups[i].style.display = 'block';
169         }
170         formElem.querySelector('.form-group.loading').style.display = 'none';
171     }
172
173 }
174
175 export default PageComments;