diff --git a/client/package.json b/client/package.json
index 310860fec..45f555f29 100644
--- a/client/package.json
+++ b/client/package.json
@@ -71,6 +71,7 @@
"ngc-webpack": "3.2.2",
"ngx-bootstrap": "2.0.0-beta.9",
"ngx-chips": "1.5.3",
+ "ngx-infinite-scroll": "^0.7.0",
"ngx-pipes": "^2.0.5",
"node-sass": "^4.1.1",
"normalize.css": "^7.0.0",
diff --git a/client/src/app/app-routing.module.ts b/client/src/app/app-routing.module.ts
index 0f9484344..fe72c9181 100644
--- a/client/src/app/app-routing.module.ts
+++ b/client/src/app/app-routing.module.ts
@@ -6,7 +6,7 @@ import { PreloadSelectedModulesList } from './core'
const routes: Routes = [
{
path: '',
- redirectTo: '/videos/list',
+ redirectTo: '/videos/trending',
pathMatch: 'full'
},
{
diff --git a/client/src/app/shared/misc/from-now.pipe.ts b/client/src/app/shared/misc/from-now.pipe.ts
index 25e5d6a85..80dab02ba 100644
--- a/client/src/app/shared/misc/from-now.pipe.ts
+++ b/client/src/app/shared/misc/from-now.pipe.ts
@@ -1,6 +1,6 @@
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'})
export class FromNowPipe implements PipeTransform {
diff --git a/client/src/app/shared/search/search.component.ts b/client/src/app/shared/search/search.component.ts
index 6ef19c97a..f49ecc8ad 100644
--- a/client/src/app/shared/search/search.component.ts
+++ b/client/src/app/shared/search/search.component.ts
@@ -1,8 +1,6 @@
import { Component, OnInit } from '@angular/core'
import { Router } from '@angular/router'
-
import { Search } from './search.model'
-import { SearchField } from './search-field.type'
import { SearchService } from './search.service'
@Component({
@@ -12,12 +10,6 @@ import { SearchService } from './search.service'
})
export class SearchComponent implements OnInit {
- fieldChoices = {
- name: 'Name',
- account: 'Account',
- host: 'Host',
- tags: 'Tags'
- }
searchCriteria: Search = {
field: 'name',
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 () {
- if (this.router.url.indexOf('/videos/list') === -1) {
- this.router.navigate([ '/videos/list' ])
- }
+ // if (this.router.url.indexOf('/videos/list') === -1) {
+ // this.router.navigate([ '/videos/list' ])
+ // }
this.searchService.searchUpdated.next(this.searchCriteria)
}
-
- getStringChoice (choiceKey: SearchField) {
- return this.fieldChoices[choiceKey]
- }
}
diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts
index c7ea6e603..7618748e9 100644
--- a/client/src/app/shared/shared.module.ts
+++ b/client/src/app/shared/shared.module.ts
@@ -6,21 +6,20 @@ import { RouterModule } from '@angular/router'
import { BsDropdownModule } from 'ngx-bootstrap/dropdown'
import { ModalModule } from 'ngx-bootstrap/modal'
-import { PaginationModule } from 'ngx-bootstrap/pagination'
import { ProgressbarModule } from 'ngx-bootstrap/progressbar'
import { BytesPipe, KeysPipe } from 'ngx-pipes'
import { SharedModule as PrimeSharedModule } from 'primeng/components/common/shared'
import { DataTableModule } from 'primeng/components/datatable/datatable'
import { AUTH_INTERCEPTOR_PROVIDER } from './auth'
+import { FromNowPipe } from './misc/from-now.pipe'
import { LoaderComponent } from './misc/loader.component'
+import { NumberFormatterPipe } from './misc/number-formatter.pipe'
import { RestExtractor, RestService } from './rest'
import { SearchComponent, SearchService } from './search'
import { UserService } from './users'
import { VideoAbuseService } from './video-abuse'
import { VideoBlacklistService } from './video-blacklist'
-import { NumberFormatterPipe } from './misc/number-formatter.pipe'
-import { FromNowPipe } from './misc/from-now.pipe'
@NgModule({
imports: [
@@ -32,7 +31,6 @@ import { FromNowPipe } from './misc/from-now.pipe'
BsDropdownModule.forRoot(),
ModalModule.forRoot(),
- PaginationModule.forRoot(),
ProgressbarModule.forRoot(),
DataTableModule,
@@ -57,7 +55,6 @@ import { FromNowPipe } from './misc/from-now.pipe'
BsDropdownModule,
ModalModule,
- PaginationModule,
ProgressbarModule,
DataTableModule,
PrimeSharedModule,
diff --git a/client/src/app/videos/video-list/shared/abstract-video-list.html b/client/src/app/videos/video-list/shared/abstract-video-list.html
index ab5530e68..69e16319e 100644
--- a/client/src/app/videos/video-list/shared/abstract-video-list.html
+++ b/client/src/app/videos/video-list/shared/abstract-video-list.html
@@ -2,15 +2,17 @@
{{ titlePage }}
-
+
-
-
diff --git a/client/src/app/videos/video-list/shared/abstract-video-list.ts b/client/src/app/videos/video-list/shared/abstract-video-list.ts
index 262ea4e21..44cdc1d9f 100644
--- a/client/src/app/videos/video-list/shared/abstract-video-list.ts
+++ b/client/src/app/videos/video-list/shared/abstract-video-list.ts
@@ -19,44 +19,77 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy {
protected notificationsService: NotificationsService
protected router: Router
protected route: ActivatedRoute
-
protected subActivatedRoute: Subscription
+ protected abstract currentRoute: string
+
abstract titlePage: string
+ private loadedPages: { [ id: number ]: boolean } = {}
+
abstract getVideosObservable (): Observable<{ videos: Video[], totalVideos: number}>
ngOnInit () {
// Subscribe to route changes
- this.subActivatedRoute = this.route.params.subscribe(routeParams => {
- this.loadRouteParams(routeParams)
-
- this.getVideos()
- })
+ const routeParams = this.route.snapshot.params
+ this.loadRouteParams(routeParams)
+ this.loadMoreVideos('after')
}
ngOnDestroy () {
this.subActivatedRoute.unsubscribe()
}
- getVideos () {
- this.videos = []
+ onNearOfTop () {
+ 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()
observable.subscribe(
({ videos, totalVideos }) => {
- this.videos = videos
+ this.loadedPages[this.pagination.currentPage] = true
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)
)
}
- onPageChanged (event: { page: number }) {
- // Be sure the current page is set
- this.pagination.currentPage = event.page
+ protected hasMoreVideos () {
+ if (!this.pagination.totalItems) return true
- 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 () {
@@ -79,8 +112,8 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy {
}
}
- protected navigateToNewParams () {
+ protected setNewRouteParams () {
const routeParams = this.buildRouteParams()
- this.router.navigate([ '/videos/list', routeParams ])
+ this.router.navigate([ this.currentRoute, routeParams ])
}
}
diff --git a/client/src/app/videos/video-list/video-recently-added.component.ts b/client/src/app/videos/video-list/video-recently-added.component.ts
index dbba264df..9bf69bd78 100644
--- a/client/src/app/videos/video-list/video-recently-added.component.ts
+++ b/client/src/app/videos/video-list/video-recently-added.component.ts
@@ -11,6 +11,7 @@ import { AbstractVideoList } from './shared'
})
export class VideoRecentlyAddedComponent extends AbstractVideoList implements OnInit, OnDestroy {
titlePage = 'Recently added'
+ currentRoute = '/videos/recently-added'
constructor (protected router: Router,
protected route: ActivatedRoute,
diff --git a/client/src/app/videos/video-list/video-trending.component.ts b/client/src/app/videos/video-list/video-trending.component.ts
index b97966c12..a1df68711 100644
--- a/client/src/app/videos/video-list/video-trending.component.ts
+++ b/client/src/app/videos/video-list/video-trending.component.ts
@@ -11,6 +11,7 @@ import { AbstractVideoList } from './shared'
})
export class VideoTrendingComponent extends AbstractVideoList implements OnInit, OnDestroy {
titlePage = 'Trending'
+ currentRoute = '/videos/trending'
constructor (protected router: Router,
protected route: ActivatedRoute,
diff --git a/client/src/app/videos/videos.module.ts b/client/src/app/videos/videos.module.ts
index 93193000c..f3981d275 100644
--- a/client/src/app/videos/videos.module.ts
+++ b/client/src/app/videos/videos.module.ts
@@ -1,4 +1,5 @@
import { NgModule } from '@angular/core'
+import { InfiniteScrollModule } from 'ngx-infinite-scroll'
import { SharedModule } from '../shared'
import { VideoService } from './shared'
import { MyVideosComponent, VideoMiniatureComponent } from './video-list'
@@ -10,7 +11,8 @@ import { VideosComponent } from './videos.component'
@NgModule({
imports: [
VideosRoutingModule,
- SharedModule
+ SharedModule,
+ InfiniteScrollModule
],
declarations: [
diff --git a/client/yarn.lock b/client/yarn.lock
index fa1802a29..bd6870061 100644
--- a/client/yarn.lock
+++ b/client/yarn.lock
@@ -4714,6 +4714,10 @@ ngx-chips@1.5.3:
dependencies:
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:
version "2.0.5"
resolved "https://registry.yarnpkg.com/ngx-pipes/-/ngx-pipes-2.0.5.tgz#743b827e350b1e66f5bdae49e90a02fa631d4c54"