aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/shared
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2019-08-02 14:49:25 +0200
committerChocobozzz <me@florianbigard.com>2019-08-02 14:49:25 +0200
commitad453580b20056fd80b3245d4db554f5ca1a5e29 (patch)
treeed07a6dbd8bc8cd27b22cd33dabcbd3d31deea07 /client/src/app/shared
parentdd570a34ff731a6cd98ef8f8bf83f234e804f6c1 (diff)
downloadPeerTube-ad453580b20056fd80b3245d4db554f5ca1a5e29.tar.gz
PeerTube-ad453580b20056fd80b3245d4db554f5ca1a5e29.tar.zst
PeerTube-ad453580b20056fd80b3245d4db554f5ca1a5e29.zip
Fix infinite scroll on big screens
Diffstat (limited to 'client/src/app/shared')
-rw-r--r--client/src/app/shared/users/user-notifications.component.html2
-rw-r--r--client/src/app/shared/users/user-notifications.component.ts5
-rw-r--r--client/src/app/shared/video-playlist/video-add-to-playlist.component.ts2
-rw-r--r--client/src/app/shared/video-playlist/video-playlist.service.ts15
-rw-r--r--client/src/app/shared/video/abstract-video-list.html2
-rw-r--r--client/src/app/shared/video/abstract-video-list.ts6
-rw-r--r--client/src/app/shared/video/infinite-scroller.directive.ts65
-rw-r--r--client/src/app/shared/video/videos-selection.component.html2
8 files changed, 70 insertions, 29 deletions
diff --git a/client/src/app/shared/users/user-notifications.component.html b/client/src/app/shared/users/user-notifications.component.html
index d0d9d9f35..292813426 100644
--- a/client/src/app/shared/users/user-notifications.component.html
+++ b/client/src/app/shared/users/user-notifications.component.html
@@ -1,6 +1,6 @@
1<div *ngIf="componentPagination.totalItems === 0" class="no-notification" i18n>You don't have notifications.</div> 1<div *ngIf="componentPagination.totalItems === 0" class="no-notification" i18n>You don't have notifications.</div>
2 2
3<div class="notifications" myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()"> 3<div class="notifications" myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()">
4 <div *ngFor="let notification of notifications" class="notification" [ngClass]="{ unread: !notification.read }" (click)="markAsRead(notification)"> 4 <div *ngFor="let notification of notifications" class="notification" [ngClass]="{ unread: !notification.read }" (click)="markAsRead(notification)">
5 5
6 <ng-container [ngSwitch]="notification.type"> 6 <ng-container [ngSwitch]="notification.type">
diff --git a/client/src/app/shared/users/user-notifications.component.ts b/client/src/app/shared/users/user-notifications.component.ts
index ce43b604a..3c9eb369d 100644
--- a/client/src/app/shared/users/user-notifications.component.ts
+++ b/client/src/app/shared/users/user-notifications.component.ts
@@ -4,6 +4,7 @@ import { UserNotificationType } from '../../../../../shared'
4import { ComponentPagination, hasMoreItems } from '@app/shared/rest/component-pagination.model' 4import { ComponentPagination, hasMoreItems } from '@app/shared/rest/component-pagination.model'
5import { Notifier } from '@app/core' 5import { Notifier } from '@app/core'
6import { UserNotification } from '@app/shared/users/user-notification.model' 6import { UserNotification } from '@app/shared/users/user-notification.model'
7import { Subject } from 'rxjs'
7 8
8@Component({ 9@Component({
9 selector: 'my-user-notifications', 10 selector: 'my-user-notifications',
@@ -24,6 +25,8 @@ export class UserNotificationsComponent implements OnInit {
24 25
25 componentPagination: ComponentPagination 26 componentPagination: ComponentPagination
26 27
28 onDataSubject = new Subject<any[]>()
29
27 constructor ( 30 constructor (
28 private userNotificationService: UserNotificationService, 31 private userNotificationService: UserNotificationService,
29 private notifier: Notifier 32 private notifier: Notifier
@@ -47,6 +50,8 @@ export class UserNotificationsComponent implements OnInit {
47 this.componentPagination.totalItems = result.total 50 this.componentPagination.totalItems = result.total
48 51
49 this.notificationsLoaded.emit() 52 this.notificationsLoaded.emit()
53
54 this.onDataSubject.next(result.data)
50 }, 55 },
51 56
52 err => this.notifier.error(err.message) 57 err => this.notifier.error(err.message)
diff --git a/client/src/app/shared/video-playlist/video-add-to-playlist.component.ts b/client/src/app/shared/video-playlist/video-add-to-playlist.component.ts
index 08ceb21bc..72de84703 100644
--- a/client/src/app/shared/video-playlist/video-add-to-playlist.component.ts
+++ b/client/src/app/shared/video-playlist/video-add-to-playlist.component.ts
@@ -83,7 +83,7 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit,
83 83
84 load () { 84 load () {
85 forkJoin([ 85 forkJoin([
86 this.videoPlaylistService.listAccountPlaylists(this.user.account, '-updatedAt'), 86 this.videoPlaylistService.listAccountPlaylists(this.user.account, undefined,'-updatedAt'),
87 this.videoPlaylistService.doesVideoExistInPlaylist(this.video.id) 87 this.videoPlaylistService.doesVideoExistInPlaylist(this.video.id)
88 ]) 88 ])
89 .subscribe( 89 .subscribe(
diff --git a/client/src/app/shared/video-playlist/video-playlist.service.ts b/client/src/app/shared/video-playlist/video-playlist.service.ts
index b93a19356..376387082 100644
--- a/client/src/app/shared/video-playlist/video-playlist.service.ts
+++ b/client/src/app/shared/video-playlist/video-playlist.service.ts
@@ -45,21 +45,28 @@ export class VideoPlaylistService {
45 ) 45 )
46 } 46 }
47 47
48 listChannelPlaylists (videoChannel: VideoChannel): Observable<ResultList<VideoPlaylist>> { 48 listChannelPlaylists (videoChannel: VideoChannel, componentPagination: ComponentPagination): Observable<ResultList<VideoPlaylist>> {
49 const url = VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannel.nameWithHost + '/video-playlists' 49 const url = VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannel.nameWithHost + '/video-playlists'
50 const pagination = this.restService.componentPaginationToRestPagination(componentPagination)
50 51
51 return this.authHttp.get<ResultList<VideoPlaylist>>(url) 52 let params = new HttpParams()
53 params = this.restService.addRestGetParams(params, pagination)
54
55 return this.authHttp.get<ResultList<VideoPlaylist>>(url, { params })
52 .pipe( 56 .pipe(
53 switchMap(res => this.extractPlaylists(res)), 57 switchMap(res => this.extractPlaylists(res)),
54 catchError(err => this.restExtractor.handleError(err)) 58 catchError(err => this.restExtractor.handleError(err))
55 ) 59 )
56 } 60 }
57 61
58 listAccountPlaylists (account: Account, sort: string): Observable<ResultList<VideoPlaylist>> { 62 listAccountPlaylists (account: Account, componentPagination: ComponentPagination, sort: string): Observable<ResultList<VideoPlaylist>> {
59 const url = AccountService.BASE_ACCOUNT_URL + account.nameWithHost + '/video-playlists' 63 const url = AccountService.BASE_ACCOUNT_URL + account.nameWithHost + '/video-playlists'
64 const pagination = componentPagination
65 ? this.restService.componentPaginationToRestPagination(componentPagination)
66 : undefined
60 67
61 let params = new HttpParams() 68 let params = new HttpParams()
62 params = this.restService.addRestGetParams(params, undefined, sort) 69 params = this.restService.addRestGetParams(params, pagination, sort)
63 70
64 return this.authHttp.get<ResultList<VideoPlaylist>>(url, { params }) 71 return this.authHttp.get<ResultList<VideoPlaylist>>(url, { params })
65 .pipe( 72 .pipe(
diff --git a/client/src/app/shared/video/abstract-video-list.html b/client/src/app/shared/video/abstract-video-list.html
index efd369bca..13aedcc74 100644
--- a/client/src/app/shared/video/abstract-video-list.html
+++ b/client/src/app/shared/video/abstract-video-list.html
@@ -19,7 +19,7 @@
19 19
20 <div class="no-results" i18n *ngIf="pagination.totalItems === 0">No results.</div> 20 <div class="no-results" i18n *ngIf="pagination.totalItems === 0">No results.</div>
21 <div 21 <div
22 myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [autoInit]="true" 22 myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [autoInit]="true" [dataObservable]="onDataSubject.asObservable()"
23 class="videos" 23 class="videos"
24 > 24 >
25 <ng-container *ngFor="let video of videos; trackBy: videoById;"> 25 <ng-container *ngFor="let video of videos; trackBy: videoById;">
diff --git a/client/src/app/shared/video/abstract-video-list.ts b/client/src/app/shared/video/abstract-video-list.ts
index 8a247a9af..2926b179b 100644
--- a/client/src/app/shared/video/abstract-video-list.ts
+++ b/client/src/app/shared/video/abstract-video-list.ts
@@ -1,7 +1,7 @@
1import { debounceTime, first, tap } from 'rxjs/operators' 1import { debounceTime, first, tap } from 'rxjs/operators'
2import { OnDestroy, OnInit } from '@angular/core' 2import { OnDestroy, OnInit } from '@angular/core'
3import { ActivatedRoute, Router } from '@angular/router' 3import { ActivatedRoute, Router } from '@angular/router'
4import { fromEvent, Observable, of, Subscription } from 'rxjs' 4import { fromEvent, Observable, of, Subject, Subscription } from 'rxjs'
5import { AuthService } from '../../core/auth' 5import { AuthService } from '../../core/auth'
6import { ComponentPagination } from '../rest/component-pagination.model' 6import { ComponentPagination } from '../rest/component-pagination.model'
7import { VideoSortField } from './sort-field.type' 7import { VideoSortField } from './sort-field.type'
@@ -59,6 +59,8 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableFor
59 blacklistInfo: false 59 blacklistInfo: false
60 } 60 }
61 61
62 onDataSubject = new Subject<any[]>()
63
62 protected abstract notifier: Notifier 64 protected abstract notifier: Notifier
63 protected abstract authService: AuthService 65 protected abstract authService: AuthService
64 protected abstract route: ActivatedRoute 66 protected abstract route: ActivatedRoute
@@ -147,6 +149,8 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableFor
147 if (this.groupByDate) this.buildGroupedDateLabels() 149 if (this.groupByDate) this.buildGroupedDateLabels()
148 150
149 this.onMoreVideos() 151 this.onMoreVideos()
152
153 this.onDataSubject.next(data)
150 }, 154 },
151 155
152 error => this.notifier.error(error.message) 156 error => this.notifier.error(error.message)
diff --git a/client/src/app/shared/video/infinite-scroller.directive.ts b/client/src/app/shared/video/infinite-scroller.directive.ts
index b1e88882c..9f613c5fa 100644
--- a/client/src/app/shared/video/infinite-scroller.directive.ts
+++ b/client/src/app/shared/video/infinite-scroller.directive.ts
@@ -1,14 +1,15 @@
1import { distinct, distinctUntilChanged, filter, map, share, startWith, throttleTime } from 'rxjs/operators' 1import { distinctUntilChanged, filter, map, share, startWith, tap, throttleTime } from 'rxjs/operators'
2import { Directive, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core' 2import { AfterContentChecked, Directive, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'
3import { fromEvent, Subscription } from 'rxjs' 3import { fromEvent, Observable, Subscription } from 'rxjs'
4 4
5@Directive({ 5@Directive({
6 selector: '[myInfiniteScroller]' 6 selector: '[myInfiniteScroller]'
7}) 7})
8export class InfiniteScrollerDirective implements OnInit, OnDestroy { 8export class InfiniteScrollerDirective implements OnInit, OnDestroy, AfterContentChecked {
9 @Input() percentLimit = 70 9 @Input() percentLimit = 70
10 @Input() autoInit = false 10 @Input() autoInit = false
11 @Input() onItself = false 11 @Input() onItself = false
12 @Input() dataObservable: Observable<any[]>
12 13
13 @Output() nearOfBottom = new EventEmitter<void>() 14 @Output() nearOfBottom = new EventEmitter<void>()
14 15
@@ -17,10 +18,22 @@ export class InfiniteScrollerDirective implements OnInit, OnDestroy {
17 private scrollDownSub: Subscription 18 private scrollDownSub: Subscription
18 private container: HTMLElement 19 private container: HTMLElement
19 20
21 private checkScroll = false
22
20 constructor (private el: ElementRef) { 23 constructor (private el: ElementRef) {
21 this.decimalLimit = this.percentLimit / 100 24 this.decimalLimit = this.percentLimit / 100
22 } 25 }
23 26
27 ngAfterContentChecked () {
28 if (this.checkScroll) {
29 this.checkScroll = false
30
31 console.log('Checking if the initial state has a scroll.')
32
33 if (this.hasScroll() === false) this.nearOfBottom.emit()
34 }
35 }
36
24 ngOnInit () { 37 ngOnInit () {
25 if (this.autoInit === true) return this.initialize() 38 if (this.autoInit === true) return this.initialize()
26 } 39 }
@@ -30,14 +43,15 @@ export class InfiniteScrollerDirective implements OnInit, OnDestroy {
30 } 43 }
31 44
32 initialize () { 45 initialize () {
33 if (this.onItself) { 46 this.container = this.onItself
34 this.container = this.el.nativeElement 47 ? this.el.nativeElement
35 } 48 : document.documentElement
36 49
37 // Emit the last value 50 // Emit the last value
38 const throttleOptions = { leading: true, trailing: true } 51 const throttleOptions = { leading: true, trailing: true }
39 52
40 const scrollObservable = fromEvent(this.container || window, 'scroll') 53 const scrollableElement = this.onItself ? this.container : window
54 const scrollObservable = fromEvent(scrollableElement, 'scroll')
41 .pipe( 55 .pipe(
42 startWith(null as string), // FIXME: typings 56 startWith(null as string), // FIXME: typings
43 throttleTime(200, undefined, throttleOptions), 57 throttleTime(200, undefined, throttleOptions),
@@ -49,23 +63,34 @@ export class InfiniteScrollerDirective implements OnInit, OnDestroy {
49 // Scroll Down 63 // Scroll Down
50 this.scrollDownSub = scrollObservable 64 this.scrollDownSub = scrollObservable
51 .pipe( 65 .pipe(
52 // Check we scroll down 66 filter(({ current }) => this.isScrollingDown(current)),
53 filter(({ current }) => { 67 filter(({ current, maximumScroll }) => (current / maximumScroll) > this.decimalLimit)
54 const res = this.lastCurrentBottom < current
55
56 this.lastCurrentBottom = current
57 return res
58 }),
59 filter(({ current, maximumScroll }) => maximumScroll <= 0 || (current / maximumScroll) > this.decimalLimit)
60 ) 68 )
61 .subscribe(() => this.nearOfBottom.emit()) 69 .subscribe(() => this.nearOfBottom.emit())
70
71 if (this.dataObservable) {
72 this.dataObservable
73 .pipe(filter(d => d.length !== 0))
74 .subscribe(() => this.checkScroll = true)
75 }
62 } 76 }
63 77
64 private getScrollInfo () { 78 private getScrollInfo () {
65 if (this.container) { 79 return { current: this.container.scrollTop, maximumScroll: this.getMaximumScroll() }
66 return { current: this.container.scrollTop, maximumScroll: this.container.scrollHeight } 80 }
67 } 81
82 private getMaximumScroll () {
83 return this.container.scrollHeight - window.innerHeight
84 }
85
86 private hasScroll () {
87 return this.getMaximumScroll() > 0
88 }
89
90 private isScrollingDown (current: number) {
91 const result = this.lastCurrentBottom < current
68 92
69 return { current: window.scrollY, maximumScroll: document.body.clientHeight - window.innerHeight } 93 this.lastCurrentBottom = current
94 return result
70 } 95 }
71} 96}
diff --git a/client/src/app/shared/video/videos-selection.component.html b/client/src/app/shared/video/videos-selection.component.html
index 120c168cd..2b4b353cf 100644
--- a/client/src/app/shared/video/videos-selection.component.html
+++ b/client/src/app/shared/video/videos-selection.component.html
@@ -1,6 +1,6 @@
1<div class="no-results" i18n *ngIf="pagination.totalItems === 0">No results.</div> 1<div class="no-results" i18n *ngIf="pagination.totalItems === 0">No results.</div>
2 2
3<div myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()" class="videos"> 3<div myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()" class="videos">
4 <div class="video" *ngFor="let video of videos; let i = index; trackBy: videoById"> 4 <div class="video" *ngFor="let video of videos; let i = index; trackBy: videoById">
5 5
6 <div class="checkbox-container"> 6 <div class="checkbox-container">