Fix infinite scroll on big screens

This commit is contained in:
Chocobozzz 2019-08-02 14:49:25 +02:00
parent dd570a34ff
commit ad453580b2
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
28 changed files with 135 additions and 49 deletions

View File

@ -1,4 +1,4 @@
<div class="row" myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()"> <div class="row" myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()">
<div class="col-xl-6 col-md-12"> <div class="col-xl-6 col-md-12">
<div i18n class="subtitle">Followers</div> <div i18n class="subtitle">Followers</div>

View File

@ -4,6 +4,7 @@ import { ComponentPagination, hasMoreItems } from '@app/shared/rest/component-pa
import { Notifier } from '@app/core' import { Notifier } from '@app/core'
import { RestService } from '@app/shared' import { RestService } from '@app/shared'
import { SortMeta } from 'primeng/api' import { SortMeta } from 'primeng/api'
import { Subject } from 'rxjs'
@Component({ @Component({
selector: 'my-about-follows', selector: 'my-about-follows',
@ -17,13 +18,13 @@ export class AboutFollowsComponent implements OnInit {
followersPagination: ComponentPagination = { followersPagination: ComponentPagination = {
currentPage: 1, currentPage: 1,
itemsPerPage: 40, itemsPerPage: 20,
totalItems: null totalItems: null
} }
followingsPagination: ComponentPagination = { followingsPagination: ComponentPagination = {
currentPage: 1, currentPage: 1,
itemsPerPage: 40, itemsPerPage: 20,
totalItems: null totalItems: null
} }
@ -32,6 +33,8 @@ export class AboutFollowsComponent implements OnInit {
order: -1 order: -1
} }
onDataSubject = new Subject<any[]>()
constructor ( constructor (
private restService: RestService, private restService: RestService,
private notifier: Notifier, private notifier: Notifier,
@ -78,6 +81,8 @@ export class AboutFollowsComponent implements OnInit {
this.followers = this.followers.concat(newFollowers) this.followers = this.followers.concat(newFollowers)
this.followersPagination.totalItems = resultList.total this.followersPagination.totalItems = resultList.total
this.onDataSubject.next(newFollowers)
}, },
err => this.notifier.error(err.message) err => this.notifier.error(err.message)
@ -94,6 +99,8 @@ export class AboutFollowsComponent implements OnInit {
this.followings = this.followings.concat(newFollowings) this.followings = this.followings.concat(newFollowings)
this.followingsPagination.totalItems = resultList.total this.followingsPagination.totalItems = resultList.total
this.onDataSubject.next(newFollowings)
}, },
err => this.notifier.error(err.message) err => this.notifier.error(err.message)

View File

@ -2,7 +2,7 @@
<div class="no-results" i18n *ngIf="channelPagination.totalItems === 0">This account does not have channels.</div> <div class="no-results" i18n *ngIf="channelPagination.totalItems === 0">This account does not have channels.</div>
<div class="channels" myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [autoInit]="true"> <div class="channels" myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [autoInit]="true" [dataObservable]="onChannelDataSubject.asObservable()">
<div class="section channel" *ngFor="let videoChannel of videoChannels"> <div class="section channel" *ngFor="let videoChannel of videoChannels">
<div class="section-title"> <div class="section-title">
<a [routerLink]="getVideoChannelLink(videoChannel)" i18n-title title="See this video channel"> <a [routerLink]="getVideoChannelLink(videoChannel)" i18n-title title="See this video channel">

View File

@ -4,7 +4,7 @@ import { Account } from '@app/shared/account/account.model'
import { AccountService } from '@app/shared/account/account.service' import { AccountService } from '@app/shared/account/account.service'
import { VideoChannelService } from '@app/shared/video-channel/video-channel.service' import { VideoChannelService } from '@app/shared/video-channel/video-channel.service'
import { concatMap, map, switchMap, tap } from 'rxjs/operators' import { concatMap, map, switchMap, tap } from 'rxjs/operators'
import { from, Subscription } from 'rxjs' import { from, Subject, Subscription } from 'rxjs'
import { VideoChannel } from '@app/shared/video-channel/video-channel.model' import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
import { Video } from '@app/shared/video/video.model' import { Video } from '@app/shared/video/video.model'
import { AuthService } from '@app/core' import { AuthService } from '@app/core'
@ -33,6 +33,8 @@ export class AccountVideoChannelsComponent implements OnInit, OnDestroy {
} }
videosSort: VideoSortField = '-publishedAt' videosSort: VideoSortField = '-publishedAt'
onChannelDataSubject = new Subject<any>()
private accountSub: Subscription private accountSub: Subscription
constructor ( constructor (
@ -75,6 +77,8 @@ export class AccountVideoChannelsComponent implements OnInit, OnDestroy {
this.videoChannels.push(videoChannel) this.videoChannels.push(videoChannel)
this.videos[videoChannel.id] = videos this.videos[videoChannel.id] = videos
this.onChannelDataSubject.next([ videoChannel ])
}) })
} }

