Handle overview pagination in client
This commit is contained in:
parent
764a965778
commit
111fdc267b
|
@ -263,16 +263,18 @@ export class ServerService {
|
||||||
.pipe(map(data => ({ data, translations })))
|
.pipe(map(data => ({ data, translations })))
|
||||||
}),
|
}),
|
||||||
map(({ data, translations }) => {
|
map(({ data, translations }) => {
|
||||||
const hashToPopulate: VideoConstant<T>[] = []
|
const hashToPopulate: VideoConstant<T>[] = Object.keys(data)
|
||||||
|
.map(dataKey => {
|
||||||
Object.keys(data)
|
|
||||||
.forEach(dataKey => {
|
|
||||||
const label = data[ dataKey ]
|
const label = data[ dataKey ]
|
||||||
|
|
||||||
hashToPopulate.push({
|
const id = attributeName === 'languages'
|
||||||
id: (attributeName === 'languages' ? dataKey : parseInt(dataKey, 10)) as T,
|
? dataKey as T
|
||||||
|
: parseInt(dataKey, 10) as T
|
||||||
|
|
||||||
|
return {
|
||||||
|
id,
|
||||||
label: peertubeTranslate(label, translations)
|
label: peertubeTranslate(label, translations)
|
||||||
})
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if (sort === true) sortBy(hashToPopulate, 'label')
|
if (sort === true) sortBy(hashToPopulate, 'label')
|
||||||
|
|
|
@ -36,6 +36,7 @@ export class LanguageChooserComponent {
|
||||||
getCurrentLanguage () {
|
getCurrentLanguage () {
|
||||||
const english = 'English'
|
const english = 'English'
|
||||||
const locale = isOnDevLocale() ? getDevLocale() : getCompleteLocale(this.localeId)
|
const locale = isOnDevLocale() ? getDevLocale() : getCompleteLocale(this.localeId)
|
||||||
|
|
||||||
if (locale) return I18N_LOCALES[locale] || english
|
if (locale) return I18N_LOCALES[locale] || english
|
||||||
return english
|
return english
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,8 +23,10 @@ export class MenuComponent implements OnInit {
|
||||||
|
|
||||||
userHasAdminAccess = false
|
userHasAdminAccess = false
|
||||||
helpVisible = false
|
helpVisible = false
|
||||||
languages: VideoConstant<string>[] = []
|
|
||||||
|
|
||||||
|
videoLanguages: string[] = []
|
||||||
|
|
||||||
|
private languages: VideoConstant<string>[] = []
|
||||||
private serverConfig: ServerConfig
|
private serverConfig: ServerConfig
|
||||||
private routesPerRight: { [ role in UserRight ]?: string } = {
|
private routesPerRight: { [ role in UserRight ]?: string } = {
|
||||||
[UserRight.MANAGE_USERS]: '/admin/users',
|
[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 () {
|
get language () {
|
||||||
return this.languageChooserModal.getCurrentLanguage()
|
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 () {
|
get nsfwPolicy () {
|
||||||
if (!this.user) return
|
if (!this.user) return
|
||||||
|
|
||||||
switch (this.user.nsfwPolicy) {
|
switch (this.user.nsfwPolicy) {
|
||||||
case 'do_not_list':
|
case 'do_not_list':
|
||||||
return this.i18n('hide')
|
return this.i18n('hide')
|
||||||
|
|
||||||
case 'blur':
|
case 'blur':
|
||||||
return this.i18n('blur')
|
return this.i18n('blur')
|
||||||
|
|
||||||
case 'display':
|
case 'display':
|
||||||
return this.i18n('display')
|
return this.i18n('display')
|
||||||
}
|
}
|
||||||
|
@ -156,13 +160,29 @@ export class MenuComponent implements OnInit {
|
||||||
toggleUseP2P () {
|
toggleUseP2P () {
|
||||||
if (!this.user) return
|
if (!this.user) return
|
||||||
this.user.webTorrentEnabled = !this.user.webTorrentEnabled
|
this.user.webTorrentEnabled = !this.user.webTorrentEnabled
|
||||||
this.userService.updateMyProfile({
|
|
||||||
webTorrentEnabled: this.user.webTorrentEnabled
|
this.userService.updateMyProfile({ webTorrentEnabled: this.user.webTorrentEnabled })
|
||||||
}).subscribe(() => this.authService.refreshUserInformation())
|
.subscribe(() => this.authService.refreshUserInformation())
|
||||||
}
|
}
|
||||||
|
|
||||||
langForLocale (localeId: string) {
|
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 () {
|
private computeIsUserHasAdminAccess () {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { catchError, map, switchMap, tap } from 'rxjs/operators'
|
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 { Injectable } from '@angular/core'
|
||||||
import { forkJoin, Observable, of } from 'rxjs'
|
import { forkJoin, Observable, of } from 'rxjs'
|
||||||
import { VideosOverview as VideosOverviewServer, peertubeTranslate } from '../../../../../shared/models'
|
import { VideosOverview as VideosOverviewServer, peertubeTranslate } from '../../../../../shared/models'
|
||||||
|
@ -21,9 +21,12 @@ export class OverviewService {
|
||||||
private serverService: ServerService
|
private serverService: ServerService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
getVideosOverview (): Observable<VideosOverview> {
|
getVideosOverview (page: number): Observable<VideosOverview> {
|
||||||
|
let params = new HttpParams()
|
||||||
|
params = params.append('page', page + '')
|
||||||
|
|
||||||
return this.authHttp
|
return this.authHttp
|
||||||
.get<VideosOverviewServer>(OverviewService.BASE_OVERVIEW_URL + 'videos')
|
.get<VideosOverviewServer>(OverviewService.BASE_OVERVIEW_URL + 'videos', { params })
|
||||||
.pipe(
|
.pipe(
|
||||||
switchMap(serverVideosOverview => this.updateVideosOverview(serverVideosOverview)),
|
switchMap(serverVideosOverview => this.updateVideosOverview(serverVideosOverview)),
|
||||||
catchError(err => this.restExtractor.handleError(err))
|
catchError(err => this.restExtractor.handleError(err))
|
||||||
|
|
|
@ -2,6 +2,11 @@
|
||||||
|
|
||||||
<div class="no-results" i18n *ngIf="notResults">No results.</div>
|
<div class="no-results" i18n *ngIf="notResults">No results.</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [autoInit]="true" [dataObservable]="onDataSubject.asObservable()"
|
||||||
|
>
|
||||||
|
<ng-container *ngFor="let overview of overviews">
|
||||||
|
|
||||||
<div class="section" *ngFor="let object of overview.categories">
|
<div class="section" *ngFor="let object of overview.categories">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<a routerLink="/search" [queryParams]="{ categoryOneOf: [ object.category.id ] }">{{ object.category.label }}</a>
|
<a routerLink="/search" [queryParams]="{ categoryOneOf: [ object.category.id ] }">{{ object.category.label }}</a>
|
||||||
|
@ -33,4 +38,8 @@
|
||||||
</my-video-miniature>
|
</my-video-miniature>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { VideosOverview } from '@app/shared/overview/videos-overview.model'
|
||||||
import { OverviewService } from '@app/shared/overview'
|
import { OverviewService } from '@app/shared/overview'
|
||||||
import { Video } from '@app/shared/video/video.model'
|
import { Video } from '@app/shared/video/video.model'
|
||||||
import { ScreenService } from '@app/shared/misc/screen.service'
|
import { ScreenService } from '@app/shared/misc/screen.service'
|
||||||
|
import { Subject } from 'rxjs'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-video-overview',
|
selector: 'my-video-overview',
|
||||||
|
@ -12,13 +13,17 @@ import { ScreenService } from '@app/shared/misc/screen.service'
|
||||||
styleUrls: [ './video-overview.component.scss' ]
|
styleUrls: [ './video-overview.component.scss' ]
|
||||||
})
|
})
|
||||||
export class VideoOverviewComponent implements OnInit {
|
export class VideoOverviewComponent implements OnInit {
|
||||||
overview: VideosOverview = {
|
onDataSubject = new Subject<any>()
|
||||||
categories: [],
|
|
||||||
channels: [],
|
overviews: VideosOverview[] = []
|
||||||
tags: []
|
|
||||||
}
|
|
||||||
notResults = false
|
notResults = false
|
||||||
|
|
||||||
|
private loaded = false
|
||||||
|
private currentPage = 1
|
||||||
|
private maxPage = 20
|
||||||
|
private lastWasEmpty = false
|
||||||
|
private isLoading = false
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private i18n: I18n,
|
private i18n: I18n,
|
||||||
private notifier: Notifier,
|
private notifier: Notifier,
|
||||||
|
@ -32,20 +37,7 @@ export class VideoOverviewComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit () {
|
ngOnInit () {
|
||||||
this.overviewService.getVideosOverview()
|
this.loadMoreResults()
|
||||||
.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)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
buildVideoChannelBy (object: { videos: Video[] }) {
|
buildVideoChannelBy (object: { videos: Video[] }) {
|
||||||
|
@ -61,4 +53,41 @@ export class VideoOverviewComponent implements OnInit {
|
||||||
|
|
||||||
return videos.slice(0, numberOfVideos * 2)
|
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