From 911186dae411d78788ccede093c251303187589a Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 29 Jun 2021 17:18:30 +0200 Subject: Reorganize watch components --- .../comment/video-comment-add.component.html | 96 -------- .../comment/video-comment-add.component.scss | 119 ---------- .../comment/video-comment-add.component.ts | 220 ----------------- .../comment/video-comment.component.html | 94 -------- .../comment/video-comment.component.scss | 199 ---------------- .../comment/video-comment.component.ts | 208 ---------------- .../comment/video-comments.component.html | 100 -------- .../comment/video-comments.component.scss | 60 ----- .../comment/video-comments.component.ts | 261 --------------------- 9 files changed, 1357 deletions(-) delete mode 100644 client/src/app/+videos/+video-watch/comment/video-comment-add.component.html delete mode 100644 client/src/app/+videos/+video-watch/comment/video-comment-add.component.scss delete mode 100644 client/src/app/+videos/+video-watch/comment/video-comment-add.component.ts delete mode 100644 client/src/app/+videos/+video-watch/comment/video-comment.component.html delete mode 100644 client/src/app/+videos/+video-watch/comment/video-comment.component.scss delete mode 100644 client/src/app/+videos/+video-watch/comment/video-comment.component.ts delete mode 100644 client/src/app/+videos/+video-watch/comment/video-comments.component.html delete mode 100644 client/src/app/+videos/+video-watch/comment/video-comments.component.scss delete mode 100644 client/src/app/+videos/+video-watch/comment/video-comments.component.ts (limited to 'client/src/app/+videos/+video-watch/comment') diff --git a/client/src/app/+videos/+video-watch/comment/video-comment-add.component.html b/client/src/app/+videos/+video-watch/comment/video-comment-add.component.html deleted file mode 100644 index 3ee818c8b..000000000 --- a/client/src/app/+videos/+video-watch/comment/video-comment-add.component.html +++ /dev/null @@ -1,96 +0,0 @@ -
-
- - -
- - - - - Markdown compatible that supports: - -
    -
  • Auto generated links
  • -
  • Break lines
  • -
  • Lists
  • -
  • - Emphasis - **bold** _italic_ -
  • -
  • - Emoji shortcuts - :) <3 -
  • -
  • - Emoji markup - :smile: - -
  • -