View File

@ -6,7 +6,7 @@
{{ getNoResultMessage() }} {{ getNoResultMessage() }}
</div> </div>
<div class="plugins" myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [autoInit]="true"> <div class="plugins" myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [autoInit]="true" [dataObservable]="onDataSubject.asObservable()">
<div class="card plugin" *ngFor="let plugin of plugins"> <div class="card plugin" *ngFor="let plugin of plugins">
<div class="card-body"> <div class="card-body">
<div class="first-row"> <div class="first-row">

View File

@ -8,6 +8,7 @@ import { PeerTubePlugin } from '@shared/models/plugins/peertube-plugin.model'
import { ActivatedRoute, Router } from '@angular/router' import { ActivatedRoute, Router } from '@angular/router'
import { compareSemVer } from '@shared/core-utils/miscs/miscs' import { compareSemVer } from '@shared/core-utils/miscs/miscs'
import { PluginService } from '@app/core/plugins/plugin.service' import { PluginService } from '@app/core/plugins/plugin.service'
import { Subject } from 'rxjs'
@Component({ @Component({
selector: 'my-plugin-list-installed', selector: 'my-plugin-list-installed',
@ -33,6 +34,8 @@ export class PluginListInstalledComponent implements OnInit {
PluginType = PluginType PluginType = PluginType
onDataSubject = new Subject<any[]>()
constructor ( constructor (
private i18n: I18n, private i18n: I18n,
private pluginService: PluginService, private pluginService: PluginService,
@ -67,6 +70,8 @@ export class PluginListInstalledComponent implements OnInit {
res => { res => {
this.plugins = this.plugins.concat(res.data) this.plugins = this.plugins.concat(res.data)
this.pagination.totalItems = res.total this.pagination.totalItems = res.total
this.onDataSubject.next(res.data)
}, },
err => this.notifier.error(err.message) err => this.notifier.error(err.message)

View File

@ -29,7 +29,7 @@
No results. No results.
</div> </div>
<div class="plugins" myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [autoInit]="true"> <div class="plugins" myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [autoInit]="true" [dataObservable]="onDataSubject.asObservable()">
<div class="card plugin" *ngFor="let plugin of plugins"> <div class="card plugin" *ngFor="let plugin of plugins">
<div class="card-body"> <div class="card-body">
<div class="first-row"> <div class="first-row">

View File

@ -36,6 +36,8 @@ export class PluginSearchComponent implements OnInit {
installing: { [name: string]: boolean } = {} installing: { [name: string]: boolean } = {}
pluginInstalled = false pluginInstalled = false
onDataSubject = new Subject<any[]>()
private searchSubject = new Subject<string>() private searchSubject = new Subject<string>()
constructor ( constructor (
@ -90,6 +92,8 @@ export class PluginSearchComponent implements OnInit {
this.plugins = this.plugins.concat(res.data) this.plugins = this.plugins.concat(res.data)
this.pagination.totalItems = res.total this.pagination.totalItems = res.total
this.onDataSubject.next(res.data)
}, },
err => { err => {

View File

@ -13,7 +13,7 @@
<div class="no-history" i18n *ngIf="pagination.totalItems === 0">You don't have videos history yet.</div> <div class="no-history" i18n *ngIf="pagination.totalItems === 0">You don't have videos history yet.</div>
<div myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [autoInit]="true" class="videos"> <div myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [autoInit]="true" [dataObservable]="onDataSubject.asObservable()" class="videos">
<div class="video" *ngFor="let video of videos"> <div class="video" *ngFor="let video of videos">
<my-video-miniature <my-video-miniature
[video]="video" [displayAsRow]="true" [video]="video" [displayAsRow]="true"

View File

@ -1,6 +1,6 @@
<div class="no-results" i18n *ngIf="pagination.totalItems === 0">You don't have any subscriptions yet.</div> <div class="no-results" i18n *ngIf="pagination.totalItems === 0">You don't have any subscriptions yet.</div>
<div class="video-channels" myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()"> <div class="video-channels" myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()">
<div *ngFor="let videoChannel of videoChannels" class="video-channel"> <div *ngFor="let videoChannel of videoChannels" class="video-channel">
<a [routerLink]="[ '/video-channels', videoChannel.nameWithHost ]"> <a [routerLink]="[ '/video-channels', videoChannel.nameWithHost ]">
<img [src]="videoChannel.avatarUrl" alt="Avatar" /> <img [src]="videoChannel.avatarUrl" alt="Avatar" />

View File

@ -3,6 +3,7 @@ import { Notifier } from '@app/core'
import { VideoChannel } from '@app/shared/video-channel/video-channel.model' import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
import { UserSubscriptionService } from '@app/shared/user-subscription' import { UserSubscriptionService } from '@app/shared/user-subscription'
import { ComponentPagination } from '@app/shared/rest/component-pagination.model' import { ComponentPagination } from '@app/shared/rest/component-pagination.model'
import { Subject } from 'rxjs'
@Component({ @Component({
selector: 'my-account-subscriptions', selector: 'my-account-subscriptions',
@ -18,6 +19,8 @@ export class MyAccountSubscriptionsComponent implements OnInit {
totalItems: null totalItems: null
} }
onDataSubject = new Subject<any[]>()
constructor ( constructor (
private userSubscriptionService: UserSubscriptionService, private userSubscriptionService: UserSubscriptionService,
private notifier: Notifier private notifier: Notifier
@ -33,6 +36,8 @@ export class MyAccountSubscriptionsComponent implements OnInit {
res => { res => {
this.videoChannels = this.videoChannels.concat(res.data) this.videoChannels = this.videoChannels.concat(res.data)
this.pagination.totalItems = res.total this.pagination.totalItems = res.total
this.onDataSubject.next(res.data)
}, },
error => this.notifier.error(error.message) error => this.notifier.error(error.message)

View File

@ -12,7 +12,7 @@
<div <div
class="videos" myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()" class="videos" myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()"
cdkDropList (cdkDropListDropped)="drop($event)" cdkDropList (cdkDropListDropped)="drop($event)" [dataObservable]="onDataSubject.asObservable()"
> >
<div class="video" *ngFor="let playlistElement of playlistElements; trackBy: trackByFn" cdkDrag> <div class="video" *ngFor="let playlistElement of playlistElements; trackBy: trackByFn" cdkDrag>
<my-video-playlist-element-miniature <my-video-playlist-element-miniature

View File

@ -7,7 +7,7 @@
margin-left: -15px; margin-left: -15px;
margin-top: -$sub-menu-margin-bottom; margin-top: -$sub-menu-margin-bottom;
padding: $sub-menu-margin-bottom 0; padding: $sub-menu-margin-bottom 0 -15px 0;
display: flex; display: flex;
justify-content: center; justify-content: center;

View File

@ -3,7 +3,7 @@ import { Notifier, ServerService } from '@app/core'
import { AuthService } from '../../core/auth' import { AuthService } from '../../core/auth'
import { ConfirmService } from '../../core/confirm' import { ConfirmService } from '../../core/confirm'
import { ComponentPagination } from '@app/shared/rest/component-pagination.model' import { ComponentPagination } from '@app/shared/rest/component-pagination.model'
import { Subscription } from 'rxjs' import { Subject, Subscription } from 'rxjs'
import { ActivatedRoute } from '@angular/router' import { ActivatedRoute } from '@angular/router'
import { VideoPlaylistService } from '@app/shared/video-playlist/video-playlist.service' import { VideoPlaylistService } from '@app/shared/video-playlist/video-playlist.service'
import { VideoPlaylist } from '@app/shared/video-playlist/video-playlist.model' import { VideoPlaylist } from '@app/shared/video-playlist/video-playlist.model'
@ -22,10 +22,12 @@ export class MyAccountVideoPlaylistElementsComponent implements OnInit, OnDestro
pagination: ComponentPagination = { pagination: ComponentPagination = {
currentPage: 1, currentPage: 1,
itemsPerPage: 30, itemsPerPage: 10,
totalItems: null totalItems: null
} }
onDataSubject = new Subject<any[]>()
private videoPlaylistId: string | number private videoPlaylistId: string | number
private paramsSub: Subscription private paramsSub: Subscription
@ -102,6 +104,8 @@ export class MyAccountVideoPlaylistElementsComponent implements OnInit, OnDestro
.subscribe(({ total, data }) => { .subscribe(({ total, data }) => {
this.playlistElements = this.playlistElements.concat(data) this.playlistElements = this.playlistElements.concat(data)
this.pagination.totalItems = total this.pagination.totalItems = total
this.onDataSubject.next(data)
}) })
} }

View File

@ -5,7 +5,7 @@
</a> </a>
</div> </div>
<div class="video-playlists" myInfiniteScroller (nearOfBottom)="onNearOfBottom()"> <div class="video-playlists" myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()">
<div *ngFor="let playlist of videoPlaylists" class="video-playlist"> <div *ngFor="let playlist of videoPlaylists" class="video-playlist">
<div class="miniature-wrapper"> <div class="miniature-wrapper">
<my-video-playlist-miniature [playlist]="playlist" [toManage]="true" [displayChannel]="true" [displayDescription]="true" [displayPrivacy]="true" <my-video-playlist-miniature [playlist]="playlist" [toManage]="true" [displayChannel]="true" [displayDescription]="true" [displayPrivacy]="true"

View File

@ -9,6 +9,7 @@ import { VideoPlaylist } from '@app/shared/video-playlist/video-playlist.model'
import { ComponentPagination } from '@app/shared/rest/component-pagination.model' import { ComponentPagination } from '@app/shared/rest/component-pagination.model'
import { VideoPlaylistService } from '@app/shared/video-playlist/video-playlist.service' import { VideoPlaylistService } from '@app/shared/video-playlist/video-playlist.service'
import { VideoPlaylistType } from '@shared/models' import { VideoPlaylistType } from '@shared/models'
import { Subject } from 'rxjs'
@Component({ @Component({
selector: 'my-account-video-playlists', selector: 'my-account-video-playlists',
@ -20,10 +21,12 @@ export class MyAccountVideoPlaylistsComponent implements OnInit {
pagination: ComponentPagination = { pagination: ComponentPagination = {
currentPage: 1, currentPage: 1,
itemsPerPage: 10, itemsPerPage: 5,
totalItems: null totalItems: null
} }
onDataSubject = new Subject<any[]>()
private user: User private user: User
constructor ( constructor (
@ -78,11 +81,15 @@ export class MyAccountVideoPlaylistsComponent implements OnInit {
} }
private loadVideoPlaylists () { private loadVideoPlaylists () {
const playlistsObservable = this.videoPlaylistService.listAccountPlaylists(this.user.account, this.pagination, '-updatedAt')
this.authService.userInformationLoaded this.authService.userInformationLoaded
.pipe(flatMap(() => this.videoPlaylistService.listAccountPlaylists(this.user.account, '-updatedAt'))) .pipe(flatMap(() => playlistsObservable))
.subscribe(res => { .subscribe(res => {
this.videoPlaylists = this.videoPlaylists.concat(res.data) this.videoPlaylists = this.videoPlaylists.concat(res.data)
this.pagination.totalItems = res.total this.pagination.totalItems = res.total
this.onDataSubject.next(res.data)
}) })
} }
} }

View File

@ -4,7 +4,7 @@
<div i18n class="no-results" *ngIf="pagination.totalItems === 0">This channel does not have playlists.</div> <div i18n class="no-results" *ngIf="pagination.totalItems === 0">This channel does not have playlists.</div>
<div class="video-playlist" myInfiniteScroller (nearOfBottom)="onNearOfBottom()"> <div class="video-playlist" myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()">
<div *ngFor="let playlist of videoPlaylists"> <div *ngFor="let playlist of videoPlaylists">
<my-video-playlist-miniature [playlist]="playlist" [toManage]="false"></my-video-playlist-miniature> <my-video-playlist-miniature [playlist]="playlist" [toManage]="false"></my-video-playlist-miniature>
</div> </div>

View File

@ -2,7 +2,7 @@ import { Component, OnDestroy, OnInit } from '@angular/core'
import { ConfirmService } from '../../core/confirm' import { ConfirmService } from '../../core/confirm'
import { VideoChannelService } from '@app/shared/video-channel/video-channel.service' import { VideoChannelService } from '@app/shared/video-channel/video-channel.service'
import { VideoChannel } from '@app/shared/video-channel/video-channel.model' import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
import { Subscription } from 'rxjs' import { Subject, Subscription } from 'rxjs'
import { Notifier } from '@app/core' import { Notifier } from '@app/core'
import { VideoPlaylist } from '@app/shared/video-playlist/video-playlist.model' import { VideoPlaylist } from '@app/shared/video-playlist/video-playlist.model'
import { ComponentPagination, hasMoreItems } from '@app/shared/rest/component-pagination.model' import { ComponentPagination, hasMoreItems } from '@app/shared/rest/component-pagination.model'
@ -22,6 +22,8 @@ export class VideoChannelPlaylistsComponent implements OnInit, OnDestroy {
totalItems: null totalItems: null
} }
onDataSubject = new Subject<any[]>()
private videoChannelSub: Subscription private videoChannelSub: Subscription
private videoChannel: VideoChannel private videoChannel: VideoChannel
@ -53,10 +55,12 @@ export class VideoChannelPlaylistsComponent implements OnInit, OnDestroy {
} }
private loadVideoPlaylists () { private loadVideoPlaylists () {
this.videoPlaylistService.listChannelPlaylists(this.videoChannel) this.videoPlaylistService.listChannelPlaylists(this.videoChannel, this.pagination)
.subscribe(res => { .subscribe(res => {
this.videoPlaylists = this.videoPlaylists.concat(res.data) this.videoPlaylists = this.videoPlaylists.concat(res.data)
this.pagination.totalItems = res.total this.pagination.totalItems = res.total
this.onDataSubject.next(res.data)
}) })
} }
} }

View File

@ -1,6 +1,6 @@
<div *ngIf="componentPagination.totalItems === 0" class="no-notification" i18n>You don't have notifications.</div> <div *ngIf="componentPagination.totalItems === 0" class="no-notification" i18n>You don't have notifications.</div>
<div class="notifications" myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()"> <div class="notifications" myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()">
<div *ngFor="let notification of notifications" class="notification" [ngClass]="{ unread: !notification.read }" (click)="markAsRead(notification)"> <div *ngFor="let notification of notifications" class="notification" [ngClass]="{ unread: !notification.read }" (click)="markAsRead(notification)">
<ng-container [ngSwitch]="notification.type"> <ng-container [ngSwitch]="notification.type">

View File

@ -4,6 +4,7 @@ import { UserNotificationType } from '../../../../../shared'
import { ComponentPagination, hasMoreItems } from '@app/shared/rest/component-pagination.model' import { ComponentPagination, hasMoreItems } from '@app/shared/rest/component-pagination.model'
import { Notifier } from '@app/core' import { Notifier } from '@app/core'
import { UserNotification } from '@app/shared/users/user-notification.model' import { UserNotification } from '@app/shared/users/user-notification.model'
import { Subject } from 'rxjs'
@Component({ @Component({
selector: 'my-user-notifications', selector: 'my-user-notifications',
@ -24,6 +25,8 @@ export class UserNotificationsComponent implements OnInit {
componentPagination: ComponentPagination componentPagination: ComponentPagination
onDataSubject = new Subject<any[]>()
constructor ( constructor (
private userNotificationService: UserNotificationService, private userNotificationService: UserNotificationService,
private notifier: Notifier private notifier: Notifier
@ -47,6 +50,8 @@ export class UserNotificationsComponent implements OnInit {
this.componentPagination.totalItems = result.total this.componentPagination.totalItems = result.total
this.notificationsLoaded.emit() this.notificationsLoaded.emit()
this.onDataSubject.next(result.data)
}, },
err => this.notifier.error(err.message) err => this.notifier.error(err.message)

View File

@ -83,7 +83,7 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit,
load () { load () {
forkJoin([ forkJoin([
this.videoPlaylistService.listAccountPlaylists(this.user.account, '-updatedAt'), this.videoPlaylistService.listAccountPlaylists(this.user.account, undefined,'-updatedAt'),
this.videoPlaylistService.doesVideoExistInPlaylist(this.video.id) this.videoPlaylistService.doesVideoExistInPlaylist(this.video.id)
]) ])
.subscribe( .subscribe(

View File

@ -45,21 +45,28 @@ export class VideoPlaylistService {
) )
} }
listChannelPlaylists (videoChannel: VideoChannel): Observable<ResultList<VideoPlaylist>> { listChannelPlaylists (videoChannel: VideoChannel, componentPagination: ComponentPagination): Observable<ResultList<VideoPlaylist>> {
const url = VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannel.nameWithHost + '/video-playlists' const url = VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannel.nameWithHost + '/video-playlists'
const pagination = this.restService.componentPaginationToRestPagination(componentPagination)
return this.authHttp.get<ResultList<VideoPlaylist>>(url) let params = new HttpParams()
params = this.restService.addRestGetParams(params, pagination)
return this.authHttp.get<ResultList<VideoPlaylist>>(url, { params })
.pipe( .pipe(
switchMap(res => this.extractPlaylists(res)), switchMap(res => this.extractPlaylists(res)),
catchError(err => this.restExtractor.handleError(err)) catchError(err => this.restExtractor.handleError(err))
) )
} }
listAccountPlaylists (account: Account, sort: string): Observable<ResultList<VideoPlaylist>> { listAccountPlaylists (account: Account, componentPagination: ComponentPagination, sort: string): Observable<ResultList<VideoPlaylist>> {
const url = AccountService.BASE_ACCOUNT_URL + account.nameWithHost + '/video-playlists' const url = AccountService.BASE_ACCOUNT_URL + account.nameWithHost + '/video-playlists'
const pagination = componentPagination
? this.restService.componentPaginationToRestPagination(componentPagination)
: undefined
let params = new HttpParams() let params = new HttpParams()
params = this.restService.addRestGetParams(params, undefined, sort) params = this.restService.addRestGetParams(params, pagination, sort)
return this.authHttp.get<ResultList<VideoPlaylist>>(url, { params }) return this.authHttp.get<ResultList<VideoPlaylist>>(url, { params })
.pipe( .pipe(

View File

@ -19,7 +19,7 @@
<div class="no-results" i18n *ngIf="pagination.totalItems === 0">No results.</div> <div class="no-results" i18n *ngIf="pagination.totalItems === 0">No results.</div>
<div <div
myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [autoInit]="true" myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [autoInit]="true" [dataObservable]="onDataSubject.asObservable()"
class="videos" class="videos"
> >
<ng-container *ngFor="let video of videos; trackBy: videoById;"> <ng-container *ngFor="let video of videos; trackBy: videoById;">

View File

@ -1,7 +1,7 @@
import { debounceTime, first, tap } from 'rxjs/operators' import { debounceTime, first, tap } from 'rxjs/operators'
import { OnDestroy, OnInit } from '@angular/core' import { OnDestroy, OnInit } from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router' import { ActivatedRoute, Router } from '@angular/router'
import { fromEvent, Observable, of, Subscription } from 'rxjs' import { fromEvent, Observable, of, Subject, Subscription } from 'rxjs'
import { AuthService } from '../../core/auth' import { AuthService } from '../../core/auth'
import { ComponentPagination } from '../rest/component-pagination.model' import { ComponentPagination } from '../rest/component-pagination.model'
import { VideoSortField } from './sort-field.type' import { VideoSortField } from './sort-field.type'
@ -59,6 +59,8 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableFor
blacklistInfo: false blacklistInfo: false
} }
onDataSubject = new Subject<any[]>()
protected abstract notifier: Notifier protected abstract notifier: Notifier
protected abstract authService: AuthService protected abstract authService: AuthService
protected abstract route: ActivatedRoute protected abstract route: ActivatedRoute
@ -147,6 +149,8 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableFor
if (this.groupByDate) this.buildGroupedDateLabels() if (this.groupByDate) this.buildGroupedDateLabels()
this.onMoreVideos() this.onMoreVideos()
this.onDataSubject.next(data)
}, },
error => this.notifier.error(error.message) error => this.notifier.error(error.message)

View File

@ -1,14 +1,15 @@
import { distinct, distinctUntilChanged, filter, map, share, startWith, throttleTime } from 'rxjs/operators' import { distinctUntilChanged, filter, map, share, startWith, tap, throttleTime } from 'rxjs/operators'
import { Directive, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core' import { AfterContentChecked, Directive, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'
import { fromEvent, Subscription } from 'rxjs' import { fromEvent, Observable, Subscription } from 'rxjs'
@Directive({ @Directive({
selector: '[myInfiniteScroller]' selector: '[myInfiniteScroller]'
}) })
export class InfiniteScrollerDirective implements OnInit, OnDestroy { export class InfiniteScrollerDirective implements OnInit, OnDestroy, AfterContentChecked {
@Input() percentLimit = 70 @Input() percentLimit = 70
@Input() autoInit = false @Input() autoInit = false
@Input() onItself = false @Input() onItself = false
@Input() dataObservable: Observable<any[]>
@Output() nearOfBottom = new EventEmitter<void>() @Output() nearOfBottom = new EventEmitter<void>()
@ -17,10 +18,22 @@ export class InfiniteScrollerDirective implements OnInit, OnDestroy {
private scrollDownSub: Subscription private scrollDownSub: Subscription
private container: HTMLElement private container: HTMLElement
private checkScroll = false
constructor (private el: ElementRef) { constructor (private el: ElementRef) {
this.decimalLimit = this.percentLimit / 100 this.decimalLimit = this.percentLimit / 100
} }
ngAfterContentChecked () {
if (this.checkScroll) {
this.checkScroll = false
console.log('Checking if the initial state has a scroll.')
if (this.hasScroll() === false) this.nearOfBottom.emit()
}
}
ngOnInit () { ngOnInit () {
if (this.autoInit === true) return this.initialize() if (this.autoInit === true) return this.initialize()
} }
@ -30,14 +43,15 @@ export class InfiniteScrollerDirective implements OnInit, OnDestroy {
} }
initialize () { initialize () {
if (this.onItself) { this.container = this.onItself
this.container = this.el.nativeElement ? this.el.nativeElement
} : document.documentElement
// Emit the last value // Emit the last value
const throttleOptions = { leading: true, trailing: true } const throttleOptions = { leading: true, trailing: true }
const scrollObservable = fromEvent(this.container || window, 'scroll') const scrollableElement = this.onItself ? this.container : window
const scrollObservable = fromEvent(scrollableElement, 'scroll')
.pipe( .pipe(
startWith(null as string), // FIXME: typings startWith(null as string), // FIXME: typings
throttleTime(200, undefined, throttleOptions), throttleTime(200, undefined, throttleOptions),
@ -49,23 +63,34 @@ export class InfiniteScrollerDirective implements OnInit, OnDestroy {
// Scroll Down // Scroll Down
this.scrollDownSub = scrollObservable this.scrollDownSub = scrollObservable
.pipe( .pipe(
// Check we scroll down filter(({ current }) => this.isScrollingDown(current)),
filter(({ current }) => { filter(({ current, maximumScroll }) => (current / maximumScroll) > this.decimalLimit)
const res = this.lastCurrentBottom < current
this.lastCurrentBottom = current
return res
}),
filter(({ current, maximumScroll }) => maximumScroll <= 0 || (current / maximumScroll) > this.decimalLimit)
) )
.subscribe(() => this.nearOfBottom.emit()) .subscribe(() => this.nearOfBottom.emit())
if (this.dataObservable) {
this.dataObservable
.pipe(filter(d => d.length !== 0))
.subscribe(() => this.checkScroll = true)
}
} }
private getScrollInfo () { private getScrollInfo () {
if (this.container) { return { current: this.container.scrollTop, maximumScroll: this.getMaximumScroll() }
return { current: this.container.scrollTop, maximumScroll: this.container.scrollHeight }
} }
return { current: window.scrollY, maximumScroll: document.body.clientHeight - window.innerHeight } private getMaximumScroll () {
return this.container.scrollHeight - window.innerHeight
}
private hasScroll () {
return this.getMaximumScroll() > 0
}
private isScrollingDown (current: number) {
const result = this.lastCurrentBottom < current
this.lastCurrentBottom = current
return result
} }
} }

View File

@ -1,6 +1,6 @@
<div class="no-results" i18n *ngIf="pagination.totalItems === 0">No results.</div> <div class="no-results" i18n *ngIf="pagination.totalItems === 0">No results.</div>
<div myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()" class="videos"> <div myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()" class="videos">
<div class="video" *ngFor="let video of videos; let i = index; trackBy: videoById"> <div class="video" *ngFor="let video of videos; let i = index; trackBy: videoById">
<div class="checkbox-container"> <div class="checkbox-container">

View File

@ -21,6 +21,7 @@
myInfiniteScroller myInfiniteScroller
[autoInit]="true" [autoInit]="true"
(nearOfBottom)="onNearOfBottom()" (nearOfBottom)="onNearOfBottom()"
[dataObservable]="onDataSubject.asObservable()"
> >
<div #commentHighlightBlock id="highlighted-comment"> <div #commentHighlightBlock id="highlighted-comment">
<my-video-comment <my-video-comment

View File

@ -1,7 +1,7 @@
import { Component, ElementRef, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewChild } from '@angular/core' import { Component, ElementRef, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewChild } from '@angular/core'
import { ActivatedRoute } from '@angular/router' import { ActivatedRoute } from '@angular/router'
import { ConfirmService, Notifier } from '@app/core' import { ConfirmService, Notifier } from '@app/core'
import { Subscription } from 'rxjs' import { Subject, Subscription } from 'rxjs'
import { VideoCommentThreadTree } from '../../../../../../shared/models/videos/video-comment.model' import { VideoCommentThreadTree } from '../../../../../../shared/models/videos/video-comment.model'
import { AuthService } from '../../../core/auth' import { AuthService } from '../../../core/auth'
import { ComponentPagination, hasMoreItems } from '../../../shared/rest/component-pagination.model' import { ComponentPagination, hasMoreItems } from '../../../shared/rest/component-pagination.model'
@ -38,6 +38,8 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
syndicationItems: Syndication[] = [] syndicationItems: Syndication[] = []
onDataSubject = new Subject<any[]>()
private sub: Subscription private sub: Subscription
constructor ( constructor (
@ -124,6 +126,8 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
res => { res => {
this.comments = this.comments.concat(res.data) this.comments = this.comments.concat(res.data)
this.componentPagination.totalItems = res.total this.componentPagination.totalItems = res.total
this.onDataSubject.next(res.data)
}, },
err => this.notifier.error(err.message) err => this.notifier.error(err.message)