]> BookStack Code Mirror - bookstack/blob - resources/assets/js/components/page-comments.js
Added comment reply and delete confirmation.
[bookstack] / resources / assets / js / components / page-comments.js
1 const MarkdownIt = require("markdown-it");
2 const md = new MarkdownIt({ html: true });
3
4 class PageComments {
5
6     constructor(elem) {
7         this.elem = elem;
8         this.pageId = Number(elem.getAttribute('page-id'));
9
10         this.formContainer = elem.querySelector('[comment-form-container]');
11         this.form = this.formContainer.querySelector('form');
12         this.formInput = this.form.querySelector('textarea');
13         this.container = elem.querySelector('[comment-container]');
14
15         // TODO - Handle elem usage when no permissions
16         this.form.addEventListener('submit', this.saveComment.bind(this));
17         this.elem.addEventListener('click', this.handleAction.bind(this));
18         this.elem.addEventListener('submit', this.updateComment.bind(this));
19
20         this.editingComment = null;
21         this.parentId = null;
22     }
23
24     handleAction(event) {
25         let actionElem = event.target.closest('[action]');
26         if (event.target.matches('a[href^="#"]')) {
27             let id = event.target.href.split('#')[1];
28             console.log(document.querySelector('#' + id));
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         this.editingComment = commentElem;
56     }
57
58     updateComment(event) {
59         let form = event.target;
60         event.preventDefault();
61         let text = form.querySelector('textarea').value;
62         let reqData = {
63             text: text,
64             html: md.render(text),
65             parent_id: this.parentId || null,
66         };
67         // TODO - Loading indicator
68         let commentId = this.editingComment.getAttribute('comment');
69         window.$http.put(window.baseUrl(`/ajax/comment/${commentId}`), reqData).then(resp => {
70             let newComment = document.createElement('div');
71             newComment.innerHTML = resp.data;
72             this.editingComment.innerHTML = newComment.children[0].innerHTML;
73             window.$events.emit('success', window.trans('entities.comment_updated_success'));
74             window.components.init(this.editingComment);
75             this.closeUpdateForm();
76             this.editingComment = null;
77         });
78     }
79
80     deleteComment(commentElem) {
81         let id = commentElem.getAttribute('comment');
82         // TODO - Loading indicator
83         window.$http.delete(window.baseUrl(`/ajax/comment/${id}`)).then(resp => {
84             commentElem.parentNode.removeChild(commentElem);
85             window.$events.emit('success', window.trans('entities.comment_deleted_success'));
86             this.updateCount();
87         });
88     }
89
90     saveComment(event) {
91         event.preventDefault();
92         event.stopPropagation();
93         let text = this.formInput.value;
94         let reqData = {
95             text: text,
96             html: md.render(text),
97             parent_id: this.parentId || null,
98         };
99         // TODO - Loading indicator
100         window.$http.post(window.baseUrl(`/ajax/page/${this.pageId}/comment`), reqData).then(resp => {
101             let newComment = document.createElement('div');
102             newComment.innerHTML = resp.data;
103             let newElem = newComment.children[0];
104             this.container.appendChild(newElem);
105             window.components.init(newElem);
106             window.$events.emit('success', window.trans('entities.comment_created_success'));
107             this.resetForm();
108             this.updateCount();
109         });
110     }
111
112     updateCount() {
113         let count = this.container.children.length;
114         this.elem.querySelector('[comments-title]').textContent = window.trans_choice('entities.comment_count', count, {count});
115     }
116
117     resetForm() {
118         this.formInput.value = '';
119         this.formContainer.appendChild(this.form);
120         this.hideForm();
121         this.removeReplyTo();
122     }
123
124     showForm() {
125         this.formContainer.style.display = 'block';
126         this.formContainer.parentNode.style.display = 'block';
127         this.elem.querySelector('[comment-add-button]').style.display = 'none';
128         this.formInput.focus();
129         window.scrollToElement(this.formInput);
130     }
131
132     hideForm() {
133         this.formContainer.style.display = 'none';
134         this.formContainer.parentNode.style.display = 'none';
135         this.elem.querySelector('[comment-add-button]').style.display = 'block';
136     }
137
138     setReply(commentElem) {
139         this.showForm();
140         this.parentId = Number(commentElem.getAttribute('local-id'));
141         this.elem.querySelector('[comment-form-reply-to]').style.display = 'block';
142         let replyLink = this.elem.querySelector('[comment-form-reply-to] a');
143         replyLink.textContent = `#${this.parentId}`;
144         replyLink.href = `#comment${this.parentId}`;
145     }
146
147     removeReplyTo() {
148         this.parentId = null;
149         this.elem.querySelector('[comment-form-reply-to]').style.display = 'none';
150     }
151
152 }
153
154 // TODO - Go to comment if url param set
155
156
157 module.exports = PageComments;