Optimize video views component

This commit is contained in:
Chocobozzz 2024-11-25 16:32:24 +01:00
parent a253a8088a
commit 4551314baa
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
20 changed files with 71 additions and 63 deletions

View File

@ -54,7 +54,7 @@
} @else { } @else {
<my-upload-progress <my-upload-progress
[isUploading]="uploadingArchive" [uploadPercents]="uploadPercents" [error]="error" [uploaded]="archiveUploadFinished" [isUploading]="uploadingArchive" [uploadPercents]="uploadPercents" [error]="error" [uploaded]="archiveUploadFinished"
[enableRetryAfterError]="enableRetryAfterError" (cancel)="cancelUpload()" (retry)="retryUpload()" [enableRetryAfterError]="enableRetryAfterError" (cancelUpload)="cancelUpload()" (retry)="retryUpload()"
> >
</my-upload-progress> </my-upload-progress>

View File

@ -50,7 +50,7 @@
<my-upload-progress <my-upload-progress
[isUploading]="isUploadingVideo" [uploadPercents]="videoUploadPercents" [error]="error" [uploaded]="videoUploaded" [isUploading]="isUploadingVideo" [uploadPercents]="videoUploadPercents" [error]="error" [uploaded]="videoUploaded"
[enableRetryAfterError]="enableRetryAfterError" (cancel)="cancelUpload()" (retry)="retryUpload()" [enableRetryAfterError]="enableRetryAfterError" (cancelUpload)="cancelUpload()" (retry)="retryUpload()"
> >
</my-upload-progress> </my-upload-progress>

View File

@ -3,7 +3,7 @@
<my-upload-progress <my-upload-progress
[isUploading]="isReplacingVideoFile" [uploadPercents]="videoUploadPercents" [error]="uploadError" [uploaded]="updateDone" [isUploading]="isReplacingVideoFile" [uploadPercents]="videoUploadPercents" [error]="uploadError" [uploaded]="updateDone"
[enableRetryAfterError]="false" (cancel)="cancelUpload()" [enableRetryAfterError]="false" (cancelUpload)="cancelUpload()"
> >
</my-upload-progress> </my-upload-progress>

View File

@ -61,7 +61,7 @@ export class VideoCommentAddComponent extends FormReactive implements OnChanges,
@Input() textValue?: string @Input() textValue?: string
@Output() commentCreated = new EventEmitter<VideoComment>() @Output() commentCreated = new EventEmitter<VideoComment>()
@Output() cancel = new EventEmitter() @Output() cancelEdition = new EventEmitter()
@ViewChild('visitorModal', { static: true }) visitorModal: NgbModal @ViewChild('visitorModal', { static: true }) visitorModal: NgbModal
@ViewChild('emojiModal', { static: true }) emojiModal: NgbModal @ViewChild('emojiModal', { static: true }) emojiModal: NgbModal
@ -186,7 +186,7 @@ export class VideoCommentAddComponent extends FormReactive implements OnChanges,
} }
cancelCommentReply () { cancelCommentReply () {
this.cancel.emit(null) this.cancelEdition.emit(null)
this.form.value['text'] = this.textareaElement.nativeElement.value = '' this.form.value['text'] = this.textareaElement.nativeElement.value = ''
} }

View File

@ -69,7 +69,7 @@
[parentComments]="newParentComments" [parentComments]="newParentComments"
[focusOnInit]="true" [focusOnInit]="true"
(commentCreated)="onCommentReplyCreated($event)" (commentCreated)="onCommentReplyCreated($event)"
(cancel)="onResetReply()" (cancelEdition)="onResetReply()"
[textValue]="redraftValue" [textValue]="redraftValue"
></my-video-comment-add> ></my-video-comment-add>

View File

@ -43,7 +43,7 @@
<ng-template #publishedTemplate> <ng-template #publishedTemplate>
<ng-container i18n>Published <my-date-toggle [date]="video.publishedAt"></my-date-toggle></ng-container> <ng-container i18n>Published <my-date-toggle [date]="video.publishedAt"></my-date-toggle></ng-container>
<my-video-views-counter [video]="video"></my-video-views-counter> <my-video-views-counter [isLive]="video.isLive" [viewers]="video.viewers" [views]="video.views"></my-video-views-counter>
</ng-template> </ng-template>
<div class="d-block d-md-none"> <!-- only shown on medium devices, has its counterpart for larger viewports below --> <div class="d-block d-md-none"> <!-- only shown on medium devices, has its counterpart for larger viewports below -->