-
-
-
- {{ formErrors.text }} -
-
-
- -
- - - -
-
- - - - - - - - - - - - - diff --git a/client/src/app/+videos/+video-watch/comment/video-comment-add.component.scss b/client/src/app/+videos/+video-watch/comment/video-comment-add.component.scss deleted file mode 100644 index fb79991db..000000000 --- a/client/src/app/+videos/+video-watch/comment/video-comment-add.component.scss +++ /dev/null @@ -1,119 +0,0 @@ -@use '_variables' as *; -@use '_mixins' as *; - -$markdown-icon-height: 18px; -$markdown-icon-width: 30px; -$peertube-textarea-height: 60px; - -form { - margin-bottom: 30px; -} - -.avatar-and-textarea { - display: flex; - margin-bottom: 10px; - - my-actor-avatar { - @include margin-right(10px); - } - - .form-group { - flex-grow: 1; - margin: 0; - position: relative; - } - - textarea { - @include peertube-textarea(100%, $peertube-textarea-height); - @include button-focus(pvar(--mainColorLightest)); - @include padding-right($markdown-icon-width + 15px !important); - - min-height: calc(#{$peertube-textarea-height} - 15px * 2); - - @media screen and (max-width: 600px) { - @include padding-right($markdown-icon-width + 19px !important); - } - - &:focus::placeholder { - opacity: 0; - } - } -} - -.markdown-guide { - position: absolute; - top: 5px; - right: 9px; - - // inset-inline is not well supported by web browsers - &.is-rtl { - right: unset; - left: 9px; - } - - ::ng-deep .help-tooltip-button { - my-global-icon { - height: $markdown-icon-height; - width: $markdown-icon-width; - - svg { - color: #C6C6C6; - fill: #C6C6C6; - border-radius: 3px; - } - } - - &:focus, - &:active, - &:hover { - my-global-icon svg { - background-color: #C6C6C6; - color: pvar(--mainBackgroundColor); - fill: pvar(--mainBackgroundColor); - } - } - } -} - -.comment-buttons { - display: flex; - justify-content: flex-end; -} - -.emoji-flex { - display: flex; - flex-flow: row wrap; - align-items: center; - - .emoji-flex-item { - text-align: left; - margin: auto; - min-width: 227px; - flex: 1; - - code { - @include margin-left(5px); - - display: inline-block; - vertical-align: middle; - } - } -} - -@media screen and (max-width: 600px) { - textarea, - .comment-buttons button { - font-size: 14px !important; - } - - textarea { - padding: 5px !important; - } -} - -.modal-body { - > span { - float: left; - margin-bottom: 20px; - } -} diff --git a/client/src/app/+videos/+video-watch/comment/video-comment-add.component.ts b/client/src/app/+videos/+video-watch/comment/video-comment-add.component.ts deleted file mode 100644 index 78efe1684..000000000 --- a/client/src/app/+videos/+video-watch/comment/video-comment-add.component.ts +++ /dev/null @@ -1,220 +0,0 @@ -import { Observable } from 'rxjs' -import { getLocaleDirection } from '@angular/common' -import { - Component, - ElementRef, - EventEmitter, - Inject, - Input, - LOCALE_ID, - OnChanges, - OnInit, - Output, - SimpleChanges, - ViewChild -} from '@angular/core' -import { Router } from '@angular/router' -import { Notifier, User } from '@app/core' -import { VIDEO_COMMENT_TEXT_VALIDATOR } from '@app/shared/form-validators/video-comment-validators' -import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' -import { Video } from '@app/shared/shared-main' -import { VideoComment, VideoCommentService } from '@app/shared/shared-video-comment' -import { NgbModal } from '@ng-bootstrap/ng-bootstrap' -import { VideoCommentCreate } from '@shared/models' - -@Component({ - selector: 'my-video-comment-add', - templateUrl: './video-comment-add.component.html', - styleUrls: ['./video-comment-add.component.scss'] -}) -export class VideoCommentAddComponent extends FormReactive implements OnChanges, OnInit { - @Input() user: User - @Input() video: Video - @Input() parentComment?: VideoComment - @Input() parentComments?: VideoComment[] - @Input() focusOnInit = false - @Input() textValue?: string - - @Output() commentCreated = new EventEmitter() - @Output() cancel = new EventEmitter() - - @ViewChild('visitorModal', { static: true }) visitorModal: NgbModal - @ViewChild('emojiModal', { static: true }) emojiModal: NgbModal - @ViewChild('textarea', { static: true }) textareaElement: ElementRef - - addingComment = false - addingCommentButtonValue: string - - constructor ( - protected formValidatorService: FormValidatorService, - private notifier: Notifier, - private videoCommentService: VideoCommentService, - private modalService: NgbModal, - private router: Router, - @Inject(LOCALE_ID) private localeId: string - ) { - super() - } - - get emojiMarkupList () { - const emojiMarkupObjectList = require('markdown-it-emoji/lib/data/light.json') - - // Populate emoji-markup-list from object to array to avoid keys alphabetical order - const emojiMarkupArrayList = [] - for (const emojiMarkupName in emojiMarkupObjectList) { - if (emojiMarkupName) { - const emoji = emojiMarkupObjectList[emojiMarkupName] - emojiMarkupArrayList.push([emoji, emojiMarkupName]) - } - } - - return emojiMarkupArrayList - } - - ngOnInit () { - this.buildForm({ - text: VIDEO_COMMENT_TEXT_VALIDATOR - }) - - if (this.user) { - if (!this.parentComment) { - this.addingCommentButtonValue = $localize`Comment` - } else { - this.addingCommentButtonValue = $localize`Reply` - } - - this.initTextValue() - } - } - - ngOnChanges (changes: SimpleChanges) { - // Not initialized yet - if (!this.form) return - - if (changes.textValue && changes.textValue.currentValue && changes.textValue.currentValue !== changes.textValue.previousValue) { - this.patchTextValue(changes.textValue.currentValue, true) - } - } - - onValidKey () { - this.check() - if (!this.form.valid) return - - this.formValidated() - } - - openVisitorModal (event: any) { - if (this.user === null) { // we only open it for visitors - // fixing ng-bootstrap ModalService and the "Expression Changed After It Has Been Checked" Error - event.srcElement.blur() - event.preventDefault() - - this.modalService.open(this.visitorModal) - } - } - - openEmojiModal (event: any) { - event.preventDefault() - this.modalService.open(this.emojiModal, { backdrop: true, size: 'lg' }) - } - - hideModals () { - this.modalService.dismissAll() - } - - formValidated () { - // If we validate very quickly the comment form, we might comment twice - if (this.addingComment) return - - this.addingComment = true - - const commentCreate: VideoCommentCreate = this.form.value - let obs: Observable - - if (this.parentComment) { - obs = this.addCommentReply(commentCreate) - } else { - obs = this.addCommentThread(commentCreate) - } - - obs.subscribe( - comment => { - this.addingComment = false - this.commentCreated.emit(comment) - this.form.reset() - }, - - err => { - this.addingComment = false - - this.notifier.error(err.text) - } - ) - } - - isAddButtonDisplayed () { - return this.form.value['text'] - } - - getUri () { - return window.location.href - } - - gotoLogin () { - this.hideModals() - this.router.navigate([ '/login' ]) - } - - cancelCommentReply () { - this.cancel.emit(null) - this.form.value['text'] = this.textareaElement.nativeElement.value = '' - } - - isRTL () { - return getLocaleDirection(this.localeId) === 'rtl' - } - - private addCommentReply (commentCreate: VideoCommentCreate) { - return this.videoCommentService - .addCommentReply(this.video.id, this.parentComment.id, commentCreate) - } - - private addCommentThread (commentCreate: VideoCommentCreate) { - return this.videoCommentService - .addCommentThread(this.video.id, commentCreate) - } - - private initTextValue () { - if (this.textValue) { - this.patchTextValue(this.textValue, this.focusOnInit) - return - } - - if (this.parentComment) { - const mentions = this.parentComments - .filter(c => c.account && c.account.id !== this.user.account.id) // Don't add mention of ourselves - .map(c => '@' + c.by) - - const mentionsSet = new Set(mentions) - const mentionsText = Array.from(mentionsSet).join(' ') + ' ' - - this.patchTextValue(mentionsText, this.focusOnInit) - } - } - - private patchTextValue (text: string, focus: boolean) { - setTimeout(() => { - if (focus) { - this.textareaElement.nativeElement.focus() - } - - // Scroll to textarea - this.textareaElement.nativeElement.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' }) - - // Use the native textarea autosize according to the text's break lines - this.textareaElement.nativeElement.dispatchEvent(new Event('input')) - }) - - this.form.patchValue({ text }) - } -} diff --git a/client/src/app/+videos/+video-watch/comment/video-comment.component.html b/client/src/app/+videos/+video-watch/comment/video-comment.component.html deleted file mode 100644 index d8b944b35..000000000 --- a/client/src/app/+videos/+video-watch/comment/video-comment.component.html +++ /dev/null @@ -1,94 +0,0 @@ -
-
- -
-
- -
-
- -
Highlighted comment
- - - -
- -
-
Reply
- - -
-
- - - - -
- This comment has been deleted -
-
- - - -
-
- -
-
- - -
-
-
- - - - diff --git a/client/src/app/+videos/+video-watch/comment/video-comment.component.scss b/client/src/app/+videos/+video-watch/comment/video-comment.component.scss deleted file mode 100644 index 87e313d41..000000000 --- a/client/src/app/+videos/+video-watch/comment/video-comment.component.scss +++ /dev/null @@ -1,199 +0,0 @@ -@use '_variables' as *; -@use '_mixins' as *; - -.root-comment { - font-size: 15px; - display: flex; - - .left { - @include margin-right(10px); - - display: flex; - flex-direction: column; - align-items: center; - - .vertical-border { - width: 2px; - height: 100%; - background-color: rgba(0, 0, 0, 0.05); - margin: 10px calc(1rem + 1px); - } - } - - .right { - width: 100%; - } -} - -my-actor-avatar { - @include actor-avatar-size(36px); -} - -.comment { - flex-grow: 1; - // Fix word-wrap with flex - min-width: 1px; -} - -.highlighted-comment { - display: inline-block; - background-color: #F5F5F5; - color: #3d3d3d; - padding: 0 5px; - font-size: 13px; - margin-bottom: 5px; - font-weight: $font-semibold; - border-radius: 3px; -} - -.comment-account-date { - display: flex; - margin-bottom: 4px; -} - -.video-author { - @include padding-right(6px); - @include padding-left(6px); - - height: 20px; - background-color: #888888; - border-radius: 12px; - margin-bottom: 2px; - max-width: 100%; - box-sizing: border-box; - flex-direction: row; - align-items: center; - display: inline-flex; - color: #fff !important; -} - -.comment-account { - word-break: break-all; - font-weight: 600; - font-size: 90%; - - a { - @include disable-default-a-behaviour; - - color: pvar(--mainForegroundColor); - - &:hover { - text-decoration: underline; - } - } - - .comment-account-fid { - opacity: .6; - } -} - -.comment-date { - @include margin-left(5px); - - font-size: 90%; - color: pvar(--greyForegroundColor); - text-decoration: none; - - &:hover { - text-decoration: underline; - } -} - -.comment-html { - @include peertube-word-wrap; - - // Mentions - ::ng-deep a { - - &:not(.linkified-url) { - @include disable-default-a-behaviour; - - color: pvar(--mainForegroundColor); - - font-weight: $font-semibold; - } - - } - - // Paragraphs - ::ng-deep p { - margin-bottom: .3rem; - } - - &.comment-html-deleted { - color: pvar(--greyForegroundColor); - margin-bottom: 1rem; - } -} - -.comment-actions { - margin-bottom: 10px; - display: flex; - - ::ng-deep .dropdown-toggle, - .comment-action-reply { - @include margin-right(10px); - - color: pvar(--greyForegroundColor); - cursor: pointer; - - &:hover, - &:active, - &:focus, - &:focus-visible { - color: pvar(--mainForegroundColor); - } - } - - ::ng-deep .action-button { - background-color: transparent; - padding: 0; - font-weight: unset; - } -} - -my-video-comment-add { - ::ng-deep form { - margin-top: 1rem; - margin-bottom: 0; - } -} - -.is-child { - // Reduce avatars size for replies - my-actor-avatar { - @include actor-avatar-size(25px); - } - - .left { - @include margin-right(6px); - } -} - -@media screen and (max-width: 1200px) { - .children { - @include margin-left(-10px); - } -} - -@media screen and (max-width: 600px) { - .children { - @include margin-left(-20px); - - .left { - align-items: flex-start; - - .vertical-border { - @include margin-left(2px); - } - } - } - - .comment-account-date { - flex-direction: column; - - .comment-date { - @include margin-left(0); - } - } -} diff --git a/client/src/app/+videos/+video-watch/comment/video-comment.component.ts b/client/src/app/+videos/+video-watch/comment/video-comment.component.ts deleted file mode 100644 index 04f8f0d58..000000000 --- a/client/src/app/+videos/+video-watch/comment/video-comment.component.ts +++ /dev/null @@ -1,208 +0,0 @@ - -import { Component, EventEmitter, Input, OnChanges, OnInit, Output, ViewChild } from '@angular/core' -import { MarkdownService, Notifier, UserService } from '@app/core' -import { AuthService } from '@app/core/auth' -import { Account, DropdownAction, Video } from '@app/shared/shared-main' -import { CommentReportComponent } from '@app/shared/shared-moderation/report-modals/comment-report.component' -import { VideoComment, VideoCommentThreadTree } from '@app/shared/shared-video-comment' -import { User, UserRight } from '@shared/models' - -@Component({ - selector: 'my-video-comment', - templateUrl: './video-comment.component.html', - styleUrls: ['./video-comment.component.scss'] -}) -export class VideoCommentComponent implements OnInit, OnChanges { - @ViewChild('commentReportModal') commentReportModal: CommentReportComponent - - @Input() video: Video - @Input() comment: VideoComment - @Input() parentComments: VideoComment[] = [] - @Input() commentTree: VideoCommentThreadTree - @Input() inReplyToCommentId: number - @Input() highlightedComment = false - @Input() firstInThread = false - @Input() redraftValue?: string - - @Output() wantedToReply = new EventEmitter() - @Output() wantedToDelete = new EventEmitter() - @Output() wantedToRedraft = new EventEmitter() - @Output() threadCreated = new EventEmitter() - @Output() resetReply = new EventEmitter() - @Output() timestampClicked = new EventEmitter() - - prependModerationActions: DropdownAction[] - - sanitizedCommentHTML = '' - newParentComments: VideoComment[] = [] - - commentAccount: Account - commentUser: User - - constructor ( - private markdownService: MarkdownService, - private authService: AuthService, - private userService: UserService, - private notifier: Notifier - ) {} - - get user () { - return this.authService.getUser() - } - - ngOnInit () { - this.init() - } - - ngOnChanges () { - this.init() - } - - onCommentReplyCreated (createdComment: VideoComment) { - if (!this.commentTree) { - this.commentTree = { - comment: this.comment, - hasDisplayedChildren: false, - children: [] - } - - this.threadCreated.emit(this.commentTree) - } - - this.commentTree.children.unshift({ - comment: createdComment, - hasDisplayedChildren: false, - children: [] - }) - - this.resetReply.emit() - - this.redraftValue = undefined - } - - onWantToReply (comment?: VideoComment) { - this.wantedToReply.emit(comment || this.comment) - } - - onWantToDelete (comment?: VideoComment) { - this.wantedToDelete.emit(comment || this.comment) - } - - onWantToRedraft (comment?: VideoComment) { - this.wantedToRedraft.emit(comment || this.comment) - } - - isUserLoggedIn () { - return this.authService.isLoggedIn() - } - - onResetReply () { - this.resetReply.emit() - } - - handleTimestampClicked (timestamp: number) { - this.timestampClicked.emit(timestamp) - } - - isRemovableByUser () { - return this.comment.account && this.isUserLoggedIn() && - ( - this.user.account.id === this.comment.account.id || - this.user.account.id === this.video.account.id || - this.user.hasRight(UserRight.REMOVE_ANY_VIDEO_COMMENT) - ) - } - - isRedraftableByUser () { - return ( - this.comment.account && - this.isUserLoggedIn() && - this.user.account.id === this.comment.account.id && - this.comment.totalReplies === 0 - ) - } - - isReportableByUser () { - return ( - this.comment.account && - this.isUserLoggedIn() && - this.comment.isDeleted === false && - this.user.account.id !== this.comment.account.id - ) - } - - isCommentDisplayed () { - // Not deleted - return !this.comment.isDeleted || - this.comment.totalReplies !== 0 || // Or root comment thread has replies - (this.commentTree?.hasDisplayedChildren) // Or this is a reply that have other replies - } - - isChild () { - return this.parentComments.length !== 0 - } - - private getUserIfNeeded (account: Account) { - if (!account.userId) return - if (!this.authService.isLoggedIn()) return - - const user = this.authService.getUser() - if (user.hasRight(UserRight.MANAGE_USERS)) { - this.userService.getUserWithCache(account.userId) - .subscribe( - user => this.commentUser = user, - - err => this.notifier.error(err.message) - ) - } - } - - private async init () { - // Before HTML rendering restore line feed for markdown list compatibility - const commentText = this.comment.text.replace(//g, '\r\n') - const html = await this.markdownService.textMarkdownToHTML(commentText, true, true) - this.sanitizedCommentHTML = this.markdownService.processVideoTimestamps(html) - this.newParentComments = this.parentComments.concat([ this.comment ]) - - if (this.comment.account) { - this.commentAccount = new Account(this.comment.account) - this.getUserIfNeeded(this.commentAccount) - } else { - this.comment.account = null - } - - this.prependModerationActions = [] - - if (this.isReportableByUser()) { - this.prependModerationActions.push({ - label: $localize`Report this comment`, - iconName: 'flag', - handler: () => this.showReportModal() - }) - } - - if (this.isRemovableByUser()) { - this.prependModerationActions.push({ - label: $localize`Remove`, - iconName: 'delete', - handler: () => this.onWantToDelete() - }) - } - - if (this.isRedraftableByUser()) { - this.prependModerationActions.push({ - label: $localize`Remove & re-draft`, - iconName: 'edit', - handler: () => this.onWantToRedraft() - }) - } - - if (this.prependModerationActions.length === 0) { - this.prependModerationActions = undefined - } - } - - private showReportModal () { - this.commentReportModal.show() - } -} diff --git a/client/src/app/+videos/+video-watch/comment/video-comments.component.html b/client/src/app/+videos/+video-watch/comment/video-comments.component.html deleted file mode 100644 index 9e6fde2e0..000000000 --- a/client/src/app/+videos/+video-watch/comment/video-comments.component.html +++ /dev/null @@ -1,100 +0,0 @@ -
-
-

- {totalNotDeletedComments, plural, =0 {Comments} =1 {1 Comment} other {{{totalNotDeletedComments}} Comments}} -

- - - - -
- - - - -
No comments.
- -
-
-
- -
- -
- -
- - - - - - - View {comment.totalReplies, plural, =1 {1 reply} other {{{ comment.totalReplies }} replies}} from {{ video?.account?.displayName || 'the author' }} and others - - - View {comment.totalReplies, plural, =1 {1 reply} other {{{ comment.totalReplies }} replies}} from {{ video?.account?.displayName || 'the author' }} - - - - View {comment.totalReplies, plural, =1 {1 reply} other {{{ comment.totalReplies }} replies}} - - -
-
- -
-
-
- -
- Comments are disabled. -
-
diff --git a/client/src/app/+videos/+video-watch/comment/video-comments.component.scss b/client/src/app/+videos/+video-watch/comment/video-comments.component.scss deleted file mode 100644 index 31aa73937..000000000 --- a/client/src/app/+videos/+video-watch/comment/video-comments.component.scss +++ /dev/null @@ -1,60 +0,0 @@ -@use '_variables' as *; -@use '_mixins' as *; - -#highlighted-comment { - margin-bottom: 25px; -} - -.view-replies { - font-weight: $font-semibold; - font-size: 15px; - cursor: pointer; -} - -.glyphicon, -.comment-thread-loading { - @include margin-right(5px); - - display: inline-block; - font-size: 13px; -} - -.title-block { - .title-page { - @include margin-right(0); - } - - my-feed { - @include margin-left(5px); - - display: inline-block; - opacity: 0; - transition: ease-in .2s opacity; - width: 12px; - position: relative; - top: -3px; - } - - &:hover my-feed { - opacity: 1; - } -} - -#dropdown-sort-comments { - font-weight: 600; - text-transform: uppercase; - border: 0; - transform: translateY(-7%); -} - -@media screen and (max-width: 600px) { - .view-replies { - @include margin-left(46px); - } -} - -@media screen and (max-width: 450px) { - .view-replies { - font-size: 14px; - } -} diff --git a/client/src/app/+videos/+video-watch/comment/video-comments.component.ts b/client/src/app/+videos/+video-watch/comment/video-comments.component.ts deleted file mode 100644 index 2c39e63fb..000000000 --- a/client/src/app/+videos/+video-watch/comment/video-comments.component.ts +++ /dev/null @@ -1,261 +0,0 @@ -import { Subject, Subscription } from 'rxjs' -import { Component, ElementRef, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges, ViewChild } from '@angular/core' -import { ActivatedRoute } from '@angular/router' -import { AuthService, ComponentPagination, ConfirmService, hasMoreItems, Notifier, User } from '@app/core' -import { HooksService } from '@app/core/plugins/hooks.service' -import { Syndication, VideoDetails } from '@app/shared/shared-main' -import { VideoComment, VideoCommentService, VideoCommentThreadTree } from '@app/shared/shared-video-comment' - -@Component({ - selector: 'my-video-comments', - templateUrl: './video-comments.component.html', - styleUrls: ['./video-comments.component.scss'] -}) -export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy { - @ViewChild('commentHighlightBlock') commentHighlightBlock: ElementRef - @Input() video: VideoDetails - @Input() user: User - - @Output() timestampClicked = new EventEmitter() - - comments: VideoComment[] = [] - highlightedThread: VideoComment - - sort = '-createdAt' - - componentPagination: ComponentPagination = { - currentPage: 1, - itemsPerPage: 10, - totalItems: null - } - totalNotDeletedComments: number - - inReplyToCommentId: number - commentReplyRedraftValue: string - commentThreadRedraftValue: string - - threadComments: { [ id: number ]: VideoCommentThreadTree } = {} - threadLoading: { [ id: number ]: boolean } = {} - - syndicationItems: Syndication[] = [] - - onDataSubject = new Subject() - - private sub: Subscription - - constructor ( - private authService: AuthService, - private notifier: Notifier, - private confirmService: ConfirmService, - private videoCommentService: VideoCommentService, - private activatedRoute: ActivatedRoute, - private hooks: HooksService - ) {} - - ngOnInit () { - // Find highlighted comment in params - this.sub = this.activatedRoute.params.subscribe( - params => { - if (params['threadId']) { - const highlightedThreadId = +params['threadId'] - this.processHighlightedThread(highlightedThreadId) - } - } - ) - } - - ngOnChanges (changes: SimpleChanges) { - if (changes['video']) { - this.resetVideo() - } - } - - ngOnDestroy () { - if (this.sub) this.sub.unsubscribe() - } - - viewReplies (commentId: number, highlightThread = false) { - this.threadLoading[commentId] = true - - const params = { - videoId: this.video.id, - threadId: commentId - } - - const obs = this.hooks.wrapObsFun( - this.videoCommentService.getVideoThreadComments.bind(this.videoCommentService), - params, - 'video-watch', - 'filter:api.video-watch.video-thread-replies.list.params', - 'filter:api.video-watch.video-thread-replies.list.result' - ) - - obs.subscribe( - res => { - this.threadComments[commentId] = res - this.threadLoading[commentId] = false - this.hooks.runAction('action:video-watch.video-thread-replies.loaded', 'video-watch', { data: res }) - - if (highlightThread) { - this.highlightedThread = new VideoComment(res.comment) - - // Scroll to the highlighted thread - setTimeout(() => this.commentHighlightBlock.nativeElement.scrollIntoView(), 0) - } - }, - - err => this.notifier.error(err.message) - ) - } - - loadMoreThreads () { - const params = { - videoId: this.video.id, - componentPagination: this.componentPagination, - sort: this.sort - } - - const obs = this.hooks.wrapObsFun( - this.videoCommentService.getVideoCommentThreads.bind(this.videoCommentService), - params, - 'video-watch', - 'filter:api.video-watch.video-threads.list.params', - 'filter:api.video-watch.video-threads.list.result' - ) - - obs.subscribe( - res => { - this.comments = this.comments.concat(res.data) - this.componentPagination.totalItems = res.total - this.totalNotDeletedComments = res.totalNotDeletedComments - - this.onDataSubject.next(res.data) - this.hooks.runAction('action:video-watch.video-threads.loaded', 'video-watch', { data: this.componentPagination }) - }, - - err => this.notifier.error(err.message) - ) - } - - onCommentThreadCreated (comment: VideoComment) { - this.comments.unshift(comment) - this.commentThreadRedraftValue = undefined - } - - onWantedToReply (comment: VideoComment) { - this.inReplyToCommentId = comment.id - } - - onResetReply () { - this.inReplyToCommentId = undefined - this.commentReplyRedraftValue = undefined - } - - onThreadCreated (commentTree: VideoCommentThreadTree) { - this.viewReplies(commentTree.comment.id) - } - - handleSortChange (sort: string) { - if (this.sort === sort) return - - this.sort = sort - this.resetVideo() - } - - handleTimestampClicked (timestamp: number) { - this.timestampClicked.emit(timestamp) - } - - async onWantedToDelete ( - commentToDelete: VideoComment, - title = $localize`Delete`, - message = $localize`Do you really want to delete this comment?` - ): Promise { - if (commentToDelete.isLocal || this.video.isLocal) { - message += $localize` The deletion will be sent to remote instances so they can reflect the change.` - } else { - message += $localize` It is a remote comment, so the deletion will only be effective on your instance.` - } - - const res = await this.confirmService.confirm(message, title) - if (res === false) return false - - this.videoCommentService.deleteVideoComment(commentToDelete.videoId, commentToDelete.id) - .subscribe( - () => { - if (this.highlightedThread?.id === commentToDelete.id) { - commentToDelete = this.comments.find(c => c.id === commentToDelete.id) - - this.highlightedThread = undefined - } - - // Mark the comment as deleted - this.softDeleteComment(commentToDelete) - }, - - err => this.notifier.error(err.message) - ) - - return true - } - - async onWantedToRedraft (commentToRedraft: VideoComment) { - const confirm = await this.onWantedToDelete(commentToRedraft, $localize`Delete and re-draft`, $localize`Do you really want to delete and re-draft this comment?`) - - if (confirm) { - this.inReplyToCommentId = commentToRedraft.inReplyToCommentId - - // Restore line feed for editing - const commentToRedraftText = commentToRedraft.text.replace(//g, '\r\n') - - if (commentToRedraft.threadId === commentToRedraft.id) { - this.commentThreadRedraftValue = commentToRedraftText - } else { - this.commentReplyRedraftValue = commentToRedraftText - } - - } - } - - isUserLoggedIn () { - return this.authService.isLoggedIn() - } - - onNearOfBottom () { - if (hasMoreItems(this.componentPagination)) { - this.componentPagination.currentPage++ - this.loadMoreThreads() - } - } - - private softDeleteComment (comment: VideoComment) { - comment.isDeleted = true - comment.deletedAt = new Date() - comment.text = '' - comment.account = null - } - - private resetVideo () { - if (this.video.commentsEnabled === true) { - // Reset all our fields - this.highlightedThread = null - this.comments = [] - this.threadComments = {} - this.threadLoading = {} - this.inReplyToCommentId = undefined - this.componentPagination.currentPage = 1 - this.componentPagination.totalItems = null - this.totalNotDeletedComments = null - - this.syndicationItems = this.videoCommentService.getVideoCommentsFeeds(this.video) - this.loadMoreThreads() - } - } - - private processHighlightedThread (highlightedThreadId: number) { - this.highlightedThread = this.comments.find(c => c.id === highlightedThreadId) - - const highlightThread = true - this.viewReplies(highlightedThreadId, highlightThread) - } -} -- cgit v1.2.3