aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/videos/video-list/overview
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2020-06-23 14:10:17 +0200
committerChocobozzz <chocobozzz@cpy.re>2020-06-23 16:00:49 +0200
commit67ed6552b831df66713bac9e672738796128d33f (patch)
tree59c97d41e0b49d75a90aa3de987968ab9b1ff447 /client/src/app/videos/video-list/overview
parent0c4bacbff53bc732f5a2677d62a6ead7752e2405 (diff)
downloadPeerTube-67ed6552b831df66713bac9e672738796128d33f.tar.gz
PeerTube-67ed6552b831df66713bac9e672738796128d33f.tar.zst
PeerTube-67ed6552b831df66713bac9e672738796128d33f.zip
Reorganize client shared modules
Diffstat (limited to 'client/src/app/videos/video-list/overview')
-rw-r--r--client/src/app/videos/video-list/overview/index.ts3
-rw-r--r--client/src/app/videos/video-list/overview/overview.service.ts78
-rw-r--r--client/src/app/videos/video-list/overview/video-overview.component.html52
-rw-r--r--client/src/app/videos/video-list/overview/video-overview.component.scss16
-rw-r--r--client/src/app/videos/video-list/overview/video-overview.component.ts94
-rw-r--r--client/src/app/videos/video-list/overview/videos-overview.model.ts20
6 files changed, 263 insertions, 0 deletions
diff --git a/client/src/app/videos/video-list/overview/index.ts b/client/src/app/videos/video-list/overview/index.ts
new file mode 100644
index 000000000..e6cfa4802
--- /dev/null
+++ b/client/src/app/videos/video-list/overview/index.ts
@@ -0,0 +1,3 @@
1export * from './overview.service'
2export * from './video-overview.component'
3export * from './videos-overview.model'
diff --git a/client/src/app/videos/video-list/overview/overview.service.ts b/client/src/app/videos/video-list/overview/overview.service.ts
new file mode 100644
index 000000000..4458454d5
--- /dev/null
+++ b/client/src/app/videos/video-list/overview/overview.service.ts
@@ -0,0 +1,78 @@
1import { forkJoin, Observable, of } from 'rxjs'
2import { catchError, map, switchMap, tap } from 'rxjs/operators'
3import { HttpClient, HttpParams } from '@angular/common/http'
4import { Injectable } from '@angular/core'
5import { RestExtractor, ServerService } from '@app/core'
6import { immutableAssign } from '@app/helpers'
7import { VideoService } from '@app/shared/shared-main'
8import { peertubeTranslate, VideosOverview as VideosOverviewServer } from '@shared/models'
9import { environment } from '../../../../environments/environment'
10import { VideosOverview } from './videos-overview.model'
11
12@Injectable()
13export class OverviewService {
14 static BASE_OVERVIEW_URL = environment.apiUrl + '/api/v1/overviews/'
15
16 constructor (
17 private authHttp: HttpClient,
18 private restExtractor: RestExtractor,
19 private videosService: VideoService,
20 private serverService: ServerService
21 ) {}
22
23 getVideosOverview (page: number): Observable<VideosOverview> {
24 let params = new HttpParams()
25 params = params.append('page', page + '')
26
27 return this.authHttp
28 .get<VideosOverviewServer>(OverviewService.BASE_OVERVIEW_URL + 'videos', { params })
29 .pipe(
30 switchMap(serverVideosOverview => this.updateVideosOverview(serverVideosOverview)),
31 catchError(err => this.restExtractor.handleError(err))
32 )
33 }
34
35 private updateVideosOverview (serverVideosOverview: VideosOverviewServer): Observable<VideosOverview> {
36 const observables: Observable<any>[] = []
37 const videosOverviewResult: VideosOverview = {
38 tags: [],
39 categories: [],
40 channels: []
41 }
42
43 // Build videos objects
44 for (const key of Object.keys(serverVideosOverview)) {
45 for (const object of serverVideosOverview[ key ]) {
46 observables.push(
47 of(object.videos)
48 .pipe(
49 switchMap(videos => this.videosService.extractVideos({ total: 0, data: videos })),
50 map(result => result.data),
51 tap(videos => {
52 videosOverviewResult[key].push(immutableAssign(object, { videos }))
53 })
54 )
55 )
56 }
57 }
58
59 if (observables.length === 0) return of(videosOverviewResult)
60
61 return forkJoin(observables)
62 .pipe(
63 // Translate categories
64 switchMap(() => {
65 return this.serverService.getServerLocale()
66 .pipe(
67 tap(translations => {
68 for (const c of videosOverviewResult.categories) {
69 c.category.label = peertubeTranslate(c.category.label, translations)
70 }
71 })
72 )
73 }),
74 map(() => videosOverviewResult)
75 )
76 }
77
78}
diff --git a/client/src/app/videos/video-list/overview/video-overview.component.html b/client/src/app/videos/video-list/overview/video-overview.component.html
new file mode 100644
index 000000000..ca986c634
--- /dev/null
+++ b/client/src/app/videos/video-list/overview/video-overview.component.html
@@ -0,0 +1,52 @@
1<h1 class="sr-only" i18n>Discover</h1>
2<div class="margin-content">
3
4 <div class="no-results" i18n *ngIf="notResults">No results.</div>
5
6 <div
7 myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [autoInit]="true" [dataObservable]="onDataSubject.asObservable()"
8 >
9 <ng-container *ngFor="let overview of overviews">
10
11 <div class="section videos" *ngFor="let object of overview.categories">
12 <h1 class="section-title">
13 <a routerLink="/search" [queryParams]="{ categoryOneOf: [ object.category.id ] }">{{ object.category.label }}</a>
14 </h1>
15
16 <div class="video-wrapper" *ngFor="let video of buildVideos(object.videos)">
17 <my-video-miniature [video]="video" [fitWidth]="true" [user]="userMiniature" [displayVideoActions]="true">
18 </my-video-miniature>
19 </div>
20 </div>
21
22 <div class="section videos" *ngFor="let object of overview.tags">
23 <h2 class="section-title">
24 <a routerLink="/search" [queryParams]="{ tagsOneOf: [ object.tag ] }">#{{ object.tag }}</a>
25 </h2>
26
27 <div class="video-wrapper" *ngFor="let video of buildVideos(object.videos)">
28 <my-video-miniature [video]="video" [fitWidth]="true" [user]="userMiniature" [displayVideoActions]="true">
29 </my-video-miniature>
30 </div>
31 </div>
32
33 <div class="section channel videos" *ngFor="let object of overview.channels">
34 <div class="section-title">
35 <a [routerLink]="[ '/video-channels', buildVideoChannelBy(object) ]">
36 <img [src]="buildVideoChannelAvatarUrl(object)" alt="Avatar" />
37
38 <h2 class="section-title">{{ object.channel.displayName }}</h2>
39 </a>
40 </div>
41
42 <div class="video-wrapper" *ngFor="let video of buildVideos(object.videos)">
43 <my-video-miniature [video]="video" [fitWidth]="true" [user]="userMiniature" [displayVideoActions]="true">
44 </my-video-miniature>
45 </div>
46 </div>
47
48 </ng-container>
49
50 </div>
51
52</div>
diff --git a/client/src/app/videos/video-list/overview/video-overview.component.scss b/client/src/app/videos/video-list/overview/video-overview.component.scss
new file mode 100644
index 000000000..c1d10188a
--- /dev/null
+++ b/client/src/app/videos/video-list/overview/video-overview.component.scss
@@ -0,0 +1,16 @@
1@import '_variables';
2@import '_mixins';
3@import '_miniature';
4
5.section-title {
6 // make the element span a full grid row within .videos grid
7 grid-column: 1 / -1;
8}
9
10.margin-content {
11 @include fluid-videos-miniature-layout;
12}
13
14.section {
15 @include miniature-rows;
16}
diff --git a/client/src/app/videos/video-list/overview/video-overview.component.ts b/client/src/app/videos/video-list/overview/video-overview.component.ts
new file mode 100644
index 000000000..b3be1d7b5
--- /dev/null
+++ b/client/src/app/videos/video-list/overview/video-overview.component.ts
@@ -0,0 +1,94 @@
1import { Subject } from 'rxjs'
2import { Component, OnInit } from '@angular/core'
3import { Notifier, ScreenService, User, UserService } from '@app/core'
4import { Video } from '@app/shared/shared-main'
5import { OverviewService } from './overview.service'
6import { VideosOverview } from './videos-overview.model'
7
8@Component({
9 selector: 'my-video-overview',
10 templateUrl: './video-overview.component.html',
11 styleUrls: [ './video-overview.component.scss' ]
12})
13export class VideoOverviewComponent implements OnInit {
14 onDataSubject = new Subject<any>()
15
16 overviews: VideosOverview[] = []
17 notResults = false
18
19 userMiniature: User
20
21 private loaded = false
22 private currentPage = 1
23 private maxPage = 20
24 private lastWasEmpty = false
25 private isLoading = false
26
27 constructor (
28 private notifier: Notifier,
29 private userService: UserService,
30 private overviewService: OverviewService,
31 private screenService: ScreenService
32 ) { }
33
34 ngOnInit () {
35 this.loadMoreResults()
36
37 this.userService.getAnonymousOrLoggedUser()
38 .subscribe(user => this.userMiniature = user)
39
40 this.userService.listenAnonymousUpdate()
41 .subscribe(user => this.userMiniature = user)
42 }
43
44 buildVideoChannelBy (object: { videos: Video[] }) {
45 return object.videos[0].byVideoChannel
46 }
47
48 buildVideoChannelAvatarUrl (object: { videos: Video[] }) {
49 return object.videos[0].videoChannelAvatarUrl
50 }
51
52 buildVideos (videos: Video[]) {
53 const numberOfVideos = this.screenService.getNumberOfAvailableMiniatures()
54
55 return videos.slice(0, numberOfVideos * 2)
56 }
57
58 onNearOfBottom () {
59 if (this.currentPage >= this.maxPage) return
60 if (this.lastWasEmpty) return
61 if (this.isLoading) return
62
63 this.currentPage++
64 this.loadMoreResults()
65 }
66
67 private loadMoreResults () {
68 this.isLoading = true
69
70 this.overviewService.getVideosOverview(this.currentPage)
71 .subscribe(
72 overview => {
73 this.isLoading = false
74
75 if (overview.tags.length === 0 && overview.channels.length === 0 && overview.categories.length === 0) {
76 this.lastWasEmpty = true
77 if (this.loaded === false) this.notResults = true
78
79 return
80 }
81
82 this.loaded = true
83 this.onDataSubject.next(overview)
84
85 this.overviews.push(overview)
86 },
87
88 err => {
89 this.notifier.error(err.message)
90 this.isLoading = false
91 }
92 )
93 }
94}
diff --git a/client/src/app/videos/video-list/overview/videos-overview.model.ts b/client/src/app/videos/video-list/overview/videos-overview.model.ts
new file mode 100644
index 000000000..6765ad9b7
--- /dev/null
+++ b/client/src/app/videos/video-list/overview/videos-overview.model.ts
@@ -0,0 +1,20 @@
1import { Video } from '@app/shared/shared-main'
2import { VideoChannelSummary, VideoConstant, VideosOverview as VideosOverviewServer } from '@shared/models'
3
4export class VideosOverview implements VideosOverviewServer {
5 channels: {
6 channel: VideoChannelSummary
7 videos: Video[]
8 }[]
9
10 categories: {
11 category: VideoConstant<number>
12 videos: Video[]
13 }[]
14
15 tags: {
16 tag: string
17 videos: Video[]
18 }[]
19 [key: string]: any
20}