View File

@ -50,7 +50,7 @@ import { forkJoin, map, Observable, of, Subscription, switchMap } from 'rxjs'
import { import {
HLSOptions, HLSOptions,
PeerTubePlayer, PeerTubePlayer,
PeerTubePlayerContructorOptions, PeerTubePlayerConstructorOptions,
PeerTubePlayerLoadOptions, PeerTubePlayerLoadOptions,
PlayerMode, PlayerMode,
videojs videojs
@ -662,7 +662,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
private buildPeerTubePlayerConstructorOptions (options: { private buildPeerTubePlayerConstructorOptions (options: {
urlOptions: URLOptions urlOptions: URLOptions
}): PeerTubePlayerContructorOptions { }): PeerTubePlayerConstructorOptions {
const { urlOptions } = options const { urlOptions } = options
return { return {

View File

@ -4,10 +4,12 @@
<ng-content></ng-content> <ng-content></ng-content>
</ng-template> </ng-template>
<a *ngIf="!href" [routerLink]="internalLink" [attr.title]="title" [ariaLabel]="ariaLabel" [tabindex]="tabindex" [ngClass]="builtClasses"> @if (href) {
<a [href]="href" [target]="target" [attr.title]="title" [ariaLabel]="ariaLabel" [tabindex]="tabindex" [ngClass]="builtClasses">
<ng-template *ngTemplateOutlet="content"></ng-template> <ng-template *ngTemplateOutlet="content"></ng-template>
</a> </a>
} @else {
<a *ngIf="href" [href]="href" [target]="target" [attr.title]="title" [ariaLabel]="ariaLabel" [tabindex]="tabindex" [ngClass]="builtClasses"> <a [routerLink]="internalLink" [attr.title]="title" [ariaLabel]="ariaLabel" [tabindex]="tabindex" [ngClass]="builtClasses">
<ng-template *ngTemplateOutlet="content"></ng-template> <ng-template *ngTemplateOutlet="content"></ng-template>
</a> </a>
}

View File

@ -1,23 +1,23 @@
import { AuthUser } from '@app/core' import { AuthUser } from '@app/core'
import { User } from '@app/core/users/user.model' import { User } from '@app/core/users/user.model'
import { durationToString, formatICU, getAbsoluteAPIUrl, getAbsoluteEmbedUrl } from '@app/helpers' import { durationToString, getAbsoluteAPIUrl, getAbsoluteEmbedUrl } from '@app/helpers'
import { Actor } from '@app/shared/shared-main/account/actor.model' import { Actor } from '@app/shared/shared-main/account/actor.model'
import { buildVideoWatchPath, getAllFiles, peertubeTranslate } from '@peertube/peertube-core-utils' import { buildVideoWatchPath, getAllFiles, peertubeTranslate } from '@peertube/peertube-core-utils'
import { import {
ActorImage, ActorImage,
HTMLServerConfig, HTMLServerConfig,
UserRight, UserRight,
Video as VideoServerModel,
VideoConstant, VideoConstant,
VideoFile, VideoFile,
VideoPrivacy, VideoPrivacy,
VideoPrivacyType, VideoPrivacyType,
VideoScheduleUpdate, VideoScheduleUpdate,
Video as VideoServerModel,
VideoSource,
VideoState, VideoState,
VideoStateType, VideoStateType,
VideoStreamingPlaylist, VideoStreamingPlaylist,
VideoStreamingPlaylistType, VideoStreamingPlaylistType
VideoSource
} from '@peertube/peertube-models' } from '@peertube/peertube-models'
export class Video implements VideoServerModel { export class Video implements VideoServerModel {
@ -326,12 +326,4 @@ export class Video implements VideoServerModel {
this.isLocal === true && this.isLocal === true &&
(this.account.name === user.username || user.hasRight(UserRight.SEE_ALL_VIDEOS)) (this.account.name === user.username || user.hasRight(UserRight.SEE_ALL_VIDEOS))
} }
getExactNumberOfViews () {
if (this.isLive) {
return formatICU($localize`{viewers, plural, =0 {No viewers} =1 {1 viewer} other {{viewers} viewers}}`, { viewers: this.viewers })
}
return formatICU($localize`{views, plural, =0 {No view} =1 {1 view} other {{views} views}}`, { views: this.views })
}
} }

View File

@ -1,10 +1,12 @@
<a [ariaLabel]="ariaLabel" *ngIf="!videoHref" [routerLink]="getVideoRouterLink()" [queryParams]="queryParams" class="video-thumbnail" tabindex="-1"> @if (videoHref) {
<a [ariaLabel]="ariaLabel" [href]="videoHref" [target]="videoTarget" class="video-thumbnail" tabindex="-1">
<ng-container *ngTemplateOutlet="aContent"></ng-container> <ng-container *ngTemplateOutlet="aContent"></ng-container>
</a> </a>
} @else {
<a [ariaLabel]="ariaLabel" *ngIf="videoHref" [href]="videoHref" [target]="videoTarget" class="video-thumbnail" tabindex="-1"> <a [ariaLabel]="ariaLabel" [routerLink]="getVideoRouterLink()" [queryParams]="queryParams" class="video-thumbnail" tabindex="-1">
<ng-container *ngTemplateOutlet="aContent"></ng-container> <ng-container *ngTemplateOutlet="aContent"></ng-container>
</a> </a>
}
<ng-template #aContent> <ng-template #aContent>
<img alt="" [attr.src]="getImageUrl()" [ngClass]="{ 'blur-filter': nsfw }" /> <img alt="" [attr.src]="getImageUrl()" [ngClass]="{ 'blur-filter': nsfw }" />

View File

@ -49,10 +49,10 @@
<div class="date-and-views"> <div class="date-and-views">
<my-date-toggle *ngIf="displayOptions.date" [date]="video.publishedAt"></my-date-toggle> <my-date-toggle *ngIf="displayOptions.date" [date]="video.publishedAt"></my-date-toggle>
<span class="views" [title]="video.getExactNumberOfViews()"> <span class="views">
<ng-container *ngIf="displayOptions.date && displayOptions.views"></ng-container> <ng-container *ngIf="displayOptions.date && displayOptions.views"></ng-container>
<my-video-views-counter *ngIf="displayOptions.views" [video]="video"></my-video-views-counter> <my-video-views-counter *ngIf="displayOptions.views" [isLive]="video.isLive" [viewers]="video.viewers" [views]="video.views"></my-video-views-counter>
</span> </span>
</div> </div>

View File

@ -28,8 +28,8 @@
<span class="date-and-views"> <span class="date-and-views">
<my-date-toggle [date]="playlistElement.video.publishedAt"></my-date-toggle> <my-date-toggle [date]="playlistElement.video.publishedAt"></my-date-toggle>
<span class="views" [title]="playlistElement.video.getExactNumberOfViews()"> <span class="views">
<my-video-views-counter [video]="playlistElement.video"></my-video-views-counter> <my-video-views-counter [isLive]="playlistElement.video.isLive" [viewers]="playlistElement.video.viewers" [views]="playlistElement.video.views"></my-video-views-counter>
</span> </span>
</span> </span>

View File

@ -1,9 +1,11 @@
<span [title]="video.getExactNumberOfViews()"> <span [title]="getExactNumberOfViews()">
<ng-container i18n *ngIf="!video.isLive"> @if (isLive) {
{video.views, plural, =1 {1 view} other {{{ video.views | myNumberFormatter }} views}} <ng-container i18n>
{viewers, plural, =1 {1 viewer} other {{{ viewers | myNumberFormatter }} viewers}}
</ng-container> </ng-container>
} @else {
<ng-container i18n *ngIf="video.isLive"> <ng-container i18n>
{video.viewers, plural, =1 {1 viewer} other {{{ video.viewers | myNumberFormatter }} viewers}} {views, plural, =1 {1 view} other {{{ views | myNumberFormatter }} views}}
</ng-container> </ng-container>
}
</span> </span>

View File

@ -1,15 +1,25 @@
import { Component, Input } from '@angular/core' import { booleanAttribute, ChangeDetectionStrategy, Component, Input } from '@angular/core'
import { formatICU } from '@app/helpers'
import { NumberFormatterPipe } from '../shared-main/common/number-formatter.pipe' import { NumberFormatterPipe } from '../shared-main/common/number-formatter.pipe'
import { NgIf } from '@angular/common'
import { Video } from '../shared-main/video/video.model'
@Component({ @Component({
selector: 'my-video-views-counter', selector: 'my-video-views-counter',
styleUrls: [ './video-views-counter.component.scss' ], styleUrls: [ './video-views-counter.component.scss' ],
templateUrl: './video-views-counter.component.html', templateUrl: './video-views-counter.component.html',
standalone: true, standalone: true,
imports: [ NgIf, NumberFormatterPipe ] changeDetection: ChangeDetectionStrategy.OnPush,
imports: [ NumberFormatterPipe ]
}) })
export class VideoViewsCounterComponent { export class VideoViewsCounterComponent {
@Input() video: Video @Input({ required: true, transform: booleanAttribute }) isLive: boolean
@Input({ required: true }) viewers: number
@Input({ required: true }) views: number
getExactNumberOfViews () {
if (this.isLive) {
return formatICU($localize`{viewers, plural, =0 {No viewers} =1 {1 viewer} other {{viewers} viewers}}`, { viewers: this.viewers })
}
return formatICU($localize`{views, plural, =0 {No view} =1 {1 view} other {{views} views}}`, { views: this.views })
}
} }

View File

@ -8,7 +8,7 @@
<input <input
*ngIf="uploaded === false" *ngIf="uploaded === false"
type="button" class="peertube-button secondary-button ms-1" i18n-value="Cancel ongoing upload" value="Cancel" (click)="cancel.emit()" type="button" class="peertube-button secondary-button ms-1" i18n-value="Cancel ongoing upload" value="Cancel" (click)="cancelUpload.emit()"
/> />
</div> </div>
@ -20,7 +20,7 @@
</my-progress-bar> </my-progress-bar>
<input type="button" class="peertube-button secondary-button ms-1" i18n-value="Retry failed upload" value="Retry" (click)="retry.emit()" /> <input type="button" class="peertube-button secondary-button ms-1" i18n-value="Retry failed upload" value="Retry" (click)="retry.emit()" />
<input type="button" class="peertube-button secondary-button ms-1" i18n-value="Cancel ongoing upload" value="Cancel" (click)="cancel.emit()" /> <input type="button" class="peertube-button secondary-button ms-1" i18n-value="Cancel ongoing upload" value="Cancel" (click)="cancelUpload.emit()" />
</div> </div>
<my-alert *ngIf="error && !enableRetryAfterError" type="danger"> <my-alert *ngIf="error && !enableRetryAfterError" type="danger">

View File

@ -17,7 +17,7 @@ export class UploadProgressComponent {
@Input() uploaded: boolean @Input() uploaded: boolean
@Input() enableRetryAfterError: boolean @Input() enableRetryAfterError: boolean
@Output() cancel = new EventEmitter() @Output() cancelUpload = new EventEmitter()
@Output() retry = new EventEmitter() @Output() retry = new EventEmitter()
getUploadingLabel () { getUploadingLabel () {

View File

@ -41,7 +41,7 @@ import { buildVideoLink, decorateVideoLink, isDefaultLocale, pick } from '@peert
import { saveAverageBandwidth } from './peertube-player-local-storage' import { saveAverageBandwidth } from './peertube-player-local-storage'
import { ControlBarOptionsBuilder, HLSOptionsBuilder, WebVideoOptionsBuilder } from './shared/player-options-builder' import { ControlBarOptionsBuilder, HLSOptionsBuilder, WebVideoOptionsBuilder } from './shared/player-options-builder'
import { TranslationsManager } from './translations-manager' import { TranslationsManager } from './translations-manager'
import { PeerTubePlayerContructorOptions, PeerTubePlayerLoadOptions, PlayerNetworkInfo, VideoJSPluginOptions } from './types' import { PeerTubePlayerConstructorOptions, PeerTubePlayerLoadOptions, PlayerNetworkInfo, VideoJSPluginOptions } from './types'
// Change 'Playback Rate' to 'Speed' (smaller for our settings menu) // Change 'Playback Rate' to 'Speed' (smaller for our settings menu)
(videojs.getComponent('PlaybackRateMenuButton') as any).prototype.controlText_ = 'Speed' (videojs.getComponent('PlaybackRateMenuButton') as any).prototype.controlText_ = 'Speed'
@ -69,7 +69,7 @@ export class PeerTubePlayer {
private currentLoadOptions: PeerTubePlayerLoadOptions private currentLoadOptions: PeerTubePlayerLoadOptions
constructor (private options: PeerTubePlayerContructorOptions) { constructor (private options: PeerTubePlayerConstructorOptions) {
this.pluginsManager = options.pluginsManager this.pluginsManager = options.pluginsManager
} }

View File

@ -1,13 +1,13 @@
import { import {
NextPreviousVideoButtonOptions, NextPreviousVideoButtonOptions,
PeerTubeLinkButtonOptions, PeerTubeLinkButtonOptions,
PeerTubePlayerContructorOptions, PeerTubePlayerConstructorOptions,
PeerTubePlayerLoadOptions, PeerTubePlayerLoadOptions,
TheaterButtonOptions TheaterButtonOptions
} from '../../types' } from '../../types'
type ControlBarOptionsBuilderConstructorOptions = type ControlBarOptionsBuilderConstructorOptions =
Pick<PeerTubePlayerContructorOptions, 'peertubeLink' | 'instanceName' | 'theaterButton'> & Pick<PeerTubePlayerConstructorOptions, 'peertubeLink' | 'instanceName' | 'theaterButton'> &
{ {
videoShortUUID: () => string videoShortUUID: () => string
p2pEnabled: () => boolean p2pEnabled: () => boolean

View File

@ -10,7 +10,7 @@ import {
HLSLoaderClass, HLSLoaderClass,
HLSPluginOptions, HLSPluginOptions,
P2PMediaLoaderPluginOptions, P2PMediaLoaderPluginOptions,
PeerTubePlayerContructorOptions, PeerTubePlayerConstructorOptions,
PeerTubePlayerLoadOptions PeerTubePlayerLoadOptions
} from '../../types' } from '../../types'
import { getRtcConfig, isSameOrigin } from '../common' import { getRtcConfig, isSameOrigin } from '../common'
@ -22,7 +22,7 @@ import debug from 'debug'
const debugLogger = debug('peertube:player:hls') const debugLogger = debug('peertube:player:hls')
type ConstructorOptions = type ConstructorOptions =
Pick<PeerTubePlayerContructorOptions, 'pluginsManager' | 'serverUrl' | 'authorizationHeader' | 'stunServers'> & Pick<PeerTubePlayerConstructorOptions, 'pluginsManager' | 'serverUrl' | 'authorizationHeader' | 'stunServers'> &
Pick<PeerTubePlayerLoadOptions, 'videoPassword' | 'requiresUserAuth' | 'videoFileToken' | 'requiresPassword' | Pick<PeerTubePlayerLoadOptions, 'videoPassword' | 'requiresUserAuth' | 'videoFileToken' | 'requiresPassword' |
'isLive' | 'liveOptions' | 'p2pEnabled' | 'hls'> 'isLive' | 'liveOptions' | 'p2pEnabled' | 'hls'>

View File

@ -5,7 +5,7 @@ import { PlaylistPluginOptions, VideoJSCaption, VideoJSStoryboard } from './peer
export type PlayerMode = 'web-video' | 'p2p-media-loader' export type PlayerMode = 'web-video' | 'p2p-media-loader'
export type PeerTubePlayerContructorOptions = { export type PeerTubePlayerConstructorOptions = {
playerElement: () => HTMLVideoElement playerElement: () => HTMLVideoElement
controls: boolean controls: boolean