]> BookStack Code Mirror - bookstack/blob - resources/js/components/page-comments.js
Comments: Added input wysiwyg for creating/updating comments
[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 text = this.formInput.value;
69         const reqData = {
70             text,
71             parent_id: this.parentId || null,
72         };
73
74         window.$http.post(`/comment/${this.pageId}`, reqData).then(resp => {
75             const newElem = htmlToDom(resp.data);
76             this.formContainer.after(newElem);
77             window.$events.success(this.createdText);
78             this.hideForm();
79             this.updateCount();
80         }).catch(err => {
81             this.form.toggleAttribute('hidden', false);
82             window.$events.showValidationErrors(err);
83         });
84
85         this.form.toggleAttribute('hidden', false);
86         loading.remove();
87     }
88
89     updateCount() {
90         const count = this.getCommentCount();
91         this.commentsTitle.textContent = window.trans_plural(this.countText, count, {count});
92     }
93
94     resetForm() {
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.formContainer.toggleAttribute('hidden', false);
103         this.addButtonContainer.toggleAttribute('hidden', true);
104         this.formContainer.scrollIntoView({behavior: 'smooth', block: 'nearest'});
105         this.loadEditor();
106     }
107
108     hideForm() {
109         this.resetForm();
110         this.formContainer.toggleAttribute('hidden', true);
111         if (this.getCommentCount() > 0) {
112             this.elem.append(this.addButtonContainer);
113         } else {
114             this.commentCountBar.append(this.addButtonContainer);
115         }
116         this.addButtonContainer.toggleAttribute('hidden', false);
117     }
118
119     loadEditor() {
120         if (this.wysiwygEditor) {
121             return;
122         }
123
124         const config = buildForInput({
125             language: this.wysiwygLanguage,
126             containerElement: this.formInput,
127             darkMode: document.documentElement.classList.contains('dark-mode'),
128             textDirection: this.wysiwygTextDirection,
129             translations: {},
130             translationMap: window.editor_translations,
131         });
132
133         window.tinymce.init(config).then(editors => {
134             this.wysiwygEditor = editors[0];
135             this.wysiwygEditor.focus();
136         });
137     }
138
139     getCommentCount() {
140         return this.container.querySelectorAll('[component="page-comment"]').length;
141     }
142
143     setReply(commentLocalId, commentElement) {
144         const targetFormLocation = commentElement.closest('.comment-branch').querySelector('.comment-branch-children');
145         targetFormLocation.append(this.formContainer);
146         this.showForm();
147         this.parentId = commentLocalId;
148         this.replyToRow.toggleAttribute('hidden', false);
149         this.formReplyLink.textContent = this.formReplyText.replace('1234', this.parentId);
150         this.formReplyLink.href = `#comment${this.parentId}`;
151     }
152
153     removeReplyTo() {
154         this.parentId = null;
155         this.replyToRow.toggleAttribute('hidden', true);
156         this.container.append(this.formContainer);
157         this.showForm();
158     }
159
160 }