Handle overview pagination in client
This commit is contained in:
parent
764a965778
commit
111fdc267b
|
@ -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')
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 () {
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue