diff --git a/lang/en/entities.php b/lang/en/entities.php index 501fc9f2a..8499bb30f 100644 --- a/lang/en/entities.php +++ b/lang/en/entities.php @@ -362,8 +362,6 @@ return [ 'comment_placeholder' => 'Leave a comment here', 'comment_count' => '{0} No Comments|{1} 1 Comment|[2,*] :count Comments', 'comment_save' => 'Save Comment', - 'comment_saving' => 'Saving comment...', - 'comment_deleting' => 'Deleting comment...', 'comment_new' => 'New Comment', 'comment_created' => 'commented :createDiff', 'comment_updated' => 'Updated :updateDiff by :username', diff --git a/resources/js/components/index.js b/resources/js/components/index.js index 803714e62..a56f18a5a 100644 --- a/resources/js/components/index.js +++ b/resources/js/components/index.js @@ -34,6 +34,7 @@ export {MarkdownEditor} from './markdown-editor'; export {NewUserPassword} from './new-user-password'; export {Notification} from './notification'; export {OptionalInput} from './optional-input'; +export {PageComment} from './page-comment'; export {PageComments} from './page-comments'; export {PageDisplay} from './page-display'; export {PageEditor} from './page-editor'; diff --git a/resources/js/components/page-comment.js b/resources/js/components/page-comment.js new file mode 100644 index 000000000..ff86297e2 --- /dev/null +++ b/resources/js/components/page-comment.js @@ -0,0 +1,85 @@ +import {Component} from './component'; +import {getLoading, htmlToDom} from '../services/dom'; + +export class PageComment extends Component { + + setup() { + // Options + this.commentId = this.$opts.commentId; + this.commentLocalId = this.$opts.commentLocalId; + this.commentParentId = this.$opts.commentParentId; + this.deletedText = this.$opts.deletedText; + this.updatedText = this.$opts.updatedText; + + // Element References + this.container = this.$el; + this.contentContainer = this.$refs.contentContainer; + this.form = this.$refs.form; + this.formCancel = this.$refs.formCancel; + this.editButton = this.$refs.editButton; + this.deleteButton = this.$refs.deleteButton; + this.replyButton = this.$refs.replyButton; + this.input = this.$refs.input; + + this.setupListeners(); + } + + setupListeners() { + this.replyButton.addEventListener('click', () => this.$emit('reply', {id: this.commentLocalId})); + this.editButton.addEventListener('click', this.startEdit.bind(this)); + this.deleteButton.addEventListener('click', this.delete.bind(this)); + this.form.addEventListener('submit', this.update.bind(this)); + this.formCancel.addEventListener('click', () => this.toggleEditMode(false)); + } + + toggleEditMode(show) { + this.contentContainer.toggleAttribute('hidden', show); + this.form.toggleAttribute('hidden', !show); + } + + startEdit() { + this.toggleEditMode(true); + const lineCount = this.$refs.input.value.split('\n').length; + this.$refs.input.style.height = `${(lineCount * 20) + 40}px`; + } + + async update(event) { + event.preventDefault(); + const loading = this.showLoading(); + this.form.toggleAttribute('hidden', true); + + const reqData = { + text: this.input.value, + parent_id: this.parentId || null, + }; + + try { + const resp = await window.$http.put(`/comment/${this.commentId}`, reqData); + const newComment = htmlToDom(resp.data); + this.container.replaceWith(newComment); + window.$events.success(this.updatedText); + } catch (err) { + console.error(err); + window.$events.showValidationErrors(err); + this.form.toggleAttribute('hidden', false); + loading.remove(); + } + } + + async delete() { + this.showLoading(); + + await window.$http.delete(`/comment/${this.commentId}`); + this.container.closest('.comment-branch').remove(); + window.$events.success(this.deletedText); + this.$emit('delete'); + } + + showLoading() { + const loading = getLoading(); + loading.classList.add('px-l'); + this.container.append(loading); + return loading; + } + +} diff --git a/resources/js/components/page-comments.js b/resources/js/components/page-comments.js index 0ac9d0572..9dc529963 100644 --- a/resources/js/components/page-comments.js +++ b/resources/js/components/page-comments.js @@ -1,6 +1,5 @@ -import {scrollAndHighlightElement} from '../services/util'; import {Component} from './component'; -import {htmlToDom} from '../services/dom'; +import {getLoading, htmlToDom} from '../services/dom'; export class PageComments extends Component { @@ -10,166 +9,110 @@ export class PageComments extends Component { // Element references this.container = this.$refs.commentContainer; - this.formContainer = this.$refs.formContainer; this.commentCountBar = this.$refs.commentCountBar; + this.commentsTitle = this.$refs.commentsTitle; this.addButtonContainer = this.$refs.addButtonContainer; this.replyToRow = this.$refs.replyToRow; + this.formContainer = this.$refs.formContainer; + this.form = this.$refs.form; + this.formInput = this.$refs.formInput; + this.addCommentButton = this.$refs.addCommentButton; + this.hideFormButton = this.$refs.hideFormButton; + this.removeReplyToButton = this.$refs.removeReplyToButton; // Translations - this.updatedText = this.$opts.updatedText; - this.deletedText = this.$opts.deletedText; this.createdText = this.$opts.createdText; this.countText = this.$opts.countText; // Internal State - this.editingComment = null; this.parentId = null; - if (this.formContainer) { - this.form = this.formContainer.querySelector('form'); - this.formInput = this.form.querySelector('textarea'); - this.form.addEventListener('submit', this.saveComment.bind(this)); - } - - this.elem.addEventListener('click', this.handleAction.bind(this)); - this.elem.addEventListener('submit', this.updateComment.bind(this)); + this.setupListeners(); } - handleAction(event) { - const actionElem = event.target.closest('[action]'); + setupListeners() { + this.removeReplyToButton.addEventListener('click', this.removeReplyTo.bind(this)); + this.hideFormButton.addEventListener('click', this.hideForm.bind(this)); + this.addCommentButton.addEventListener('click', this.showForm.bind(this)); - if (event.target.matches('a[href^="#"]')) { - const id = event.target.href.split('#')[1]; - scrollAndHighlightElement(document.querySelector(`#${id}`)); - } - - if (actionElem === null) return; - event.preventDefault(); - - const action = actionElem.getAttribute('action'); - const comment = actionElem.closest('[comment]'); - if (action === 'edit') this.editComment(comment); - if (action === 'closeUpdateForm') this.closeUpdateForm(); - if (action === 'delete') this.deleteComment(comment); - if (action === 'addComment') this.showForm(); - if (action === 'hideForm') this.hideForm(); - if (action === 'reply') this.setReply(comment); - if (action === 'remove-reply-to') this.removeReplyTo(); - } - - closeUpdateForm() { - if (!this.editingComment) return; - this.editingComment.querySelector('[comment-content]').style.display = 'block'; - this.editingComment.querySelector('[comment-edit-container]').style.display = 'none'; - } - - editComment(commentElem) { - this.hideForm(); - if (this.editingComment) this.closeUpdateForm(); - commentElem.querySelector('[comment-content]').style.display = 'none'; - commentElem.querySelector('[comment-edit-container]').style.display = 'block'; - const textArea = commentElem.querySelector('[comment-edit-container] textarea'); - const lineCount = textArea.value.split('\n').length; - textArea.style.height = `${(lineCount * 20) + 40}px`; - this.editingComment = commentElem; - } - - updateComment(event) { - const form = event.target; - event.preventDefault(); - const text = form.querySelector('textarea').value; - const reqData = { - text, - parent_id: this.parentId || null, - }; - this.showLoading(form); - const commentId = this.editingComment.getAttribute('comment'); - window.$http.put(`/comment/${commentId}`, reqData).then(resp => { - const newComment = document.createElement('div'); - newComment.innerHTML = resp.data; - this.editingComment.innerHTML = newComment.children[0].innerHTML; - window.$events.success(this.updatedText); - window.$components.init(this.editingComment); - this.closeUpdateForm(); - this.editingComment = null; - }).catch(window.$events.showValidationErrors).then(() => { - this.hideLoading(form); - }); - } - - deleteComment(commentElem) { - const id = commentElem.getAttribute('comment'); - this.showLoading(commentElem.querySelector('[comment-content]')); - window.$http.delete(`/comment/${id}`).then(() => { - commentElem.parentNode.removeChild(commentElem); - window.$events.success(this.deletedText); + this.elem.addEventListener('page-comment-delete', () => { this.updateCount(); this.hideForm(); }); + + this.elem.addEventListener('page-comment-reply', event => { + this.setReply(event.detail.id); + }); + + if (this.form) { + this.form.addEventListener('submit', this.saveComment.bind(this)); + } } saveComment(event) { event.preventDefault(); event.stopPropagation(); + + const loading = getLoading(); + loading.classList.add('px-l'); + this.form.after(loading); + this.form.toggleAttribute('hidden', true); + const text = this.formInput.value; const reqData = { text, parent_id: this.parentId || null, }; - this.showLoading(this.form); + window.$http.post(`/comment/${this.pageId}`, reqData).then(resp => { const newElem = htmlToDom(resp.data); this.container.appendChild(newElem); - window.$components.init(newElem); window.$events.success(this.createdText); this.resetForm(); this.updateCount(); }).catch(err => { + this.form.toggleAttribute('hidden', false); window.$events.showValidationErrors(err); - this.hideLoading(this.form); }); + + loading.remove(); } updateCount() { - const count = this.container.children.length; - this.elem.querySelector('[comments-title]').textContent = window.trans_plural(this.countText, count, {count}); + const count = this.getCommentCount(); + this.commentsTitle.textContent = window.trans_plural(this.countText, count, {count}); } resetForm() { this.formInput.value = ''; - this.formContainer.appendChild(this.form); this.hideForm(); this.removeReplyTo(); - this.hideLoading(this.form); } showForm() { - this.formContainer.style.display = 'block'; - this.formContainer.parentNode.style.display = 'block'; - this.addButtonContainer.style.display = 'none'; + this.formContainer.toggleAttribute('hidden', false); + this.addButtonContainer.toggleAttribute('hidden', true); this.formInput.focus(); - this.formInput.scrollIntoView({behavior: 'smooth'}); } hideForm() { - this.formContainer.style.display = 'none'; - this.formContainer.parentNode.style.display = 'none'; + this.formContainer.toggleAttribute('hidden', true); if (this.getCommentCount() > 0) { this.elem.appendChild(this.addButtonContainer); } else { this.commentCountBar.appendChild(this.addButtonContainer); } - this.addButtonContainer.style.display = 'block'; + this.addButtonContainer.toggleAttribute('hidden', false); } getCommentCount() { - return this.elem.querySelectorAll('.comment-box[comment]').length; + return this.container.querySelectorAll('[compontent="page-comment"]').length; } - setReply(commentElem) { + setReply(commentLocalId) { this.showForm(); - this.parentId = Number(commentElem.getAttribute('local-id')); - this.replyToRow.style.display = 'block'; + this.parentId = commentLocalId; + this.replyToRow.toggleAttribute('hidden', false); const replyLink = this.replyToRow.querySelector('a'); replyLink.textContent = `#${this.parentId}`; replyLink.href = `#comment${this.parentId}`; @@ -177,23 +120,7 @@ export class PageComments extends Component { removeReplyTo() { this.parentId = null; - this.replyToRow.style.display = 'none'; - } - - showLoading(formElem) { - const groups = formElem.querySelectorAll('.form-group'); - for (const group of groups) { - group.style.display = 'none'; - } - formElem.querySelector('.form-group.loading').style.display = 'block'; - } - - hideLoading(formElem) { - const groups = formElem.querySelectorAll('.form-group'); - for (const group of groups) { - group.style.display = 'block'; - } - formElem.querySelector('.form-group.loading').style.display = 'none'; + this.replyToRow.toggleAttribute('hidden', true); } } diff --git a/resources/views/comments/comment-branch.blade.php b/resources/views/comments/comment-branch.blade.php index d64dd4ade..69c967cd2 100644 --- a/resources/views/comments/comment-branch.blade.php +++ b/resources/views/comments/comment-branch.blade.php @@ -1,4 +1,4 @@ -
+
@include('comments.comment', ['comment' => $branch['comment']])
diff --git a/resources/views/comments/comment.blade.php b/resources/views/comments/comment.blade.php index 093e5a899..ce0d59473 100644 --- a/resources/views/comments/comment.blade.php +++ b/resources/views/comments/comment.blade.php @@ -1,4 +1,11 @@ -
+
@@ -21,10 +28,10 @@
@if(userCan('comment-update', $comment)) - + @endif @if(userCan('comment-create-all')) - + @endif @if(userCan('comment-delete', $comment)) @endif -
- +
{!! $comment->html !!}
@if(userCan('comment-update', $comment)) - + @endif
\ No newline at end of file diff --git a/resources/views/comments/comments.blade.php b/resources/views/comments/comments.blade.php index f50e3a218..b79f0fd45 100644 --- a/resources/views/comments/comments.blade.php +++ b/resources/views/comments/comments.blade.php @@ -1,17 +1,16 @@
-
-
{{ trans_choice('entities.comment_count', $commentTree->count(), ['count' => $commentTree->count()]) }}
+
+
{{ trans_choice('entities.comment_count', $commentTree->count(), ['count' => $commentTree->count()]) }}
@if ($commentTree->empty() && userCan('comment-create-all')) -
-
@endif @@ -28,7 +27,8 @@ @if (!$commentTree->empty())
-
@endif diff --git a/resources/views/comments/create.blade.php b/resources/views/comments/create.blade.php index a5a84b004..5f9f6d449 100644 --- a/resources/views/comments/create.blade.php +++ b/resources/views/comments/create.blade.php @@ -1,31 +1,29 @@ -