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