Handle overview pagination in client

pull/2567/head
Chocobozzz 2020-03-11 16:41:38 +01:00
parent 764a965778
commit 111fdc267b
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
6 changed files with 133 additions and 69 deletions

View File

@ -263,17 +263,19 @@ export class ServerService {
.pipe(map(data => ({ data, translations })))
}),
map(({ data, translations }) => {
const hashToPopulate: VideoConstant<T>[] = []
const hashToPopulate: VideoConstant<T>[] = Object.keys(data)
.map(dataKey => {
const label = data[ dataKey ]
Object.keys(data)
.forEach(dataKey => {
const label = data[ dataKey ]
const id = attributeName === 'languages'
? dataKey as T
: parseInt(dataKey, 10) as T
hashToPopulate.push({
id: (attributeName === 'languages' ? dataKey : parseInt(dataKey, 10)) as T,
label: peertubeTranslate(label, translations)
})
})
return {
id,
label: peertubeTranslate(label, translations)
}
})
if (sort === true) sortBy(hashToPopulate, 'label')

View File

@ -36,6 +36,7 @@ export class LanguageChooserComponent {
getCurrentLanguage () {
const english = 'English'
const locale = isOnDevLocale() ? getDevLocale() : getCompleteLocale(this.localeId)
if (locale) return I18N_LOCALES[locale] || english
return english
}

View File

@ -23,8 +23,10 @@ export class MenuComponent implements OnInit {
userHasAdminAccess = false
helpVisible = false
languages: VideoConstant<string>[] = []
videoLanguages: string[] = []
private languages: VideoConstant<string>[] = []
private serverConfig: ServerConfig
private routesPerRight: { [ role in UserRight ]?: string } = {
[UserRight.MANAGE_USERS]: '/admin/users',
@ -71,30 +73,32 @@ export class MenuComponent implements OnInit {
}
)
this.hotkeysService.cheatSheetToggle.subscribe(isOpen => this.helpVisible = isOpen)
this.hotkeysService.cheatSheetToggle
.subscribe(isOpen => this.helpVisible = isOpen)
this.serverService.getVideoLanguages().subscribe(languages => this.languages = languages)
this.serverService.getVideoLanguages()
.subscribe(languages => {
this.languages = languages
this.authService.userInformationLoaded
.subscribe(() => this.buildUserLanguages())
})
}
get language () {
return this.languageChooserModal.getCurrentLanguage()
}
get videoLanguages (): string[] {
if (!this.user) return
if (!this.user.videoLanguages) return [this.i18n('any language')]
return this.user.videoLanguages
.map(locale => this.langForLocale(locale))
.map(value => value === undefined ? '?' : value)
}
get nsfwPolicy () {
if (!this.user) return
switch (this.user.nsfwPolicy) {
case 'do_not_list':
return this.i18n('hide')
case 'blur':
return this.i18n('blur')
case 'display':
return this.i18n('display')
}
@ -156,13 +160,29 @@ export class MenuComponent implements OnInit {
toggleUseP2P () {
if (!this.user) return
this.user.webTorrentEnabled = !this.user.webTorrentEnabled
this.userService.updateMyProfile({
webTorrentEnabled: this.user.webTorrentEnabled
}).subscribe(() => this.authService.refreshUserInformation())
this.userService.updateMyProfile({ webTorrentEnabled: this.user.webTorrentEnabled })
.subscribe(() => this.authService.refreshUserInformation())
}
langForLocale (localeId: string) {
return this.languages.find(lang => lang.id = localeId).label
return this.languages.find(lang => lang.id === localeId).label
}
private buildUserLanguages () {
if (!this.user) {
this.videoLanguages = []
return
}
if (!this.user.videoLanguages) {
this.videoLanguages = [ this.i18n('any language') ]
return
}
this.videoLanguages = this.user.videoLanguages
.map(locale => this.langForLocale(locale))
.map(value => value === undefined ? '?' : value)
}
private computeIsUserHasAdminAccess () {

View File

@ -1,5 +1,5 @@
import { catchError, map, switchMap, tap } from 'rxjs/operators'
import { HttpClient } from '@angular/common/http'
import { HttpClient, HttpParams } from '@angular/common/http'
import { Injectable } from '@angular/core'
import { forkJoin, Observable, of } from 'rxjs'
import { VideosOverview as VideosOverviewServer, peertubeTranslate } from '../../../../../shared/models'
@ -21,9 +21,12 @@ export class OverviewService {
private serverService: ServerService
) {}
getVideosOverview (): Observable<VideosOverview> {
getVideosOverview (page: number): Observable<VideosOverview> {
let params = new HttpParams()
params = params.append('page', page + '')
return this.authHttp
.get<VideosOverviewServer>(OverviewService.BASE_OVERVIEW_URL + 'videos')
.get<VideosOverviewServer>(OverviewService.BASE_OVERVIEW_URL + 'videos', { params })
.pipe(
switchMap(serverVideosOverview => this.updateVideosOverview(serverVideosOverview)),
catchError(err => this.restExtractor.handleError(err))

View File

@ -2,35 +2,44 @@
<div class="no-results" i18n *ngIf="notResults">No results.</div>
<div class="section" *ngFor="let object of overview.categories">
<div class="section-title">
<a routerLink="/search" [queryParams]="{ categoryOneOf: [ object.category.id ] }">{{ object.category.label }}</a>
</div>
<div
myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [autoInit]="true" [dataObservable]="onDataSubject.asObservable()"
>
<ng-container *ngFor="let overview of overviews">
<my-video-miniature *ngFor="let video of buildVideos(object.videos)" [video]="video" [user]="user" [displayVideoActions]="false">
</my-video-miniature>
</div>
<div class="section" *ngFor="let object of overview.categories">
<div class="section-title">
<a routerLink="/search" [queryParams]="{ categoryOneOf: [ object.category.id ] }">{{ object.category.label }}</a>
</div>
<div class="section" *ngFor="let object of overview.tags">
<div class="section-title">
<a routerLink="/search" [queryParams]="{ tagsOneOf: [ object.tag ] }">#{{ object.tag }}</a>
</div>
<my-video-miniature *ngFor="let video of buildVideos(object.videos)" [video]="video" [user]="user" [displayVideoActions]="false">
</my-video-miniature>
</div>
<my-video-miniature *ngFor="let video of buildVideos(object.videos)" [video]="video" [user]="user" [displayVideoActions]="false">
</my-video-miniature>
</div>
<div class="section" *ngFor="let object of overview.tags">
<div class="section-title">
<a routerLink="/search" [queryParams]="{ tagsOneOf: [ object.tag ] }">#{{ object.tag }}</a>
</div>
<div class="section channel" *ngFor="let object of overview.channels">
<div class="section-title">
<a [routerLink]="[ '/video-channels', buildVideoChannelBy(object) ]">
<img [src]="buildVideoChannelAvatarUrl(object)" alt="Avatar" />
<my-video-miniature *ngFor="let video of buildVideos(object.videos)" [video]="video" [user]="user" [displayVideoActions]="false">
</my-video-miniature>
</div>
<div>{{ object.channel.displayName }}</div>
</a>
</div>
<div class="section channel" *ngFor="let object of overview.channels">
<div class="section-title">
<a [routerLink]="[ '/video-channels', buildVideoChannelBy(object) ]">
<img [src]="buildVideoChannelAvatarUrl(object)" alt="Avatar" />
<div>{{ object.channel.displayName }}</div>
</a>
</div>
<my-video-miniature *ngFor="let video of buildVideos(object.videos)" [video]="video" [user]="user" [displayVideoActions]="false">
</my-video-miniature>
</div>
</ng-container>
<my-video-miniature *ngFor="let video of buildVideos(object.videos)" [video]="video" [user]="user" [displayVideoActions]="false">
</my-video-miniature>
</div>
</div>

View File

@ -5,6 +5,7 @@ import { VideosOverview } from '@app/shared/overview/videos-overview.model'
import { OverviewService } from '@app/shared/overview'
import { Video } from '@app/shared/video/video.model'
import { ScreenService } from '@app/shared/misc/screen.service'
import { Subject } from 'rxjs'
@Component({
selector: 'my-video-overview',
@ -12,13 +13,17 @@ import { ScreenService } from '@app/shared/misc/screen.service'
styleUrls: [ './video-overview.component.scss' ]
})
export class VideoOverviewComponent implements OnInit {
overview: VideosOverview = {
categories: [],
channels: [],
tags: []
}
onDataSubject = new Subject<any>()
overviews: VideosOverview[] = []
notResults = false
private loaded = false
private currentPage = 1
private maxPage = 20
private lastWasEmpty = false
private isLoading = false
constructor (
private i18n: I18n,
private notifier: Notifier,
@ -32,20 +37,7 @@ export class VideoOverviewComponent implements OnInit {
}
ngOnInit () {
this.overviewService.getVideosOverview()
.subscribe(
overview => {
this.overview = overview
if (
this.overview.categories.length === 0 &&
this.overview.channels.length === 0 &&
this.overview.tags.length === 0
) this.notResults = true
},
err => this.notifier.error(err.message)
)
this.loadMoreResults()
}
buildVideoChannelBy (object: { videos: Video[] }) {
@ -61,4 +53,41 @@ export class VideoOverviewComponent implements OnInit {
return videos.slice(0, numberOfVideos * 2)
}
onNearOfBottom () {
if (this.currentPage >= this.maxPage) return
if (this.lastWasEmpty) return
if (this.isLoading) return
this.currentPage++
this.loadMoreResults()
}
private loadMoreResults () {
this.isLoading = true
this.overviewService.getVideosOverview(this.currentPage)
.subscribe(
overview => {
this.isLoading = false
if (overview.tags.length === 0 && overview.channels.length === 0 && overview.categories.length === 0) {
this.lastWasEmpty = true
if (this.loaded === false) this.notResults = true
return
}
this.loaded = true
this.onDataSubject.next(overview)
this.overviews.push(overview)
},
err => {
this.notifier.error(err.message)
this.isLoading = false
}
)
}
}