From 5bcbcbe338ef5a1ed14f084311d013fbb25dabcf Mon Sep 17 00:00:00 2001 From: Rigel Kent Date: Fri, 22 Jan 2021 00:12:44 +0100 Subject: modularize abstract video list header and implement video hotness recommendation variant --- client/src/app/+videos/video-list/index.ts | 3 +- .../src/app/+videos/video-list/trending/index.ts | 4 + .../video-list/trending/video-hot.component.ts | 85 +++++++++++++++++++ .../trending/video-most-liked.component.ts | 81 ++++++++++++++++++ .../trending/video-trending-header.component.html | 6 ++ .../trending/video-trending-header.component.scss | 17 ++++ .../trending/video-trending-header.component.ts | 59 +++++++++++++ .../trending/video-trending.component.ts | 99 ++++++++++++++++++++++ .../+videos/video-list/video-local.component.ts | 3 +- .../video-list/video-most-liked.component.ts | 68 --------------- .../video-list/video-recently-added.component.ts | 3 +- .../+videos/video-list/video-trending.component.ts | 83 ------------------ .../video-user-subscriptions.component.ts | 6 +- 13 files changed, 360 insertions(+), 157 deletions(-) create mode 100644 client/src/app/+videos/video-list/trending/index.ts create mode 100644 client/src/app/+videos/video-list/trending/video-hot.component.ts create mode 100644 client/src/app/+videos/video-list/trending/video-most-liked.component.ts create mode 100644 client/src/app/+videos/video-list/trending/video-trending-header.component.html create mode 100644 client/src/app/+videos/video-list/trending/video-trending-header.component.scss create mode 100644 client/src/app/+videos/video-list/trending/video-trending-header.component.ts create mode 100644 client/src/app/+videos/video-list/trending/video-trending.component.ts delete mode 100644 client/src/app/+videos/video-list/video-most-liked.component.ts delete mode 100644 client/src/app/+videos/video-list/video-trending.component.ts (limited to 'client/src/app/+videos/video-list') diff --git a/client/src/app/+videos/video-list/index.ts b/client/src/app/+videos/video-list/index.ts index af1bd58b7..dc27e29e2 100644 --- a/client/src/app/+videos/video-list/index.ts +++ b/client/src/app/+videos/video-list/index.ts @@ -1,5 +1,4 @@ export * from './overview' +export * from './trending' export * from './video-local.component' export * from './video-recently-added.component' -export * from './video-trending.component' -export * from './video-most-liked.component' diff --git a/client/src/app/+videos/video-list/trending/index.ts b/client/src/app/+videos/video-list/trending/index.ts new file mode 100644 index 000000000..8bae205a5 --- /dev/null +++ b/client/src/app/+videos/video-list/trending/index.ts @@ -0,0 +1,4 @@ +export * from './video-trending-header.component' +export * from './video-trending.component' +export * from './video-hot.component' +export * from './video-most-liked.component' diff --git a/client/src/app/+videos/video-list/trending/video-hot.component.ts b/client/src/app/+videos/video-list/trending/video-hot.component.ts new file mode 100644 index 000000000..1617eb21e --- /dev/null +++ b/client/src/app/+videos/video-list/trending/video-hot.component.ts @@ -0,0 +1,85 @@ +import { Component, ComponentFactoryResolver, Injector, OnDestroy, OnInit } from '@angular/core' +import { ActivatedRoute, Router } from '@angular/router' +import { AuthService, LocalStorageService, Notifier, ScreenService, ServerService, UserService } from '@app/core' +import { HooksService } from '@app/core/plugins/hooks.service' +import { immutableAssign } from '@app/helpers' +import { VideoService } from '@app/shared/shared-main' +import { AbstractVideoList } from '@app/shared/shared-video-miniature' +import { VideoSortField } from '@shared/models' +import { VideoTrendingHeaderComponent } from './video-trending-header.component' + +@Component({ + selector: 'my-videos-hot', + styleUrls: [ '../../../shared/shared-video-miniature/abstract-video-list.scss' ], + templateUrl: '../../../shared/shared-video-miniature/abstract-video-list.html' +}) +export class VideoHotComponent extends AbstractVideoList implements OnInit, OnDestroy { + HeaderComponent = VideoTrendingHeaderComponent + titlePage: string + defaultSort: VideoSortField = '-hot' + + useUserVideoPreferences = true + + constructor ( + protected router: Router, + protected serverService: ServerService, + protected route: ActivatedRoute, + protected notifier: Notifier, + protected authService: AuthService, + protected userService: UserService, + protected screenService: ScreenService, + protected storageService: LocalStorageService, + protected cfr: ComponentFactoryResolver, + private videoService: VideoService, + private hooks: HooksService + ) { + super() + + this.headerComponentInjector = this.getInjector() + } + + ngOnInit () { + super.ngOnInit() + + this.generateSyndicationList() + } + + ngOnDestroy () { + super.ngOnDestroy() + } + + getVideosObservable (page: number) { + const newPagination = immutableAssign(this.pagination, { currentPage: page }) + const params = { + videoPagination: newPagination, + sort: this.sort, + categoryOneOf: this.categoryOneOf, + languageOneOf: this.languageOneOf, + nsfwPolicy: this.nsfwPolicy, + skipCount: true + } + + return this.hooks.wrapObsFun( + this.videoService.getVideos.bind(this.videoService), + params, + 'common', + 'filter:api.trending-videos.videos.list.params', + 'filter:api.trending-videos.videos.list.result' + ) + } + + generateSyndicationList () { + this.syndicationItems = this.videoService.getVideoFeedUrls(this.sort, undefined, this.categoryOneOf) + } + + getInjector () { + return Injector.create({ + providers: [{ + provide: 'data', + useValue: { + model: this.defaultSort + } + }] + }) + } +} diff --git a/client/src/app/+videos/video-list/trending/video-most-liked.component.ts b/client/src/app/+videos/video-list/trending/video-most-liked.component.ts new file mode 100644 index 000000000..1781cc6aa --- /dev/null +++ b/client/src/app/+videos/video-list/trending/video-most-liked.component.ts @@ -0,0 +1,81 @@ +import { Component, ComponentFactoryResolver, Injector, OnInit } from '@angular/core' +import { ActivatedRoute, Router } from '@angular/router' +import { AuthService, LocalStorageService, Notifier, ScreenService, ServerService, UserService } from '@app/core' +import { HooksService } from '@app/core/plugins/hooks.service' +import { immutableAssign } from '@app/helpers' +import { VideoService } from '@app/shared/shared-main' +import { AbstractVideoList } from '@app/shared/shared-video-miniature' +import { VideoSortField } from '@shared/models' +import { VideoTrendingHeaderComponent } from './video-trending-header.component' + +@Component({ + selector: 'my-videos-most-liked', + styleUrls: [ '../../../shared/shared-video-miniature/abstract-video-list.scss' ], + templateUrl: '../../../shared/shared-video-miniature/abstract-video-list.html' +}) +export class VideoMostLikedComponent extends AbstractVideoList implements OnInit { + HeaderComponent = VideoTrendingHeaderComponent + titlePage: string + defaultSort: VideoSortField = '-likes' + + useUserVideoPreferences = true + + constructor ( + protected router: Router, + protected serverService: ServerService, + protected route: ActivatedRoute, + protected notifier: Notifier, + protected authService: AuthService, + protected userService: UserService, + protected screenService: ScreenService, + protected storageService: LocalStorageService, + protected cfr: ComponentFactoryResolver, + private videoService: VideoService, + private hooks: HooksService + ) { + super() + + this.headerComponentInjector = this.getInjector() + } + + ngOnInit () { + super.ngOnInit() + + this.generateSyndicationList() + } + + getVideosObservable (page: number) { + const newPagination = immutableAssign(this.pagination, { currentPage: page }) + const params = { + videoPagination: newPagination, + sort: this.sort, + categoryOneOf: this.categoryOneOf, + languageOneOf: this.languageOneOf, + nsfwPolicy: this.nsfwPolicy, + skipCount: true + } + + return this.hooks.wrapObsFun( + this.videoService.getVideos.bind(this.videoService), + params, + 'common', + 'filter:api.most-liked-videos.videos.list.params', + 'filter:api.most-liked-videos.videos.list.result' + ) + } + + generateSyndicationList () { + this.syndicationItems = this.videoService.getVideoFeedUrls(this.sort, undefined, this.categoryOneOf) + } + + getInjector () { + return Injector.create({ + providers: [{ + provide: 'data', + useValue: { + model: this.defaultSort + } + }] + }) + } +} diff --git a/client/src/app/+videos/video-list/trending/video-trending-header.component.html b/client/src/app/+videos/video-list/trending/video-trending-header.component.html new file mode 100644 index 000000000..6319ee6d3 --- /dev/null +++ b/client/src/app/+videos/video-list/trending/video-trending-header.component.html @@ -0,0 +1,6 @@ +
+ +
\ No newline at end of file diff --git a/client/src/app/+videos/video-list/trending/video-trending-header.component.scss b/client/src/app/+videos/video-list/trending/video-trending-header.component.scss new file mode 100644 index 000000000..923a1d67a --- /dev/null +++ b/client/src/app/+videos/video-list/trending/video-trending-header.component.scss @@ -0,0 +1,17 @@ +.btn-group label { + border: 1px solid transparent; + border-radius: 9999px !important; + padding: 5px 16px; + opacity: .8; + + &:not(:first-child) { + margin-left: .5rem; + } + + my-global-icon { + position: relative; + top: -2px; + height: 1rem; + margin-right: .1rem; + } +} \ No newline at end of file diff --git a/client/src/app/+videos/video-list/trending/video-trending-header.component.ts b/client/src/app/+videos/video-list/trending/video-trending-header.component.ts new file mode 100644 index 000000000..125f14e33 --- /dev/null +++ b/client/src/app/+videos/video-list/trending/video-trending-header.component.ts @@ -0,0 +1,59 @@ +import { Component, Inject } from '@angular/core' +import { Router } from '@angular/router' +import { VideoListHeaderComponent } from '@app/shared/shared-video-miniature' +import { GlobalIconName } from '@app/shared/shared-icons' +import { VideoSortField } from '@shared/models' + +interface VideoTrendingHeaderItem { + label: string + iconName: GlobalIconName + value: VideoSortField + path: string + tooltip?: string +} + +@Component({ + selector: 'video-trending-title-page', + host: { 'class': 'title-page title-page-single' }, + styleUrls: [ './video-trending-header.component.scss' ], + templateUrl: './video-trending-header.component.html' +}) +export class VideoTrendingHeaderComponent extends VideoListHeaderComponent { + buttons: VideoTrendingHeaderItem[] + + constructor ( + @Inject('data') public data: any, + private router: Router + ) { + super(data) + + this.buttons = [ + { + label: $localize`:A variant of Trending videos based on the number of recent interactions:Hot`, + iconName: 'flame', + value: '-hot', + path: 'hot', + tooltip: $localize`Videos totalizing the most interactions for recent videos`, + }, + { + label: $localize`:Main variant of Trending videos based on number of recent views:Views`, + iconName: 'trending', + value: '-trending', + path: 'trending', + tooltip: $localize`Videos totalizing the most views during the last 24 hours`, + }, + { + label: $localize`:a variant of Trending videos based on the number of likes:Likes`, + iconName: 'like', + value: '-likes', + path: 'most-liked', + tooltip: $localize`Videos that have the most likes` + } + ] + } + + setSort () { + const path = this.buttons.find(b => b.value === this.data.model).path + this.router.navigate([ `/videos/${path}` ]) + } +} diff --git a/client/src/app/+videos/video-list/trending/video-trending.component.ts b/client/src/app/+videos/video-list/trending/video-trending.component.ts new file mode 100644 index 000000000..e77231586 --- /dev/null +++ b/client/src/app/+videos/video-list/trending/video-trending.component.ts @@ -0,0 +1,99 @@ +import { Component, ComponentFactoryResolver, Injector, OnDestroy, OnInit } from '@angular/core' +import { ActivatedRoute, Router } from '@angular/router' +import { AuthService, LocalStorageService, Notifier, ScreenService, ServerService, UserService } from '@app/core' +import { HooksService } from '@app/core/plugins/hooks.service' +import { immutableAssign } from '@app/helpers' +import { VideoService } from '@app/shared/shared-main' +import { AbstractVideoList } from '@app/shared/shared-video-miniature' +import { VideoSortField } from '@shared/models' +import { VideoTrendingHeaderComponent } from './video-trending-header.component' + +@Component({ + selector: 'my-videos-trending', + styleUrls: [ '../../../shared/shared-video-miniature/abstract-video-list.scss' ], + templateUrl: '../../../shared/shared-video-miniature/abstract-video-list.html' +}) +export class VideoTrendingComponent extends AbstractVideoList implements OnInit, OnDestroy { + HeaderComponent = VideoTrendingHeaderComponent + titlePage: string + defaultSort: VideoSortField = '-trending' + + useUserVideoPreferences = true + + constructor ( + protected router: Router, + protected serverService: ServerService, + protected route: ActivatedRoute, + protected notifier: Notifier, + protected authService: AuthService, + protected userService: UserService, + protected screenService: ScreenService, + protected storageService: LocalStorageService, + protected cfr: ComponentFactoryResolver, + private videoService: VideoService, + private hooks: HooksService + ) { + super() + + this.headerComponentInjector = this.getInjector() + } + + ngOnInit () { + super.ngOnInit() + + this.generateSyndicationList() + + this.serverService.getConfig().subscribe( + config => { + const trendingDays = config.trending.videos.intervalDays + + if (trendingDays === 1) { + this.titleTooltip = $localize`Trending videos are those totalizing the greatest number of views during the last 24 hours` + } else { + this.titleTooltip = $localize`Trending videos are those totalizing the greatest number of views during the last ${trendingDays} days` + } + + this.headerComponentInjector = this.getInjector() + this.setHeader() + }) + } + + ngOnDestroy () { + super.ngOnDestroy() + } + + getVideosObservable (page: number) { + const newPagination = immutableAssign(this.pagination, { currentPage: page }) + const params = { + videoPagination: newPagination, + sort: this.sort, + categoryOneOf: this.categoryOneOf, + languageOneOf: this.languageOneOf, + nsfwPolicy: this.nsfwPolicy, + skipCount: true + } + + return this.hooks.wrapObsFun( + this.videoService.getVideos.bind(this.videoService), + params, + 'common', + 'filter:api.trending-videos.videos.list.params', + 'filter:api.trending-videos.videos.list.result' + ) + } + + generateSyndicationList () { + this.syndicationItems = this.videoService.getVideoFeedUrls(this.sort, undefined, this.categoryOneOf) + } + + getInjector () { + return Injector.create({ + providers: [{ + provide: 'data', + useValue: { + model: this.defaultSort + } + }] + }) + } +} diff --git a/client/src/app/+videos/video-list/video-local.component.ts b/client/src/app/+videos/video-list/video-local.component.ts index 20dd61db9..af7eecff4 100644 --- a/client/src/app/+videos/video-list/video-local.component.ts +++ b/client/src/app/+videos/video-list/video-local.component.ts @@ -1,4 +1,4 @@ -import { Component, OnDestroy, OnInit } from '@angular/core' +import { Component, ComponentFactoryResolver, OnDestroy, OnInit } from '@angular/core' import { ActivatedRoute, Router } from '@angular/router' import { AuthService, LocalStorageService, Notifier, ScreenService, ServerService, UserService } from '@app/core' import { HooksService } from '@app/core/plugins/hooks.service' @@ -28,6 +28,7 @@ export class VideoLocalComponent extends AbstractVideoList implements OnInit, On protected userService: UserService, protected screenService: ScreenService, protected storageService: LocalStorageService, + protected cfr: ComponentFactoryResolver, private videoService: VideoService, private hooks: HooksService ) { diff --git a/client/src/app/+videos/video-list/video-most-liked.component.ts b/client/src/app/+videos/video-list/video-most-liked.component.ts deleted file mode 100644 index 93408d76b..000000000 --- a/client/src/app/+videos/video-list/video-most-liked.component.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { Component, OnInit } from '@angular/core' -import { ActivatedRoute, Router } from '@angular/router' -import { AuthService, LocalStorageService, Notifier, ScreenService, ServerService, UserService } from '@app/core' -import { HooksService } from '@app/core/plugins/hooks.service' -import { immutableAssign } from '@app/helpers' -import { VideoService } from '@app/shared/shared-main' -import { AbstractVideoList } from '@app/shared/shared-video-miniature' -import { VideoSortField } from '@shared/models' - -@Component({ - selector: 'my-videos-most-liked', - styleUrls: [ '../../shared/shared-video-miniature/abstract-video-list.scss' ], - templateUrl: '../../shared/shared-video-miniature/abstract-video-list.html' -}) -export class VideoMostLikedComponent extends AbstractVideoList implements OnInit { - titlePage: string - defaultSort: VideoSortField = '-likes' - - useUserVideoPreferences = true - - constructor ( - protected router: Router, - protected serverService: ServerService, - protected route: ActivatedRoute, - protected notifier: Notifier, - protected authService: AuthService, - protected userService: UserService, - protected screenService: ScreenService, - protected storageService: LocalStorageService, - private videoService: VideoService, - private hooks: HooksService - ) { - super() - } - - ngOnInit () { - super.ngOnInit() - - this.generateSyndicationList() - - this.titlePage = $localize`Most liked videos` - this.titleTooltip = $localize`Videos that have the most likes.` - } - - getVideosObservable (page: number) { - const newPagination = immutableAssign(this.pagination, { currentPage: page }) - const params = { - videoPagination: newPagination, - sort: this.sort, - categoryOneOf: this.categoryOneOf, - languageOneOf: this.languageOneOf, - nsfwPolicy: this.nsfwPolicy, - skipCount: true - } - - return this.hooks.wrapObsFun( - this.videoService.getVideos.bind(this.videoService), - params, - 'common', - 'filter:api.most-liked-videos.videos.list.params', - 'filter:api.most-liked-videos.videos.list.result' - ) - } - - generateSyndicationList () { - this.syndicationItems = this.videoService.getVideoFeedUrls(this.sort, undefined, this.categoryOneOf) - } -} diff --git a/client/src/app/+videos/video-list/video-recently-added.component.ts b/client/src/app/+videos/video-list/video-recently-added.component.ts index 34db6aabd..2f4908074 100644 --- a/client/src/app/+videos/video-list/video-recently-added.component.ts +++ b/client/src/app/+videos/video-list/video-recently-added.component.ts @@ -1,4 +1,4 @@ -import { Component, OnDestroy, OnInit } from '@angular/core' +import { Component, ComponentFactoryResolver, OnDestroy, OnInit } from '@angular/core' import { ActivatedRoute, Router } from '@angular/router' import { AuthService, LocalStorageService, Notifier, ScreenService, ServerService, UserService } from '@app/core' import { HooksService } from '@app/core/plugins/hooks.service' @@ -28,6 +28,7 @@ export class VideoRecentlyAddedComponent extends AbstractVideoList implements On protected userService: UserService, protected screenService: ScreenService, protected storageService: LocalStorageService, + protected cfr: ComponentFactoryResolver, private videoService: VideoService, private hooks: HooksService ) { diff --git a/client/src/app/+videos/video-list/video-trending.component.ts b/client/src/app/+videos/video-list/video-trending.component.ts deleted file mode 100644 index a188795d1..000000000 --- a/client/src/app/+videos/video-list/video-trending.component.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { Component, OnDestroy, OnInit } from '@angular/core' -import { ActivatedRoute, Router } from '@angular/router' -import { AuthService, LocalStorageService, Notifier, ScreenService, ServerService, UserService } from '@app/core' -import { HooksService } from '@app/core/plugins/hooks.service' -import { immutableAssign } from '@app/helpers' -import { VideoService } from '@app/shared/shared-main' -import { AbstractVideoList } from '@app/shared/shared-video-miniature' -import { VideoSortField } from '@shared/models' - -@Component({ - selector: 'my-videos-trending', - styleUrls: [ '../../shared/shared-video-miniature/abstract-video-list.scss' ], - templateUrl: '../../shared/shared-video-miniature/abstract-video-list.html' -}) -export class VideoTrendingComponent extends AbstractVideoList implements OnInit, OnDestroy { - titlePage: string - defaultSort: VideoSortField = '-trending' - - useUserVideoPreferences = true - - constructor ( - protected router: Router, - protected serverService: ServerService, - protected route: ActivatedRoute, - protected notifier: Notifier, - protected authService: AuthService, - protected userService: UserService, - protected screenService: ScreenService, - protected storageService: LocalStorageService, - private videoService: VideoService, - private hooks: HooksService - ) { - super() - } - - ngOnInit () { - super.ngOnInit() - - this.generateSyndicationList() - - this.serverService.getConfig().subscribe( - config => { - const trendingDays = config.trending.videos.intervalDays - - if (trendingDays === 1) { - this.titlePage = $localize`Trending for the last 24 hours` - this.titleTooltip = $localize`Trending videos are those totalizing the greatest number of views during the last 24 hours` - return - } - - this.titlePage = $localize`Trending for the last ${trendingDays} days` - this.titleTooltip = $localize`Trending videos are those totalizing the greatest number of views during the last ${trendingDays} days` - }) - } - - ngOnDestroy () { - super.ngOnDestroy() - } - - getVideosObservable (page: number) { - const newPagination = immutableAssign(this.pagination, { currentPage: page }) - const params = { - videoPagination: newPagination, - sort: this.sort, - categoryOneOf: this.categoryOneOf, - languageOneOf: this.languageOneOf, - nsfwPolicy: this.nsfwPolicy, - skipCount: true - } - - return this.hooks.wrapObsFun( - this.videoService.getVideos.bind(this.videoService), - params, - 'common', - 'filter:api.trending-videos.videos.list.params', - 'filter:api.trending-videos.videos.list.result' - ) - } - - generateSyndicationList () { - this.syndicationItems = this.videoService.getVideoFeedUrls(this.sort, undefined, this.categoryOneOf) - } -} diff --git a/client/src/app/+videos/video-list/video-user-subscriptions.component.ts b/client/src/app/+videos/video-list/video-user-subscriptions.component.ts index 62d862ec9..e352a2b2c 100644 --- a/client/src/app/+videos/video-list/video-user-subscriptions.component.ts +++ b/client/src/app/+videos/video-list/video-user-subscriptions.component.ts @@ -1,6 +1,6 @@ import { switchMap } from 'rxjs/operators' -import { Component, OnDestroy, OnInit } from '@angular/core' +import { Component, ComponentFactoryResolver, OnDestroy, OnInit } from '@angular/core' import { ActivatedRoute, Router } from '@angular/router' import { AuthService, LocalStorageService, Notifier, ScopedTokensService, ScreenService, ServerService, UserService } from '@app/core' import { HooksService } from '@app/core/plugins/hooks.service' @@ -33,6 +33,7 @@ export class VideoUserSubscriptionsComponent extends AbstractVideoList implement protected screenService: ScreenService, protected storageService: LocalStorageService, private userSubscription: UserSubscriptionService, + protected cfr: ComponentFactoryResolver, private hooks: HooksService, private videoService: VideoService, private scopedTokensService: ScopedTokensService @@ -102,7 +103,8 @@ export class VideoUserSubscriptionsComponent extends AbstractVideoList implement } generateSyndicationList () { - // not implemented yet + /* method disabled: the view provides its own */ + throw new Error('Method not implemented.') } activateCopiedMessage () { -- cgit v1.2.3