]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/commitdiff
Create a dedicated component for video rating
authorChocobozzz <me@florianbigard.com>
Tue, 29 Jun 2021 15:00:30 +0000 (17:00 +0200)
committerChocobozzz <me@florianbigard.com>
Tue, 29 Jun 2021 15:00:30 +0000 (17:00 +0200)
client/src/app/+videos/+video-watch/video-rate.component.html [new file with mode: 0644]
client/src/app/+videos/+video-watch/video-rate.component.scss [new file with mode: 0644]
client/src/app/+videos/+video-watch/video-rate.component.ts [new file with mode: 0644]
client/src/app/+videos/+video-watch/video-watch.component.html
client/src/app/+videos/+video-watch/video-watch.component.scss
client/src/app/+videos/+video-watch/video-watch.component.ts
client/src/app/+videos/+video-watch/video-watch.module.ts

diff --git a/client/src/app/+videos/+video-watch/video-rate.component.html b/client/src/app/+videos/+video-watch/video-rate.component.html
new file mode 100644 (file)
index 0000000..7dd9b36
--- /dev/null
@@ -0,0 +1,23 @@
+<ng-template #ratePopoverText>
+  <span [innerHTML]="getRatePopoverText()"></span>
+</ng-template>
+
+<button
+  [ngbPopover]="getRatePopoverText() && ratePopoverText" [ngClass]="{ 'activated': userRating === 'like' }" (click)="setLike()" (keyup.enter)="setLike()"
+  class="action-button action-button-like" [attr.aria-pressed]="userRating === 'like'" [attr.aria-label]="tooltipLike"
+  [ngbTooltip]="tooltipLike"
+  placement="bottom auto"
+>
+  <my-global-icon iconName="like"></my-global-icon>
+  <span *ngIf="video.likes" class="count">{{ video.likes }}</span>
+</button>
+
+<button
+  [ngbPopover]="getRatePopoverText() && ratePopoverText" [ngClass]="{ 'activated': userRating === 'dislike' }" (click)="setDislike()" (keyup.enter)="setDislike()"
+  class="action-button action-button-dislike" [attr.aria-pressed]="userRating === 'dislike'" [attr.aria-label]="tooltipDislike"
+  [ngbTooltip]="tooltipDislike"
+  placement="bottom auto"
+>
+  <my-global-icon iconName="dislike"></my-global-icon>
+  <span *ngIf="video.dislikes" class="count">{{ video.dislikes }}</span>
+</button>
diff --git a/client/src/app/+videos/+video-watch/video-rate.component.scss b/client/src/app/+videos/+video-watch/video-rate.component.scss
new file mode 100644 (file)
index 0000000..f4f696f
--- /dev/null
@@ -0,0 +1,15 @@
+@use '_variables' as *;
+@use '_mixins' as *;
+
+.action-button-like,
+.action-button-dislike {
+  filter: brightness(120%);
+
+  .count {
+    margin: 0 5px;
+  }
+}
+
+.activated {
+  color: pvar(--activatedActionButtonColor) !important;
+}
diff --git a/client/src/app/+videos/+video-watch/video-rate.component.ts b/client/src/app/+videos/+video-watch/video-rate.component.ts
new file mode 100644 (file)
index 0000000..89a666a
--- /dev/null
@@ -0,0 +1,142 @@
+import { Hotkey, HotkeysService } from 'angular2-hotkeys'
+import { Observable } from 'rxjs'
+import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output } from '@angular/core'
+import { Notifier, ScreenService } from '@app/core'
+import { VideoDetails, VideoService } from '@app/shared/shared-main'
+import { UserVideoRateType } from '@shared/models'
+
+@Component({
+  selector: 'my-video-rate',
+  templateUrl: './video-rate.component.html',
+  styleUrls: [ './video-rate.component.scss' ]
+})
+export class VideoRateComponent implements OnInit, OnChanges, OnDestroy {
+  @Input() video: VideoDetails
+  @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
+  ) { }
+
+  async 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
+        }, undefined, $localize`Like the video`),
+
+        new Hotkey('shift+d', () => {
+          this.setDislike()
+          return false
+        }, undefined, $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 <a href="/login">logged in</a> to rate this video.`
+  }
+
+  private checkUserRating () {
+    // Unlogged users do not have ratings
+    if (this.isUserLoggedIn === false) return
+
+    this.videoService.getUserVideoRating(this.video.id)
+        .subscribe(
+          ratingObject => {
+            if (!ratingObject) return
+
+            this.userRating = ratingObject.rating
+            this.userRatingLoaded.emit(this.userRating)
+          },
+
+          err => this.notifier.error(err.message)
+        )
+  }
+
+  private setRating (nextRating: UserVideoRateType) {
+    const ratingMethods: { [id in UserVideoRateType]: (id: number) => Observable<any> } = {
+      like: this.videoService.setVideoLike,
+      dislike: this.videoService.setVideoDislike,
+      none: this.videoService.unsetVideoLike
+    }
+
+    ratingMethods[nextRating].call(this.videoService, this.video.id)
+          .subscribe(
+            () => {
+              // Update the video like attribute
+              this.updateVideoRating(this.userRating, nextRating)
+              this.userRating = nextRating
+              this.rateUpdated.emit(this.userRating)
+            },
+
+            (err: { message: string }) => 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()
+  }
+}
index 4e424d95d5c1339d6958bd33efffc17f95eeac9f..a659a7db162f04bdb3fd9bffd92af1083c4a7fb1 100644 (file)
               </div>
 
               <div class="video-actions-rates">
-                <ng-template #ratePopoverText>
-                  <span [innerHTML]="getRatePopoverText()"></span>
-                </ng-template>
-
                 <div class="video-actions full-width justify-content-end">
-                  <button
-                    [ngbPopover]="getRatePopoverText() && ratePopoverText" [ngClass]="{ 'activated': userRating === 'like' }" (click)="setLike()" (keyup.enter)="setLike()"
-                    class="action-button action-button-like" [attr.aria-pressed]="userRating === 'like'" [attr.aria-label]="tooltipLike"
-                    [ngbTooltip]="tooltipLike"
-                    placement="bottom auto"
-                  >
-                    <my-global-icon iconName="like"></my-global-icon>
-                    <span *ngIf="video.likes" class="count">{{ video.likes }}</span>
-                </button>
-
-                  <button
-                    [ngbPopover]="getRatePopoverText() && ratePopoverText" [ngClass]="{ 'activated': userRating === 'dislike' }" (click)="setDislike()" (keyup.enter)="setDislike()"
-                    class="action-button action-button-dislike" [attr.aria-pressed]="userRating === 'dislike'" [attr.aria-label]="tooltipDislike"
-                    [ngbTooltip]="tooltipDislike"
-                    placement="bottom auto"
-                  >
-                    <my-global-icon iconName="dislike"></my-global-icon>
-                    <span *ngIf="video.dislikes" class="count">{{ video.dislikes }}</span>
-                  </button>
+                  <my-video-rate
+                    [video]="video" [isUserLoggedIn]="isUserLoggedIn()"
+                    (rateUpdated)="onRateUpdated($event)" (userRatingLoaded)="onRateUpdated($event)"
+                  ></my-video-rate>
 
                   <button *ngIf="video.support" (click)="showSupportModal()" (keyup.enter)="showSupportModal()" class="action-button action-button-support" [attr.aria-label]="tooltipSupport"
                     [ngbTooltip]="tooltipSupport"
index 7b79344567300fef46db701eca1664c042d3be98..4d68504f5fc491f884fa597031258244401b1f75 100644 (file)
@@ -239,7 +239,6 @@ $video-info-margin-left: 44px;
           ::ng-deep.action-button {
             @include peertube-button;
             @include button-with-icon(21px, 0, -1px);
-            @include apply-svg-color(pvar(--actionButtonColor));
 
             font-size: 100%;
             font-weight: $font-semibold;
@@ -258,35 +257,6 @@ $video-info-margin-left: 44px;
               opacity: 0.9;
             }
 
-            &.action-button-like,
-            &.action-button-dislike {
-              filter: brightness(120%);
-
-              .count {
-                margin: 0 5px;
-              }
-            }
-
-            &.action-button-like.activated {
-              .count {
-                color: pvar(--activatedActionButtonColor);
-              }
-
-              my-global-icon {
-                @include apply-svg-color(pvar(--activatedActionButtonColor));
-              }
-            }
-
-            &.action-button-dislike.activated {
-              .count {
-                color: pvar(--activatedActionButtonColor);
-              }
-
-              my-global-icon {
-                @include apply-svg-color(pvar(--activatedActionButtonColor));
-              }
-            }
-
             &.action-button-support {
               color: pvar(--supportButtonColor);
 
index 12b0baebe77486121d50cf44b50d456134f562b0..e2fbf752443fbff51dc572d40d75ca19843229ac 100644 (file)
@@ -1,5 +1,5 @@
 import { Hotkey, HotkeysService } from 'angular2-hotkeys'
-import { forkJoin, Observable, Subscription } from 'rxjs'
+import { forkJoin, Subscription } from 'rxjs'
 import { catchError } from 'rxjs/operators'
 import { PlatformLocation } from '@angular/common'
 import { ChangeDetectorRef, Component, ElementRef, Inject, LOCALE_ID, NgZone, OnDestroy, OnInit, ViewChild } from '@angular/core'
@@ -77,8 +77,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
 
   theaterEnabled = false
 
-  userRating: UserVideoRateType = null
-
   playerPlaceholderImgSrc: string
 
   video: VideoDetails = null
@@ -98,10 +96,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
   hasAlreadyAcceptedPrivacyConcern = false
   remoteServerDown = false
 
-  hotkeys: Hotkey[] = []
-
-  tooltipLike = ''
-  tooltipDislike = ''
   tooltipSupport = ''
   tooltipSaveToPlaylist = ''
 
@@ -117,6 +111,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
     liveInfo: true
   }
 
+  userRating: UserVideoRateType
+
   private nextVideoUuid = ''
   private nextVideoTitle = ''
   private currentTime: number
@@ -127,6 +123,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
 
   private serverConfig: HTMLServerConfig
 
+  private hotkeys: Hotkey[] = []
+
   constructor (
     private elementRef: ElementRef,
     private changeDetector: ChangeDetectorRef,
@@ -165,8 +163,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
   async ngOnInit () {
     // Hide the tooltips for unlogged users in mobile view, this adds confusion with the popover
     if (this.user || !this.screenService.isInMobileView()) {
-      this.tooltipLike = $localize`Like this video`
-      this.tooltipDislike = $localize`Dislike this video`
       this.tooltipSupport = $localize`Support options for this video`
       this.tooltipSaveToPlaylist = $localize`Save to playlist`
     }
@@ -232,28 +228,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
     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 <a href="/login">logged in</a> to rate this video.`
-  }
-
   showMoreDescription () {
     if (this.completeVideoDescription === undefined) {
       return this.loadCompleteDescription()
@@ -408,6 +382,11 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
     this.loadVideo(videoId)
   }
 
+  onRateUpdated (userRating: UserVideoRateType) {
+    this.userRating = userRating
+    this.setVideoLikesBarTooltipText()
+  }
+
   displayOtherVideosAsRow () {
     // Use the same value as in the SASS file
     return this.screenService.getWindowInnerWidth() <= 1100
@@ -544,22 +523,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
     this.notifier.error(errorMessage)
   }
 
-  private checkUserRating () {
-    // Unlogged users do not have ratings
-    if (this.isUserLoggedIn() === false) return
-
-    this.videoService.getUserVideoRating(this.video.id)
-        .subscribe(
-          ratingObject => {
-            if (ratingObject) {
-              this.userRating = ratingObject.rating
-            }
-          },
-
-          err => this.notifier.error(err.message)
-        )
-  }
-
   private async onVideoFetched (
     video: VideoDetails,
     videoCaptions: VideoCaption[],
@@ -593,7 +556,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
     this.setVideoLikesBarTooltipText()
 
     this.setOpenGraphTags()
-    this.checkUserRating()
 
     const hookOptions = {
       videojs,
@@ -706,44 +668,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
     }
   }
 
-  private setRating (nextRating: UserVideoRateType) {
-    const ratingMethods: { [id in UserVideoRateType]: (id: number) => Observable<any> } = {
-      like: this.videoService.setVideoLike,
-      dislike: this.videoService.setVideoDislike,
-      none: this.videoService.unsetVideoLike
-    }
-
-    ratingMethods[nextRating].call(this.videoService, this.video.id)
-          .subscribe(
-            () => {
-              // Update the video like attribute
-              this.updateVideoRating(this.userRating, nextRating)
-              this.userRating = nextRating
-            },
-
-            (err: { message: string }) => 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()
-    this.setVideoLikesBarTooltipText()
-  }
-
   private setOpenGraphTags () {
     this.metaService.setTitle(this.video.name)
 
@@ -982,16 +906,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
 
     if (this.isUserLoggedIn()) {
       this.hotkeys = this.hotkeys.concat([
-        new Hotkey('shift+l', () => {
-          this.setLike()
-          return false
-        }, undefined, $localize`Like the video`),
-
-        new Hotkey('shift+d', () => {
-          this.setDislike()
-          return false
-        }, undefined, $localize`Dislike the video`),
-
         new Hotkey('shift+s', () => {
           this.subscribeButton.subscribed ? this.subscribeButton.unsubscribe() : this.subscribeButton.subscribe()
           return false
index 62ce7be2ddf542001df235f26e7bfaa8c16ceb0c..57f36c723c7118a60e79019cb3a761985fed5f9d 100644 (file)
@@ -10,6 +10,7 @@ import { SharedVideoModule } from '@app/shared/shared-video'
 import { SharedVideoCommentModule } from '@app/shared/shared-video-comment'
 import { SharedVideoMiniatureModule } from '@app/shared/shared-video-miniature'
 import { SharedVideoPlaylistModule } from '@app/shared/shared-video-playlist'
+import { SharedActorImageModule } from '../../shared/shared-actor-image/shared-actor-image.module'
 import { VideoCommentService } from '../../shared/shared-video-comment/video-comment.service'
 import { VideoCommentAddComponent } from './comment/video-comment-add.component'
 import { VideoCommentComponent } from './comment/video-comment.component'
@@ -17,11 +18,11 @@ import { VideoCommentsComponent } from './comment/video-comments.component'
 import { PlayerStylesComponent } from './player-styles.component'
 import { RecommendationsModule } from './recommendations/recommendations.module'
 import { TimestampRouteTransformerDirective } from './timestamp-route-transformer.directive'
+import { VideoAvatarChannelComponent } from './video-avatar-channel.component'
+import { VideoRateComponent } from './video-rate.component'
 import { VideoWatchPlaylistComponent } from './video-watch-playlist.component'
 import { VideoWatchRoutingModule } from './video-watch-routing.module'
 import { VideoWatchComponent } from './video-watch.component'
-import { SharedActorImageModule } from '../../shared/shared-actor-image/shared-actor-image.module'
-import { VideoAvatarChannelComponent } from './video-avatar-channel.component'
 
 @NgModule({
   imports: [
@@ -45,6 +46,7 @@ import { VideoAvatarChannelComponent } from './video-avatar-channel.component'
   declarations: [
     VideoWatchComponent,
     VideoWatchPlaylistComponent,
+    VideoRateComponent,
 
     VideoCommentsComponent,
     VideoCommentAddComponent,