diff --git a/app/Activity/Tools/CommentTree.php b/app/Activity/Tools/CommentTree.php index 13afc9252..a05a9d247 100644 --- a/app/Activity/Tools/CommentTree.php +++ b/app/Activity/Tools/CommentTree.php @@ -28,7 +28,7 @@ class CommentTree public function empty(): bool { - return count($this->tree) === 0; + return count($this->getActive()) === 0; } public function count(): int @@ -41,11 +41,21 @@ class CommentTree return array_filter($this->tree, fn (CommentTreeNode $node) => !$node->comment->archived); } + public function activeThreadCount(): int + { + return count($this->getActive()); + } + public function getArchived(): array { return array_filter($this->tree, fn (CommentTreeNode $node) => $node->comment->archived); } + public function archivedThreadCount(): int + { + return count($this->getArchived()); + } + public function getCommentNodeForId(int $commentId): ?CommentTreeNode { foreach ($this->tree as $node) { diff --git a/lang/en/entities.php b/lang/en/entities.php index cda58e65b..c70658c01 100644 --- a/lang/en/entities.php +++ b/lang/en/entities.php @@ -392,9 +392,10 @@ return [ 'comment' => 'Comment', 'comments' => 'Comments', 'comment_add' => 'Add Comment', - 'comment_archived' => ':count Archived Comment|:count Archived Comments', + 'comment_none' => 'No comments to display', 'comment_placeholder' => 'Leave a comment here', - 'comment_count' => '{0} No Comments|{1} 1 Comment|[2,*] :count Comments', + 'comment_thread_count' => ':count Comment Thread|:count Comment Threads', + 'comment_archived_count' => ':count Archived', 'comment_save' => 'Save Comment', 'comment_new' => 'New Comment', 'comment_created' => 'commented :createDiff', diff --git a/resources/js/components/page-comment.ts b/resources/js/components/page-comment.ts index 82cb95f13..12485b807 100644 --- a/resources/js/components/page-comment.ts +++ b/resources/js/components/page-comment.ts @@ -140,8 +140,8 @@ export class PageComment extends Component { const action = isArchived ? 'unarchive' : 'archive'; const response = await window.$http.put(`/comment/${this.commentId}/${action}`); - this.$emit(action, {new_thread_dom: htmlToDom(response.data as string)}); window.$events.success(this.archiveText); + this.$emit(action, {new_thread_dom: htmlToDom(response.data as string)}); this.container.closest('.comment-branch')?.remove(); } diff --git a/resources/js/components/page-comments.ts b/resources/js/components/page-comments.ts index 083919b82..2482c9dcb 100644 --- a/resources/js/components/page-comments.ts +++ b/resources/js/components/page-comments.ts @@ -1,6 +1,7 @@ import {Component} from './component'; import {getLoading, htmlToDom} from '../services/dom.ts'; import {buildForInput} from '../wysiwyg-tinymce/config'; +import {Tabs} from "./tabs"; export interface CommentReplyEvent extends Event { detail: { @@ -21,7 +22,8 @@ export class PageComments extends Component { private pageId: number; private container: HTMLElement; private commentCountBar: HTMLElement; - private commentsTitle: HTMLElement; + private activeTab: HTMLElement; + private archivedTab: HTMLElement; private addButtonContainer: HTMLElement; private archiveContainer: HTMLElement; private replyToRow: HTMLElement; @@ -37,6 +39,7 @@ export class PageComments extends Component { private wysiwygEditor: any = null; private createdText: string; private countText: string; + private archivedCountText: string; private parentId: number | null = null; private contentReference: string = ''; private formReplyText: string = ''; @@ -48,7 +51,8 @@ export class PageComments extends Component { // Element references this.container = this.$refs.commentContainer; this.commentCountBar = this.$refs.commentCountBar; - this.commentsTitle = this.$refs.commentsTitle; + this.activeTab = this.$refs.activeTab; + this.archivedTab = this.$refs.archivedTab; this.addButtonContainer = this.$refs.addButtonContainer; this.archiveContainer = this.$refs.archiveContainer; this.replyToRow = this.$refs.replyToRow; @@ -67,6 +71,7 @@ export class PageComments extends Component { // Translations this.createdText = this.$opts.createdText; this.countText = this.$opts.countText; + this.archivedCountText = this.$opts.archivedCountText; this.formReplyText = this.formReplyLink?.textContent || ''; @@ -85,10 +90,12 @@ export class PageComments extends Component { this.elem.addEventListener('page-comment-archive', (event: ArchiveEvent) => { this.archiveContainer.append(event.detail.new_thread_dom); + setTimeout(() => this.updateCount(), 1); }); this.elem.addEventListener('page-comment-unarchive', (event: ArchiveEvent) => { - this.container.append(event.detail.new_thread_dom) + this.container.append(event.detail.new_thread_dom); + setTimeout(() => this.updateCount(), 1); }); if (this.form) { @@ -136,8 +143,10 @@ export class PageComments extends Component { } protected updateCount(): void { - const count = this.getCommentCount(); - this.commentsTitle.textContent = window.$trans.choice(this.countText, count); + const activeCount = this.getActiveThreadCount(); + this.activeTab.textContent = window.$trans.choice(this.countText, activeCount); + const archivedCount = this.getArchivedThreadCount(); + this.archivedTab.textContent = window.$trans.choice(this.archivedCountText, archivedCount); } protected resetForm(): void { @@ -155,12 +164,18 @@ export class PageComments extends Component { this.addButtonContainer.toggleAttribute('hidden', true); this.formContainer.scrollIntoView({behavior: 'smooth', block: 'nearest'}); this.loadEditor(); + + // Ensure the active comments tab is displaying + const tabs = window.$components.firstOnElement(this.elem, 'tabs'); + if (tabs instanceof Tabs) { + tabs.show('comment-tab-panel-active'); + } } protected hideForm(): void { this.resetForm(); this.formContainer.toggleAttribute('hidden', true); - if (this.getCommentCount() > 0) { + if (this.getActiveThreadCount() > 0) { this.elem.append(this.addButtonContainer); } else { this.commentCountBar.append(this.addButtonContainer); @@ -198,8 +213,12 @@ export class PageComments extends Component { } } - protected getCommentCount(): number { - return this.container.querySelectorAll('[component="page-comment"]').length; + protected getActiveThreadCount(): number { + return this.container.querySelectorAll(':scope > .comment-branch:not([hidden])').length; + } + + protected getArchivedThreadCount(): number { + return this.archiveContainer.querySelectorAll(':scope > .comment-branch').length; } protected setReply(commentLocalId, commentElement): void { diff --git a/resources/js/components/tabs.js b/resources/js/components/tabs.ts similarity index 78% rename from resources/js/components/tabs.js rename to resources/js/components/tabs.ts index f0fc058ce..56405b8c7 100644 --- a/resources/js/components/tabs.js +++ b/resources/js/components/tabs.ts @@ -19,18 +19,25 @@ import {Component} from './component'; */ export class Tabs extends Component { + protected container: HTMLElement; + protected tabList: HTMLElement; + protected tabs: HTMLElement[]; + protected panels: HTMLElement[]; + + protected activeUnder: number; + protected active: null|boolean = null; + setup() { this.container = this.$el; - this.tabList = this.container.querySelector('[role="tablist"]'); + this.tabList = this.container.querySelector('[role="tablist"]') as HTMLElement; this.tabs = Array.from(this.tabList.querySelectorAll('[role="tab"]')); this.panels = Array.from(this.container.querySelectorAll(':scope > [role="tabpanel"], :scope > * > [role="tabpanel"]')); this.activeUnder = this.$opts.activeUnder ? Number(this.$opts.activeUnder) : 10000; - this.active = null; this.container.addEventListener('click', event => { - const tab = event.target.closest('[role="tab"]'); - if (tab && this.tabs.includes(tab)) { - this.show(tab.getAttribute('aria-controls')); + const tab = (event.target as HTMLElement).closest('[role="tab"]'); + if (tab instanceof HTMLElement && this.tabs.includes(tab)) { + this.show(tab.getAttribute('aria-controls') || ''); } }); @@ -40,7 +47,7 @@ export class Tabs extends Component { this.updateActiveState(); } - show(sectionId) { + public show(sectionId: string): void { for (const panel of this.panels) { panel.toggleAttribute('hidden', panel.id !== sectionId); } @@ -54,7 +61,7 @@ export class Tabs extends Component { this.$emit('change', {showing: sectionId}); } - updateActiveState() { + protected updateActiveState(): void { const active = window.innerWidth < this.activeUnder; if (active === this.active) { return; @@ -69,13 +76,13 @@ export class Tabs extends Component { this.active = active; } - activate() { + protected activate(): void { const panelToShow = this.panels.find(p => !p.hasAttribute('hidden')) || this.panels[0]; this.show(panelToShow.id); this.tabList.toggleAttribute('hidden', false); } - deactivate() { + protected deactivate(): void { for (const panel of this.panels) { panel.removeAttribute('hidden'); } diff --git a/resources/sass/_components.scss b/resources/sass/_components.scss index 5486d6112..d25fab299 100644 --- a/resources/sass/_components.scss +++ b/resources/sass/_components.scss @@ -802,6 +802,13 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group { display: block; } +.comment-container .empty-state { + display: none; +} +.comment-container:not(:has([component="page-comment"])) .empty-state { + display: block; +} + .comment-container-compact .comment-box { margin-bottom: vars.$xs; .meta { diff --git a/resources/views/comments/comments.blade.php b/resources/views/comments/comments.blade.php index 06e96cad6..882cfdf45 100644 --- a/resources/views/comments/comments.blade.php +++ b/resources/views/comments/comments.blade.php @@ -1,49 +1,73 @@ -
-
-
{{ trans_choice('entities.comment_count', $commentTree->count(), ['count' => $commentTree->count()]) }}
+
+
+ + +
@if ($commentTree->empty() && userCan('comment-create-all')) -
+
+ class="button outline mb-m">{{ trans('entities.comment_add') }}
@endif
-
- @foreach($commentTree->getActive() as $branch) - @include('comments.comment-branch', ['branch' => $branch, 'readOnly' => false]) - @endforeach +
+
+ @foreach($commentTree->getActive() as $branch) + @include('comments.comment-branch', ['branch' => $branch, 'readOnly' => false]) + @endforeach +
+ +

{{ trans('entities.comment_none') }}

+ + @if(userCan('comment-create-all')) + @include('comments.create') + @if (!$commentTree->empty()) +
+ +
+ @endif + @endif
- @if(userCan('comment-create-all')) - @include('comments.create') - @if (!$commentTree->empty()) -
- - - - -
- @endif - @endif - -
+ @if(userCan('comment-create-all') || $commentTree->canUpdateAny()) diff --git a/resources/views/pages/show.blade.php b/resources/views/pages/show.blade.php index e3a31dd5e..137d43bdb 100644 --- a/resources/views/pages/show.blade.php +++ b/resources/views/pages/show.blade.php @@ -28,12 +28,6 @@ @include('entities.sibling-navigation', ['next' => $next, 'previous' => $previous]) @if ($commentTree->enabled()) - @if(($previous || $next)) - - @endif -