Chocobozzz/PeerTube

View on GitHub
client/src/app/+videos/+video-watch/shared/action-buttons/video-rate.component.ts

Summary

Maintainability
A
3 hrs
Test Coverage
import { Observable } from 'rxjs'
import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output } from '@angular/core'
import { Notifier, ScreenService, Hotkey, HotkeysService } from '@app/core'
import { UserVideoRateType } from '@peertube/peertube-models'
import { GlobalIconComponent } from '../../../../shared/shared-icons/global-icon.component'
import { NgClass, NgIf } from '@angular/common'
import { NgbPopover, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'
import { VideoService } from '@app/shared/shared-main/video/video.service'
import { VideoDetails } from '@app/shared/shared-main/video/video-details.model'

@Component({
  selector: 'my-video-rate',
  templateUrl: './video-rate.component.html',
  styleUrls: [ './video-rate.component.scss' ],
  standalone: true,
  imports: [ NgbPopover, NgClass, NgbTooltip, GlobalIconComponent, NgIf ]
})
export class VideoRateComponent implements OnInit, OnChanges, OnDestroy {
  @Input() video: VideoDetails
  @Input() videoPassword: string
  @Input() isUserLoggedIn: boolean

  @Output() userRatingLoaded = new EventEmitter<UserVideoRateType>()
  @Output() rateUpdated = new EventEmitter<UserVideoRateType>()

  userRating: UserVideoRateType

  tooltipLike = ''
  tooltipDislike = ''

  private hotkeys: Hotkey[]

  constructor (
    private videoService: VideoService,
    private notifier: Notifier,
    private hotkeysService: HotkeysService,
    private screenService: ScreenService
  ) { }

  ngOnInit () {
    // Hide the tooltips for unlogged users in mobile view, this adds confusion with the popover
    if (this.isUserLoggedIn || !this.screenService.isInMobileView()) {
      this.tooltipLike = $localize`Like this video`
      this.tooltipDislike = $localize`Dislike this video`
    }

    if (this.isUserLoggedIn) {
      this.hotkeys = [
        new Hotkey('Shift+l', () => {
          this.setLike()
          return false
        }, $localize`Like the video`),

        new Hotkey('Shift+d', () => {
          this.setDislike()
          return false
        }, $localize`Dislike the video`)
      ]

      this.hotkeysService.add(this.hotkeys)
    }
  }

  ngOnChanges () {
    this.checkUserRating()
  }

  ngOnDestroy () {
    this.hotkeysService.remove(this.hotkeys)
  }

  setLike () {
    if (this.isUserLoggedIn === false) return

    // Already liked this video
    if (this.userRating === 'like') this.setRating('none')
    else this.setRating('like')
  }

  setDislike () {
    if (this.isUserLoggedIn === false) return

    // Already disliked this video
    if (this.userRating === 'dislike') this.setRating('none')
    else this.setRating('dislike')
  }

  getRatePopoverText () {
    if (this.isUserLoggedIn) return undefined

    return $localize`You need to be logged in to rate this video.`
  }

  private checkUserRating () {
    // Unlogged users do not have ratings
    if (this.isUserLoggedIn === false) return

    this.videoService.getUserVideoRating(this.video.uuid)
        .subscribe({
          next: ratingObject => {
            if (!ratingObject) return

            this.userRating = ratingObject.rating
            this.userRatingLoaded.emit(this.userRating)
          },

          error: err => this.notifier.error(err.message)
        })
  }

  private setRating (nextRating: UserVideoRateType) {
    const ratingMethods: { [id in UserVideoRateType]: (id: string, videoPassword: string) => Observable<any> } = {
      like: this.videoService.setVideoLike.bind(this.videoService),
      dislike: this.videoService.setVideoDislike.bind(this.videoService),
      none: this.videoService.unsetVideoLike.bind(this.videoService)
    }

    ratingMethods[nextRating](this.video.uuid, this.videoPassword)
      .subscribe({
        next: () => {
          // Update the video like attribute
          this.updateVideoRating(this.userRating, nextRating)
          this.userRating = nextRating
          this.rateUpdated.emit(this.userRating)
        },

        error: err => this.notifier.error(err.message)
      })
  }

  private updateVideoRating (oldRating: UserVideoRateType, newRating: UserVideoRateType) {
    let likesToIncrement = 0
    let dislikesToIncrement = 0

    if (oldRating) {
      if (oldRating === 'like') likesToIncrement--
      if (oldRating === 'dislike') dislikesToIncrement--
    }

    if (newRating === 'like') likesToIncrement++
    if (newRating === 'dislike') dislikesToIncrement++

    this.video.likes += likesToIncrement
    this.video.dislikes += dislikesToIncrement

    this.video.buildLikeAndDislikePercents()
  }
}