X-Git-Url: http://source.bookstackapp.com/bookstack/blobdiff_plain/099f6104d07faffdcb2bd9793b249055eb4795b2..refs/pull/5627/head:/resources/js/components/page-comments.ts diff --git a/resources/js/components/page-comments.ts b/resources/js/components/page-comments.ts index 083919b82..94f5ab3bb 100644 --- a/resources/js/components/page-comments.ts +++ b/resources/js/components/page-comments.ts @@ -1,42 +1,38 @@ import {Component} from './component'; -import {getLoading, htmlToDom} from '../services/dom.ts'; +import {getLoading, htmlToDom} from '../services/dom'; import {buildForInput} from '../wysiwyg-tinymce/config'; - -export interface CommentReplyEvent extends Event { - detail: { - id: string; // ID of comment being replied to - element: HTMLElement; // Container for comment replied to - } -} - -export interface ArchiveEvent extends Event { - detail: { - new_thread_dom: HTMLElement; - } -} +import {Tabs} from "./tabs"; +import {PageCommentReference} from "./page-comment-reference"; +import {scrollAndHighlightElement} from "../services/util"; +import {PageCommentArchiveEventData, PageCommentReplyEventData} from "./page-comment"; export class PageComments extends Component { - private elem: HTMLElement; - private pageId: number; - private container: HTMLElement; - private commentCountBar: HTMLElement; - private commentsTitle: HTMLElement; - private addButtonContainer: HTMLElement; - private archiveContainer: HTMLElement; - private replyToRow: HTMLElement; - private formContainer: HTMLElement; - private form: HTMLFormElement; - private formInput: HTMLInputElement; - private formReplyLink: HTMLAnchorElement; - private addCommentButton: HTMLElement; - private hideFormButton: HTMLElement; - private removeReplyToButton: HTMLElement; - private wysiwygLanguage: string; - private wysiwygTextDirection: string; + private elem!: HTMLElement; + private pageId!: number; + private container!: HTMLElement; + private commentCountBar!: HTMLElement; + private activeTab!: HTMLElement; + private archivedTab!: HTMLElement; + private addButtonContainer!: HTMLElement; + private archiveContainer!: HTMLElement; + private replyToRow!: HTMLElement; + private referenceRow!: HTMLElement; + private formContainer!: HTMLElement; + private form!: HTMLFormElement; + private formInput!: HTMLInputElement; + private formReplyLink!: HTMLAnchorElement; + private formReferenceLink!: HTMLAnchorElement; + private addCommentButton!: HTMLElement; + private hideFormButton!: HTMLElement; + private removeReplyToButton!: HTMLElement; + private removeReferenceButton!: HTMLElement; + private wysiwygLanguage!: string; + private wysiwygTextDirection!: string; private wysiwygEditor: any = null; - private createdText: string; - private countText: string; + private createdText!: string; + private countText!: string; + private archivedCountText!: string; private parentId: number | null = null; private contentReference: string = ''; private formReplyText: string = ''; @@ -48,17 +44,21 @@ 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; + this.referenceRow = this.$refs.referenceRow; this.formContainer = this.$refs.formContainer; this.form = this.$refs.form as HTMLFormElement; this.formInput = this.$refs.formInput as HTMLInputElement; this.formReplyLink = this.$refs.formReplyLink as HTMLAnchorElement; + this.formReferenceLink = this.$refs.formReferenceLink as HTMLAnchorElement; this.addCommentButton = this.$refs.addCommentButton; this.hideFormButton = this.$refs.hideFormButton; this.removeReplyToButton = this.$refs.removeReplyToButton; + this.removeReferenceButton = this.$refs.removeReferenceButton; // WYSIWYG options this.wysiwygLanguage = this.$opts.wysiwygLanguage; @@ -67,6 +67,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 || ''; @@ -79,27 +80,30 @@ export class PageComments extends Component { this.hideForm(); }); - this.elem.addEventListener('page-comment-reply', (event: CommentReplyEvent) => { + this.elem.addEventListener('page-comment-reply', ((event: CustomEvent) => { this.setReply(event.detail.id, event.detail.element); - }); + }) as EventListener); - this.elem.addEventListener('page-comment-archive', (event: ArchiveEvent) => { + this.elem.addEventListener('page-comment-archive', ((event: CustomEvent) => { this.archiveContainer.append(event.detail.new_thread_dom); - }); + setTimeout(() => this.updateCount(), 1); + }) as EventListener); - this.elem.addEventListener('page-comment-unarchive', (event: ArchiveEvent) => { - this.container.append(event.detail.new_thread_dom) - }); + this.elem.addEventListener('page-comment-unarchive', ((event: CustomEvent) => { + this.container.append(event.detail.new_thread_dom); + setTimeout(() => this.updateCount(), 1); + }) as EventListener); if (this.form) { this.removeReplyToButton.addEventListener('click', this.removeReplyTo.bind(this)); + this.removeReferenceButton.addEventListener('click', () => this.setContentReference('')); this.hideFormButton.addEventListener('click', this.hideForm.bind(this)); this.addCommentButton.addEventListener('click', this.showForm.bind(this)); this.form.addEventListener('submit', this.saveComment.bind(this)); } } - protected saveComment(event): void { + protected saveComment(event: SubmitEvent): void { event.preventDefault(); event.stopPropagation(); @@ -111,7 +115,7 @@ export class PageComments extends Component { const reqData = { html: this.wysiwygEditor.getContent(), parent_id: this.parentId || null, - content_ref: this.contentReference || '', + content_ref: this.contentReference, }; window.$http.post(`/comment/${this.pageId}`, reqData).then(resp => { @@ -123,6 +127,11 @@ export class PageComments extends Component { this.container.append(newElem); } + const refs = window.$components.allWithinElement(newElem, 'page-comment-reference'); + for (const ref of refs) { + ref.showForDisplay(); + } + window.$events.success(this.createdText); this.hideForm(); this.updateCount(); @@ -136,17 +145,17 @@ 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 { this.removeEditor(); this.formInput.value = ''; - this.parentId = null; - this.contentReference = ''; - this.replyToRow.toggleAttribute('hidden', true); - this.container.append(this.formContainer); + this.setContentReference(''); + this.removeReplyTo(); } protected showForm(): 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); @@ -182,10 +197,10 @@ export class PageComments extends Component { drawioUrl: '', pageId: 0, translations: {}, - translationMap: (window as Record).editor_translations, + translationMap: (window as unknown as Record).editor_translations, }); - (window as {tinymce: {init: (Object) => Promise}}).tinymce.init(config).then(editors => { + (window as unknown as {tinymce: {init: (arg0: Object) => Promise}}).tinymce.init(config).then(editors => { this.wysiwygEditor = editors[0]; setTimeout(() => this.wysiwygEditor.focus(), 50); }); @@ -198,15 +213,19 @@ 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 setReply(commentLocalId, commentElement): void { - const targetFormLocation = commentElement.closest('.comment-branch').querySelector('.comment-branch-children'); + protected getArchivedThreadCount(): number { + return this.archiveContainer.querySelectorAll(':scope > .comment-branch').length; + } + + protected setReply(commentLocalId: string, commentElement: HTMLElement): void { + const targetFormLocation = (commentElement.closest('.comment-branch') as HTMLElement).querySelector('.comment-branch-children') as HTMLElement; targetFormLocation.append(this.formContainer); this.showForm(); - this.parentId = commentLocalId; + this.parentId = Number(commentLocalId); this.replyToRow.toggleAttribute('hidden', false); this.formReplyLink.textContent = this.formReplyText.replace('1234', String(this.parentId)); this.formReplyLink.href = `#comment${this.parentId}`; @@ -221,7 +240,21 @@ export class PageComments extends Component { public startNewComment(contentReference: string): void { this.removeReplyTo(); - this.contentReference = contentReference; + this.setContentReference(contentReference); + } + + protected setContentReference(reference: string): void { + this.contentReference = reference; + this.referenceRow.toggleAttribute('hidden', !Boolean(reference)); + const [id] = reference.split(':'); + this.formReferenceLink.href = `#${id}`; + this.formReferenceLink.onclick = function(event) { + event.preventDefault(); + const el = document.getElementById(id); + if (el) { + scrollAndHighlightElement(el); + } + }; } }