aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2020-03-11 16:41:38 +0100
committerChocobozzz <me@florianbigard.com>2020-03-11 16:45:09 +0100
commit111fdc267b36201cf1be1fdf91017005102b4a5e (patch)
tree5ea98f766cc74de3434a855e998e763324e9da72 /client/src
parent764a965778ac89e027fd05dd35697c6763e0dc18 (diff)
downloadPeerTube-111fdc267b36201cf1be1fdf91017005102b4a5e.tar.gz
PeerTube-111fdc267b36201cf1be1fdf91017005102b4a5e.tar.zst
PeerTube-111fdc267b36201cf1be1fdf91017005102b4a5e.zip
Handle overview pagination in client
Diffstat (limited to 'client/src')
-rw-r--r--client/src/app/core/server/server.service.ts24
-rw-r--r--client/src/app/menu/language-chooser.component.ts1
-rw-r--r--client/src/app/menu/menu.component.ts50
-rw-r--r--client/src/app/shared/overview/overview.service.ts9
-rw-r--r--client/src/app/videos/video-list/video-overview.component.html55
-rw-r--r--client/src/app/videos/video-list/video-overview.component.ts67
6 files changed, 135 insertions, 71 deletions
diff --git a/client/src/app/core/server/server.service.ts b/client/src/app/core/server/server.service.ts
index e015d0e14..da7832b32 100644
--- a/client/src/app/core/server/server.service.ts
+++ b/client/src/app/core/server/server.service.ts
@@ -263,17 +263,19 @@ export class ServerService {
263 .pipe(map(data => ({ data, translations }))) 263 .pipe(map(data => ({ data, translations })))
264 }), 264 }),
265 map(({ data, translations }) => { 265 map(({ data, translations }) => {
266 const hashToPopulate: VideoConstant<T>[] = [] 266 const hashToPopulate: VideoConstant<T>[] = Object.keys(data)
267 267 .map(dataKey => {
268 Object.keys(data) 268 const label = data[ dataKey ]
269 .forEach(dataKey => { 269
270 const label = data[ dataKey ] 270 const id = attributeName === 'languages'
271 271 ? dataKey as T
272 hashToPopulate.push({ 272 : parseInt(dataKey, 10) as T
273 id: (attributeName === 'languages' ? dataKey : parseInt(dataKey, 10)) as T, 273
274 label: peertubeTranslate(label, translations) 274 return {
275 }) 275 id,
276 }) 276 label: peertubeTranslate(label, translations)
277 }
278 })
277 279
278 if (sort === true) sortBy(hashToPopulate, 'label') 280 if (sort === true) sortBy(hashToPopulate, 'label')
279 281
diff --git a/client/src/app/menu/language-chooser.component.ts b/client/src/app/menu/language-chooser.component.ts
index fb74cdf19..9bc934ad4 100644
--- a/client/src/app/menu/language-chooser.component.ts
+++ b/client/src/app/menu/language-chooser.component.ts
@@ -36,6 +36,7 @@ export class LanguageChooserComponent {
36 getCurrentLanguage () { 36 getCurrentLanguage () {
37 const english = 'English' 37 const english = 'English'
38 const locale = isOnDevLocale() ? getDevLocale() : getCompleteLocale(this.localeId) 38 const locale = isOnDevLocale() ? getDevLocale() : getCompleteLocale(this.localeId)
39
39 if (locale) return I18N_LOCALES[locale] || english 40 if (locale) return I18N_LOCALES[locale] || english
40 return english 41 return english
41 } 42 }
diff --git a/client/src/app/menu/menu.component.ts b/client/src/app/menu/menu.component.ts
index ce209457c..37702e975 100644
--- a/client/src/app/menu/menu.component.ts
+++ b/client/src/app/menu/menu.component.ts
@@ -23,8 +23,10 @@ export class MenuComponent implements OnInit {
23 23
24 userHasAdminAccess = false 24 userHasAdminAccess = false
25 helpVisible = false 25 helpVisible = false
26 languages: VideoConstant<string>[] = []
27 26
27 videoLanguages: string[] = []
28
29 private languages: VideoConstant<string>[] = []
28 private serverConfig: ServerConfig 30 private serverConfig: ServerConfig
29 private routesPerRight: { [ role in UserRight ]?: string } = { 31 private routesPerRight: { [ role in UserRight ]?: string } = {
30 [UserRight.MANAGE_USERS]: '/admin/users', 32 [UserRight.MANAGE_USERS]: '/admin/users',
@@ -71,30 +73,32 @@ export class MenuComponent implements OnInit {
71 } 73 }
72 ) 74 )
73 75
74 this.hotkeysService.cheatSheetToggle.subscribe(isOpen => this.helpVisible = isOpen) 76 this.hotkeysService.cheatSheetToggle
77 .subscribe(isOpen => this.helpVisible = isOpen)
78
79 this.serverService.getVideoLanguages()
80 .subscribe(languages => {
81 this.languages = languages
75 82
76 this.serverService.getVideoLanguages().subscribe(languages => this.languages = languages) 83 this.authService.userInformationLoaded
84 .subscribe(() => this.buildUserLanguages())
85 })
77 } 86 }
78 87
79 get language () { 88 get language () {
80 return this.languageChooserModal.getCurrentLanguage() 89 return this.languageChooserModal.getCurrentLanguage()
81 } 90 }
82 91
83 get videoLanguages (): string[] {
84 if (!this.user) return
85 if (!this.user.videoLanguages) return [this.i18n('any language')]
86 return this.user.videoLanguages
87 .map(locale => this.langForLocale(locale))
88 .map(value => value === undefined ? '?' : value)
89 }
90
91 get nsfwPolicy () { 92 get nsfwPolicy () {
92 if (!this.user) return 93 if (!this.user) return
94
93 switch (this.user.nsfwPolicy) { 95 switch (this.user.nsfwPolicy) {
94 case 'do_not_list': 96 case 'do_not_list':
95 return this.i18n('hide') 97 return this.i18n('hide')
98
96 case 'blur': 99 case 'blur':
97 return this.i18n('blur') 100 return this.i18n('blur')
101
98 case 'display': 102 case 'display':
99 return this.i18n('display') 103 return this.i18n('display')
100 } 104 }
@@ -156,13 +160,29 @@ export class MenuComponent implements OnInit {
156 toggleUseP2P () { 160 toggleUseP2P () {
157 if (!this.user) return 161 if (!this.user) return
158 this.user.webTorrentEnabled = !this.user.webTorrentEnabled 162 this.user.webTorrentEnabled = !this.user.webTorrentEnabled
159 this.userService.updateMyProfile({ 163
160 webTorrentEnabled: this.user.webTorrentEnabled 164 this.userService.updateMyProfile({ webTorrentEnabled: this.user.webTorrentEnabled })
161 }).subscribe(() => this.authService.refreshUserInformation()) 165 .subscribe(() => this.authService.refreshUserInformation())
162 } 166 }
163 167
164 langForLocale (localeId: string) { 168 langForLocale (localeId: string) {
165 return this.languages.find(lang => lang.id = localeId).label 169 return this.languages.find(lang => lang.id === localeId).label
170 }
171
172 private buildUserLanguages () {
173 if (!this.user) {
174 this.videoLanguages = []
175 return
176 }
177
178 if (!this.user.videoLanguages) {
179 this.videoLanguages = [ this.i18n('any language') ]
180 return
181 }
182
183 this.videoLanguages = this.user.videoLanguages
184 .map(locale => this.langForLocale(locale))
185 .map(value => value === undefined ? '?' : value)
166 } 186 }
167 187
168 private computeIsUserHasAdminAccess () { 188 private computeIsUserHasAdminAccess () {
diff --git a/client/src/app/shared/overview/overview.service.ts b/client/src/app/shared/overview/overview.service.ts
index 79cb781f7..6d8af8052 100644
--- a/client/src/app/shared/overview/overview.service.ts
+++ b/client/src/app/shared/overview/overview.service.ts
@@ -1,5 +1,5 @@
1import { catchError, map, switchMap, tap } from 'rxjs/operators' 1import { catchError, map, switchMap, tap } from 'rxjs/operators'
2import { HttpClient } from '@angular/common/http' 2import { HttpClient, HttpParams } from '@angular/common/http'
3import { Injectable } from '@angular/core' 3import { Injectable } from '@angular/core'
4import { forkJoin, Observable, of } from 'rxjs' 4import { forkJoin, Observable, of } from 'rxjs'
5import { VideosOverview as VideosOverviewServer, peertubeTranslate } from '../../../../../shared/models' 5import { VideosOverview as VideosOverviewServer, peertubeTranslate } from '../../../../../shared/models'
@@ -21,9 +21,12 @@ export class OverviewService {
21 private serverService: ServerService 21 private serverService: ServerService
22 ) {} 22 ) {}
23 23
24 getVideosOverview (): Observable<VideosOverview> { 24 getVideosOverview (page: number): Observable<VideosOverview> {
25 let params = new HttpParams()
26 params = params.append('page', page + '')
27
25 return this.authHttp 28 return this.authHttp
26 .get<VideosOverviewServer>(OverviewService.BASE_OVERVIEW_URL + 'videos') 29 .get<VideosOverviewServer>(OverviewService.BASE_OVERVIEW_URL + 'videos', { params })
27 .pipe( 30 .pipe(
28 switchMap(serverVideosOverview => this.updateVideosOverview(serverVideosOverview)), 31 switchMap(serverVideosOverview => this.updateVideosOverview(serverVideosOverview)),
29 catchError(err => this.restExtractor.handleError(err)) 32 catchError(err => this.restExtractor.handleError(err))
diff --git a/client/src/app/videos/video-list/video-overview.component.html b/client/src/app/videos/video-list/video-overview.component.html
index 5fe1f5c80..84999cfb2 100644
--- a/client/src/app/videos/video-list/video-overview.component.html
+++ b/client/src/app/videos/video-list/video-overview.component.html
@@ -2,35 +2,44 @@
2 2
3 <div class="no-results" i18n *ngIf="notResults">No results.</div> 3 <div class="no-results" i18n *ngIf="notResults">No results.</div>
4 4
5 <div class="section" *ngFor="let object of overview.categories"> 5 <div
6 <div class="section-title"> 6 myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [autoInit]="true" [dataObservable]="onDataSubject.asObservable()"
7 <a routerLink="/search" [queryParams]="{ categoryOneOf: [ object.category.id ] }">{{ object.category.label }}</a> 7 >
8 </div> 8 <ng-container *ngFor="let overview of overviews">
9 9
10 <my-video-miniature *ngFor="let video of buildVideos(object.videos)" [video]="video" [user]="user" [displayVideoActions]="false"> 10 <div class="section" *ngFor="let object of overview.categories">
11 </my-video-miniature> 11 <div class="section-title">
12 </div> 12 <a routerLink="/search" [queryParams]="{ categoryOneOf: [ object.category.id ] }">{{ object.category.label }}</a>
13 </div>
13 14
14 <div class="section" *ngFor="let object of overview.tags"> 15 <my-video-miniature *ngFor="let video of buildVideos(object.videos)" [video]="video" [user]="user" [displayVideoActions]="false">
15 <div class="section-title"> 16 </my-video-miniature>
16 <a routerLink="/search" [queryParams]="{ tagsOneOf: [ object.tag ] }">#{{ object.tag }}</a> 17 </div>
17 </div>
18 18
19 <my-video-miniature *ngFor="let video of buildVideos(object.videos)" [video]="video" [user]="user" [displayVideoActions]="false"> 19 <div class="section" *ngFor="let object of overview.tags">
20 </my-video-miniature> 20 <div class="section-title">
21 </div> 21 <a routerLink="/search" [queryParams]="{ tagsOneOf: [ object.tag ] }">#{{ object.tag }}</a>
22 </div>
23
24 <my-video-miniature *ngFor="let video of buildVideos(object.videos)" [video]="video" [user]="user" [displayVideoActions]="false">
25 </my-video-miniature>
26 </div>
27
28 <div class="section channel" *ngFor="let object of overview.channels">
29 <div class="section-title">
30 <a [routerLink]="[ '/video-channels', buildVideoChannelBy(object) ]">
31 <img [src]="buildVideoChannelAvatarUrl(object)" alt="Avatar" />
32
33 <div>{{ object.channel.displayName }}</div>
34 </a>
35 </div>
22 36
23 <div class="section channel" *ngFor="let object of overview.channels"> 37 <my-video-miniature *ngFor="let video of buildVideos(object.videos)" [video]="video" [user]="user" [displayVideoActions]="false">
24 <div class="section-title"> 38 </my-video-miniature>
25 <a [routerLink]="[ '/video-channels', buildVideoChannelBy(object) ]"> 39 </div>
26 <img [src]="buildVideoChannelAvatarUrl(object)" alt="Avatar" />
27 40
28 <div>{{ object.channel.displayName }}</div> 41 </ng-container>
29 </a>
30 </div>
31 42
32 <my-video-miniature *ngFor="let video of buildVideos(object.videos)" [video]="video" [user]="user" [displayVideoActions]="false">
33 </my-video-miniature>
34 </div> 43 </div>
35 44
36</div> 45</div>
diff --git a/client/src/app/videos/video-list/video-overview.component.ts b/client/src/app/videos/video-list/video-overview.component.ts
index 4fee92d54..101073949 100644
--- a/client/src/app/videos/video-list/video-overview.component.ts
+++ b/client/src/app/videos/video-list/video-overview.component.ts
@@ -5,6 +5,7 @@ import { VideosOverview } from '@app/shared/overview/videos-overview.model'
5import { OverviewService } from '@app/shared/overview' 5import { OverviewService } from '@app/shared/overview'
6import { Video } from '@app/shared/video/video.model' 6import { Video } from '@app/shared/video/video.model'
7import { ScreenService } from '@app/shared/misc/screen.service' 7import { ScreenService } from '@app/shared/misc/screen.service'
8import { Subject } from 'rxjs'
8 9
9@Component({ 10@Component({
10 selector: 'my-video-overview', 11 selector: 'my-video-overview',
@@ -12,13 +13,17 @@ import { ScreenService } from '@app/shared/misc/screen.service'
12 styleUrls: [ './video-overview.component.scss' ] 13 styleUrls: [ './video-overview.component.scss' ]
13}) 14})
14export class VideoOverviewComponent implements OnInit { 15export class VideoOverviewComponent implements OnInit {
15 overview: VideosOverview = { 16 onDataSubject = new Subject<any>()
16 categories: [], 17
17 channels: [], 18 overviews: VideosOverview[] = []
18 tags: []
19 }
20 notResults = false 19 notResults = false
21 20
21 private loaded = false
22 private currentPage = 1
23 private maxPage = 20
24 private lastWasEmpty = false
25 private isLoading = false
26
22 constructor ( 27 constructor (
23 private i18n: I18n, 28 private i18n: I18n,
24 private notifier: Notifier, 29 private notifier: Notifier,
@@ -32,20 +37,7 @@ export class VideoOverviewComponent implements OnInit {
32 } 37 }
33 38
34 ngOnInit () { 39 ngOnInit () {
35 this.overviewService.getVideosOverview() 40 this.loadMoreResults()
36 .subscribe(
37 overview => {
38 this.overview = overview
39
40 if (
41 this.overview.categories.length === 0 &&
42 this.overview.channels.length === 0 &&
43 this.overview.tags.length === 0
44 ) this.notResults = true
45 },
46
47 err => this.notifier.error(err.message)
48 )
49 } 41 }
50 42
51 buildVideoChannelBy (object: { videos: Video[] }) { 43 buildVideoChannelBy (object: { videos: Video[] }) {
@@ -61,4 +53,41 @@ export class VideoOverviewComponent implements OnInit {
61 53
62 return videos.slice(0, numberOfVideos * 2) 54 return videos.slice(0, numberOfVideos * 2)
63 } 55 }
56
57 onNearOfBottom () {
58 if (this.currentPage >= this.maxPage) return
59 if (this.lastWasEmpty) return
60 if (this.isLoading) return
61
62 this.currentPage++
63 this.loadMoreResults()
64 }
65
66 private loadMoreResults () {
67 this.isLoading = true
68
69 this.overviewService.getVideosOverview(this.currentPage)
70 .subscribe(
71 overview => {
72 this.isLoading = false
73
74 if (overview.tags.length === 0 && overview.channels.length === 0 && overview.categories.length === 0) {
75 this.lastWasEmpty = true
76 if (this.loaded === false) this.notResults = true
77
78 return
79 }
80
81 this.loaded = true
82 this.onDataSubject.next(overview)
83
84 this.overviews.push(overview)
85 },
86
87 err => {
88 this.notifier.error(err.message)
89 this.isLoading = false
90 }
91 )
92 }
64} 93}