Add auto scroll to videos list

pull/159/head
Chocobozzz 2017-12-01 16:17:32 +01:00
parent 9bf9d2a5c2
commit 2bbb34127f
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
11 changed files with 73 additions and 59 deletions

View File

@ -71,6 +71,7 @@
"ngc-webpack": "3.2.2", "ngc-webpack": "3.2.2",
"ngx-bootstrap": "2.0.0-beta.9", "ngx-bootstrap": "2.0.0-beta.9",
"ngx-chips": "1.5.3", "ngx-chips": "1.5.3",
"ngx-infinite-scroll": "^0.7.0",
"ngx-pipes": "^2.0.5", "ngx-pipes": "^2.0.5",
"node-sass": "^4.1.1", "node-sass": "^4.1.1",
"normalize.css": "^7.0.0", "normalize.css": "^7.0.0",

View File

@ -6,7 +6,7 @@ import { PreloadSelectedModulesList } from './core'
const routes: Routes = [ const routes: Routes = [
{ {
path: '', path: '',
redirectTo: '/videos/list', redirectTo: '/videos/trending',
pathMatch: 'full' pathMatch: 'full'
}, },
{ {

View File

@ -1,6 +1,6 @@
import { Pipe, PipeTransform } from '@angular/core' import { Pipe, PipeTransform } from '@angular/core'
// Thanks: https://github.com/danrevah/ngx-pipes/blob/master/src/pipes/math/bytes.ts // Thanks: https://stackoverflow.com/questions/3177836/how-to-format-time-since-xxx-e-g-4-minutes-ago-similar-to-stack-exchange-site
@Pipe({name: 'fromNow'}) @Pipe({name: 'fromNow'})
export class FromNowPipe implements PipeTransform { export class FromNowPipe implements PipeTransform {

View File

@ -1,8 +1,6 @@
import { Component, OnInit } from '@angular/core' import { Component, OnInit } from '@angular/core'
import { Router } from '@angular/router' import { Router } from '@angular/router'
import { Search } from './search.model' import { Search } from './search.model'
import { SearchField } from './search-field.type'
import { SearchService } from './search.service' import { SearchService } from './search.service'
@Component({ @Component({
@ -12,12 +10,6 @@ import { SearchService } from './search.service'
}) })
export class SearchComponent implements OnInit { export class SearchComponent implements OnInit {
fieldChoices = {
name: 'Name',
account: 'Account',
host: 'Host',
tags: 'Tags'
}
searchCriteria: Search = { searchCriteria: Search = {
field: 'name', field: 'name',
value: '' value: ''
@ -40,30 +32,11 @@ export class SearchComponent implements OnInit {
) )
} }
get choiceKeys () {
return Object.keys(this.fieldChoices)
}
choose ($event: MouseEvent, choice: SearchField) {
$event.preventDefault()
$event.stopPropagation()
this.searchCriteria.field = choice
if (this.searchCriteria.value) {
this.doSearch()
}
}
doSearch () { doSearch () {
if (this.router.url.indexOf('/videos/list') === -1) { // if (this.router.url.indexOf('/videos/list') === -1) {
this.router.navigate([ '/videos/list' ]) // this.router.navigate([ '/videos/list' ])
} // }
this.searchService.searchUpdated.next(this.searchCriteria) this.searchService.searchUpdated.next(this.searchCriteria)
} }
getStringChoice (choiceKey: SearchField) {
return this.fieldChoices[choiceKey]
}
} }

View File

@ -6,21 +6,20 @@ import { RouterModule } from '@angular/router'
import { BsDropdownModule } from 'ngx-bootstrap/dropdown' import { BsDropdownModule } from 'ngx-bootstrap/dropdown'
import { ModalModule } from 'ngx-bootstrap/modal' import { ModalModule } from 'ngx-bootstrap/modal'
import { PaginationModule } from 'ngx-bootstrap/pagination'
import { ProgressbarModule } from 'ngx-bootstrap/progressbar' import { ProgressbarModule } from 'ngx-bootstrap/progressbar'
import { BytesPipe, KeysPipe } from 'ngx-pipes' import { BytesPipe, KeysPipe } from 'ngx-pipes'
import { SharedModule as PrimeSharedModule } from 'primeng/components/common/shared' import { SharedModule as PrimeSharedModule } from 'primeng/components/common/shared'
import { DataTableModule } from 'primeng/components/datatable/datatable' import { DataTableModule } from 'primeng/components/datatable/datatable'
import { AUTH_INTERCEPTOR_PROVIDER } from './auth' import { AUTH_INTERCEPTOR_PROVIDER } from './auth'
import { FromNowPipe } from './misc/from-now.pipe'
import { LoaderComponent } from './misc/loader.component' import { LoaderComponent } from './misc/loader.component'
import { NumberFormatterPipe } from './misc/number-formatter.pipe'
import { RestExtractor, RestService } from './rest' import { RestExtractor, RestService } from './rest'
import { SearchComponent, SearchService } from './search' import { SearchComponent, SearchService } from './search'
import { UserService } from './users' import { UserService } from './users'
import { VideoAbuseService } from './video-abuse' import { VideoAbuseService } from './video-abuse'
import { VideoBlacklistService } from './video-blacklist' import { VideoBlacklistService } from './video-blacklist'
import { NumberFormatterPipe } from './misc/number-formatter.pipe'
import { FromNowPipe } from './misc/from-now.pipe'
@NgModule({ @NgModule({
imports: [ imports: [
@ -32,7 +31,6 @@ import { FromNowPipe } from './misc/from-now.pipe'
BsDropdownModule.forRoot(), BsDropdownModule.forRoot(),
ModalModule.forRoot(), ModalModule.forRoot(),
PaginationModule.forRoot(),
ProgressbarModule.forRoot(), ProgressbarModule.forRoot(),
DataTableModule, DataTableModule,
@ -57,7 +55,6 @@ import { FromNowPipe } from './misc/from-now.pipe'
BsDropdownModule, BsDropdownModule,
ModalModule, ModalModule,
PaginationModule,
ProgressbarModule, ProgressbarModule,
DataTableModule, DataTableModule,
PrimeSharedModule, PrimeSharedModule,

View File

@ -2,15 +2,17 @@
{{ titlePage }} {{ titlePage }}
</div> </div>
<div class="videos-miniatures"> <div
class="videos-miniatures"
infiniteScroll
[infiniteScrollUpDistance]="1.5"
[infiniteScrollDistance]="0.5"
(scrolled)="onNearOfBottom()"
(scrolledUp)="onNearOfTop()"
>
<my-video-miniature <my-video-miniature
class="ng-animate" class="ng-animate"
*ngFor="let video of videos" [video]="video" [user]="user" [currentSort]="sort" *ngFor="let video of videos" [video]="video" [user]="user" [currentSort]="sort"
> >
</my-video-miniature> </my-video-miniature>
</div> </div>
<pagination *ngIf="pagination.totalItems !== null && pagination.totalItems !== 0"
[totalItems]="pagination.totalItems" [itemsPerPage]="pagination.itemsPerPage" [maxSize]="6" [boundaryLinks]="true" [rotate]="false"
[(ngModel)]="pagination.currentPage" (pageChanged)="onPageChanged($event)"
></pagination>

View File

@ -19,44 +19,77 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy {
protected notificationsService: NotificationsService protected notificationsService: NotificationsService
protected router: Router protected router: Router
protected route: ActivatedRoute protected route: ActivatedRoute
protected subActivatedRoute: Subscription protected subActivatedRoute: Subscription
protected abstract currentRoute: string
abstract titlePage: string abstract titlePage: string
private loadedPages: { [ id: number ]: boolean } = {}
abstract getVideosObservable (): Observable<{ videos: Video[], totalVideos: number}> abstract getVideosObservable (): Observable<{ videos: Video[], totalVideos: number}>
ngOnInit () { ngOnInit () {
// Subscribe to route changes // Subscribe to route changes
this.subActivatedRoute = this.route.params.subscribe(routeParams => { const routeParams = this.route.snapshot.params
this.loadRouteParams(routeParams) this.loadRouteParams(routeParams)
this.loadMoreVideos('after')
this.getVideos()
})
} }
ngOnDestroy () { ngOnDestroy () {
this.subActivatedRoute.unsubscribe() this.subActivatedRoute.unsubscribe()
} }
getVideos () { onNearOfTop () {
this.videos = [] if (this.pagination.currentPage > 1) {
this.previousPage()
}
}
onNearOfBottom () {
if (this.hasMoreVideos()) {
this.nextPage()
}
}
loadMoreVideos (where: 'before' | 'after') {
if (this.loadedPages[this.pagination.currentPage] === true) return
const observable = this.getVideosObservable() const observable = this.getVideosObservable()
observable.subscribe( observable.subscribe(
({ videos, totalVideos }) => { ({ videos, totalVideos }) => {
this.videos = videos this.loadedPages[this.pagination.currentPage] = true
this.pagination.totalItems = totalVideos this.pagination.totalItems = totalVideos
if (where === 'before') {
this.videos = videos.concat(this.videos)
} else {
this.videos = this.videos.concat(videos)
}
}, },
error => this.notificationsService.error('Error', error.text) error => this.notificationsService.error('Error', error.text)
) )
} }
onPageChanged (event: { page: number }) { protected hasMoreVideos () {
// Be sure the current page is set if (!this.pagination.totalItems) return true
this.pagination.currentPage = event.page
this.navigateToNewParams() const maxPage = this.pagination.totalItems/this.pagination.itemsPerPage
return maxPage > this.pagination.currentPage
}
protected previousPage () {
this.pagination.currentPage--
this.setNewRouteParams()
this.loadMoreVideos('before')
}
protected nextPage () {
this.pagination.currentPage++
this.setNewRouteParams()
this.loadMoreVideos('after')
} }
protected buildRouteParams () { protected buildRouteParams () {
@ -79,8 +112,8 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy {
} }
} }
protected navigateToNewParams () { protected setNewRouteParams () {
const routeParams = this.buildRouteParams() const routeParams = this.buildRouteParams()
this.router.navigate([ '/videos/list', routeParams ]) this.router.navigate([ this.currentRoute, routeParams ])
} }
} }

View File

@ -11,6 +11,7 @@ import { AbstractVideoList } from './shared'
}) })
export class VideoRecentlyAddedComponent extends AbstractVideoList implements OnInit, OnDestroy { export class VideoRecentlyAddedComponent extends AbstractVideoList implements OnInit, OnDestroy {
titlePage = 'Recently added' titlePage = 'Recently added'
currentRoute = '/videos/recently-added'
constructor (protected router: Router, constructor (protected router: Router,
protected route: ActivatedRoute, protected route: ActivatedRoute,

View File

@ -11,6 +11,7 @@ import { AbstractVideoList } from './shared'
}) })
export class VideoTrendingComponent extends AbstractVideoList implements OnInit, OnDestroy { export class VideoTrendingComponent extends AbstractVideoList implements OnInit, OnDestroy {
titlePage = 'Trending' titlePage = 'Trending'
currentRoute = '/videos/trending'
constructor (protected router: Router, constructor (protected router: Router,
protected route: ActivatedRoute, protected route: ActivatedRoute,

View File

@ -1,4 +1,5 @@
import { NgModule } from '@angular/core' import { NgModule } from '@angular/core'
import { InfiniteScrollModule } from 'ngx-infinite-scroll'
import { SharedModule } from '../shared' import { SharedModule } from '../shared'
import { VideoService } from './shared' import { VideoService } from './shared'
import { MyVideosComponent, VideoMiniatureComponent } from './video-list' import { MyVideosComponent, VideoMiniatureComponent } from './video-list'
@ -10,7 +11,8 @@ import { VideosComponent } from './videos.component'
@NgModule({ @NgModule({
imports: [ imports: [
VideosRoutingModule, VideosRoutingModule,
SharedModule SharedModule,
InfiniteScrollModule
], ],
declarations: [ declarations: [

View File

@ -4714,6 +4714,10 @@ ngx-chips@1.5.3:
dependencies: dependencies:
ng2-material-dropdown "0.7.10" ng2-material-dropdown "0.7.10"
ngx-infinite-scroll@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/ngx-infinite-scroll/-/ngx-infinite-scroll-0.7.0.tgz#a390c61c6a05ac14485e1c5bc8b4e6f6bd62fd6a"
ngx-pipes@^2.0.5: ngx-pipes@^2.0.5:
version "2.0.5" version "2.0.5"
resolved "https://registry.yarnpkg.com/ngx-pipes/-/ngx-pipes-2.0.5.tgz#743b827e350b1e66f5bdae49e90a02fa631d4c54" resolved "https://registry.yarnpkg.com/ngx-pipes/-/ngx-pipes-2.0.5.tgz#743b827e350b1e66f5bdae49e90a02fa631d4c54"