diff options
author | Chocobozzz <florian.bigard@gmail.com> | 2017-12-01 16:17:32 +0100 |
---|---|---|
committer | Chocobozzz <florian.bigard@gmail.com> | 2017-12-01 16:17:32 +0100 |
commit | 2bbb34127fccd187ed690949b6791e49fdd77194 (patch) | |
tree | d9ebcc72ab5e1d439f08e73881dc62baecb3bc36 | |
parent | 9bf9d2a5c223bf006496ae7adf0c0bd7a7975108 (diff) | |
download | PeerTube-2bbb34127fccd187ed690949b6791e49fdd77194.tar.gz PeerTube-2bbb34127fccd187ed690949b6791e49fdd77194.tar.zst PeerTube-2bbb34127fccd187ed690949b6791e49fdd77194.zip |
Add auto scroll to videos list
-rw-r--r-- | client/package.json | 1 | ||||
-rw-r--r-- | client/src/app/app-routing.module.ts | 2 | ||||
-rw-r--r-- | client/src/app/shared/misc/from-now.pipe.ts | 2 | ||||
-rw-r--r-- | client/src/app/shared/search/search.component.ts | 33 | ||||
-rw-r--r-- | client/src/app/shared/shared.module.ts | 7 | ||||
-rw-r--r-- | client/src/app/videos/video-list/shared/abstract-video-list.html | 14 | ||||
-rw-r--r-- | client/src/app/videos/video-list/shared/abstract-video-list.ts | 63 | ||||
-rw-r--r-- | client/src/app/videos/video-list/video-recently-added.component.ts | 1 | ||||
-rw-r--r-- | client/src/app/videos/video-list/video-trending.component.ts | 1 | ||||
-rw-r--r-- | client/src/app/videos/videos.module.ts | 4 | ||||
-rw-r--r-- | client/yarn.lock | 4 |
11 files changed, 73 insertions, 59 deletions
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 @@ | |||
71 | "ngc-webpack": "3.2.2", | 71 | "ngc-webpack": "3.2.2", |
72 | "ngx-bootstrap": "2.0.0-beta.9", | 72 | "ngx-bootstrap": "2.0.0-beta.9", |
73 | "ngx-chips": "1.5.3", | 73 | "ngx-chips": "1.5.3", |
74 | "ngx-infinite-scroll": "^0.7.0", | ||
74 | "ngx-pipes": "^2.0.5", | 75 | "ngx-pipes": "^2.0.5", |
75 | "node-sass": "^4.1.1", | 76 | "node-sass": "^4.1.1", |
76 | "normalize.css": "^7.0.0", | 77 | "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' | |||
6 | const routes: Routes = [ | 6 | const routes: Routes = [ |
7 | { | 7 | { |
8 | path: '', | 8 | path: '', |
9 | redirectTo: '/videos/list', | 9 | redirectTo: '/videos/trending', |
10 | pathMatch: 'full' | 10 | pathMatch: 'full' |
11 | }, | 11 | }, |
12 | { | 12 | { |
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 @@ | |||
1 | import { Pipe, PipeTransform } from '@angular/core' | 1 | import { Pipe, PipeTransform } from '@angular/core' |
2 | 2 | ||
3 | // Thanks: https://github.com/danrevah/ngx-pipes/blob/master/src/pipes/math/bytes.ts | 3 | // Thanks: https://stackoverflow.com/questions/3177836/how-to-format-time-since-xxx-e-g-4-minutes-ago-similar-to-stack-exchange-site |
4 | 4 | ||
5 | @Pipe({name: 'fromNow'}) | 5 | @Pipe({name: 'fromNow'}) |
6 | export class FromNowPipe implements PipeTransform { | 6 | 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 @@ | |||
1 | import { Component, OnInit } from '@angular/core' | 1 | import { Component, OnInit } from '@angular/core' |
2 | import { Router } from '@angular/router' | 2 | import { Router } from '@angular/router' |
3 | |||
4 | import { Search } from './search.model' | 3 | import { Search } from './search.model' |
5 | import { SearchField } from './search-field.type' | ||
6 | import { SearchService } from './search.service' | 4 | import { SearchService } from './search.service' |
7 | 5 | ||
8 | @Component({ | 6 | @Component({ |
@@ -12,12 +10,6 @@ import { SearchService } from './search.service' | |||
12 | }) | 10 | }) |
13 | 11 | ||
14 | export class SearchComponent implements OnInit { | 12 | export class SearchComponent implements OnInit { |
15 | fieldChoices = { | ||
16 | name: 'Name', | ||
17 | account: 'Account', | ||
18 | host: 'Host', | ||
19 | tags: 'Tags' | ||
20 | } | ||
21 | searchCriteria: Search = { | 13 | searchCriteria: Search = { |
22 | field: 'name', | 14 | field: 'name', |
23 | value: '' | 15 | value: '' |
@@ -40,30 +32,11 @@ export class SearchComponent implements OnInit { | |||
40 | ) | 32 | ) |
41 | } | 33 | } |
42 | 34 | ||
43 | get choiceKeys () { | ||
44 | return Object.keys(this.fieldChoices) | ||
45 | } | ||
46 | |||
47 | choose ($event: MouseEvent, choice: SearchField) { | ||
48 | $event.preventDefault() | ||
49 | $event.stopPropagation() | ||
50 | |||
51 | this.searchCriteria.field = choice | ||
52 | |||
53 | if (this.searchCriteria.value) { | ||
54 | this.doSearch() | ||
55 | } | ||
56 | } | ||
57 | |||
58 | doSearch () { | 35 | doSearch () { |
59 | if (this.router.url.indexOf('/videos/list') === -1) { | 36 | // if (this.router.url.indexOf('/videos/list') === -1) { |
60 | this.router.navigate([ '/videos/list' ]) | 37 | // this.router.navigate([ '/videos/list' ]) |
61 | } | 38 | // } |
62 | 39 | ||
63 | this.searchService.searchUpdated.next(this.searchCriteria) | 40 | this.searchService.searchUpdated.next(this.searchCriteria) |
64 | } | 41 | } |
65 | |||
66 | getStringChoice (choiceKey: SearchField) { | ||
67 | return this.fieldChoices[choiceKey] | ||
68 | } | ||
69 | } | 42 | } |
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' | |||
6 | 6 | ||
7 | import { BsDropdownModule } from 'ngx-bootstrap/dropdown' | 7 | import { BsDropdownModule } from 'ngx-bootstrap/dropdown' |
8 | import { ModalModule } from 'ngx-bootstrap/modal' | 8 | import { ModalModule } from 'ngx-bootstrap/modal' |
9 | import { PaginationModule } from 'ngx-bootstrap/pagination' | ||
10 | import { ProgressbarModule } from 'ngx-bootstrap/progressbar' | 9 | import { ProgressbarModule } from 'ngx-bootstrap/progressbar' |
11 | import { BytesPipe, KeysPipe } from 'ngx-pipes' | 10 | import { BytesPipe, KeysPipe } from 'ngx-pipes' |
12 | import { SharedModule as PrimeSharedModule } from 'primeng/components/common/shared' | 11 | import { SharedModule as PrimeSharedModule } from 'primeng/components/common/shared' |
13 | import { DataTableModule } from 'primeng/components/datatable/datatable' | 12 | import { DataTableModule } from 'primeng/components/datatable/datatable' |
14 | 13 | ||
15 | import { AUTH_INTERCEPTOR_PROVIDER } from './auth' | 14 | import { AUTH_INTERCEPTOR_PROVIDER } from './auth' |
15 | import { FromNowPipe } from './misc/from-now.pipe' | ||
16 | import { LoaderComponent } from './misc/loader.component' | 16 | import { LoaderComponent } from './misc/loader.component' |
17 | import { NumberFormatterPipe } from './misc/number-formatter.pipe' | ||
17 | import { RestExtractor, RestService } from './rest' | 18 | import { RestExtractor, RestService } from './rest' |
18 | import { SearchComponent, SearchService } from './search' | 19 | import { SearchComponent, SearchService } from './search' |
19 | import { UserService } from './users' | 20 | import { UserService } from './users' |
20 | import { VideoAbuseService } from './video-abuse' | 21 | import { VideoAbuseService } from './video-abuse' |
21 | import { VideoBlacklistService } from './video-blacklist' | 22 | import { VideoBlacklistService } from './video-blacklist' |
22 | import { NumberFormatterPipe } from './misc/number-formatter.pipe' | ||
23 | import { FromNowPipe } from './misc/from-now.pipe' | ||
24 | 23 | ||
25 | @NgModule({ | 24 | @NgModule({ |
26 | imports: [ | 25 | imports: [ |
@@ -32,7 +31,6 @@ import { FromNowPipe } from './misc/from-now.pipe' | |||
32 | 31 | ||
33 | BsDropdownModule.forRoot(), | 32 | BsDropdownModule.forRoot(), |
34 | ModalModule.forRoot(), | 33 | ModalModule.forRoot(), |
35 | PaginationModule.forRoot(), | ||
36 | ProgressbarModule.forRoot(), | 34 | ProgressbarModule.forRoot(), |
37 | 35 | ||
38 | DataTableModule, | 36 | DataTableModule, |
@@ -57,7 +55,6 @@ import { FromNowPipe } from './misc/from-now.pipe' | |||
57 | 55 | ||
58 | BsDropdownModule, | 56 | BsDropdownModule, |
59 | ModalModule, | 57 | ModalModule, |
60 | PaginationModule, | ||
61 | ProgressbarModule, | 58 | ProgressbarModule, |
62 | DataTableModule, | 59 | DataTableModule, |
63 | PrimeSharedModule, | 60 | 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 @@ | |||
2 | {{ titlePage }} | 2 | {{ titlePage }} |
3 | </div> | 3 | </div> |
4 | 4 | ||
5 | <div class="videos-miniatures"> | 5 | <div |
6 | class="videos-miniatures" | ||
7 | infiniteScroll | ||
8 | [infiniteScrollUpDistance]="1.5" | ||
9 | [infiniteScrollDistance]="0.5" | ||
10 | (scrolled)="onNearOfBottom()" | ||
11 | (scrolledUp)="onNearOfTop()" | ||
12 | > | ||
6 | <my-video-miniature | 13 | <my-video-miniature |
7 | class="ng-animate" | 14 | class="ng-animate" |
8 | *ngFor="let video of videos" [video]="video" [user]="user" [currentSort]="sort" | 15 | *ngFor="let video of videos" [video]="video" [user]="user" [currentSort]="sort" |
9 | > | 16 | > |
10 | </my-video-miniature> | 17 | </my-video-miniature> |
11 | </div> | 18 | </div> |
12 | |||
13 | <pagination *ngIf="pagination.totalItems !== null && pagination.totalItems !== 0" | ||
14 | [totalItems]="pagination.totalItems" [itemsPerPage]="pagination.itemsPerPage" [maxSize]="6" [boundaryLinks]="true" [rotate]="false" | ||
15 | [(ngModel)]="pagination.currentPage" (pageChanged)="onPageChanged($event)" | ||
16 | ></pagination> | ||
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 { | |||
19 | protected notificationsService: NotificationsService | 19 | protected notificationsService: NotificationsService |
20 | protected router: Router | 20 | protected router: Router |
21 | protected route: ActivatedRoute | 21 | protected route: ActivatedRoute |
22 | |||
23 | protected subActivatedRoute: Subscription | 22 | protected subActivatedRoute: Subscription |
24 | 23 | ||
24 | protected abstract currentRoute: string | ||
25 | |||
25 | abstract titlePage: string | 26 | abstract titlePage: string |
27 | private loadedPages: { [ id: number ]: boolean } = {} | ||
28 | |||
26 | abstract getVideosObservable (): Observable<{ videos: Video[], totalVideos: number}> | 29 | abstract getVideosObservable (): Observable<{ videos: Video[], totalVideos: number}> |
27 | 30 | ||
28 | ngOnInit () { | 31 | ngOnInit () { |
29 | // Subscribe to route changes | 32 | // Subscribe to route changes |
30 | this.subActivatedRoute = this.route.params.subscribe(routeParams => { | 33 | const routeParams = this.route.snapshot.params |
31 | this.loadRouteParams(routeParams) | 34 | this.loadRouteParams(routeParams) |
32 | 35 | this.loadMoreVideos('after') | |
33 | this.getVideos() | ||
34 | }) | ||
35 | } | 36 | } |
36 | 37 | ||
37 | ngOnDestroy () { | 38 | ngOnDestroy () { |
38 | this.subActivatedRoute.unsubscribe() | 39 | this.subActivatedRoute.unsubscribe() |
39 | } | 40 | } |
40 | 41 | ||
41 | getVideos () { | 42 | onNearOfTop () { |
42 | this.videos = [] | 43 | if (this.pagination.currentPage > 1) { |
44 | this.previousPage() | ||
45 | } | ||
46 | } | ||
47 | |||
48 | onNearOfBottom () { | ||
49 | if (this.hasMoreVideos()) { | ||
50 | this.nextPage() | ||
51 | } | ||
52 | } | ||
53 | |||
54 | loadMoreVideos (where: 'before' | 'after') { | ||
55 | if (this.loadedPages[this.pagination.currentPage] === true) return | ||
43 | 56 | ||
44 | const observable = this.getVideosObservable() | 57 | const observable = this.getVideosObservable() |
45 | 58 | ||
46 | observable.subscribe( | 59 | observable.subscribe( |
47 | ({ videos, totalVideos }) => { | 60 | ({ videos, totalVideos }) => { |
48 | this.videos = videos | 61 | this.loadedPages[this.pagination.currentPage] = true |
49 | this.pagination.totalItems = totalVideos | 62 | this.pagination.totalItems = totalVideos |
63 | |||
64 | if (where === 'before') { | ||
65 | this.videos = videos.concat(this.videos) | ||
66 | } else { | ||
67 | this.videos = this.videos.concat(videos) | ||
68 | } | ||
50 | }, | 69 | }, |
51 | error => this.notificationsService.error('Error', error.text) | 70 | error => this.notificationsService.error('Error', error.text) |
52 | ) | 71 | ) |
53 | } | 72 | } |
54 | 73 | ||
55 | onPageChanged (event: { page: number }) { | 74 | protected hasMoreVideos () { |
56 | // Be sure the current page is set | 75 | if (!this.pagination.totalItems) return true |
57 | this.pagination.currentPage = event.page | 76 | |
77 | const maxPage = this.pagination.totalItems/this.pagination.itemsPerPage | ||
78 | return maxPage > this.pagination.currentPage | ||
79 | } | ||
80 | |||
81 | protected previousPage () { | ||
82 | this.pagination.currentPage-- | ||
83 | |||
84 | this.setNewRouteParams() | ||
85 | this.loadMoreVideos('before') | ||
86 | } | ||
87 | |||
88 | protected nextPage () { | ||
89 | this.pagination.currentPage++ | ||
58 | 90 | ||
59 | this.navigateToNewParams() | 91 | this.setNewRouteParams() |
92 | this.loadMoreVideos('after') | ||
60 | } | 93 | } |
61 | 94 | ||
62 | protected buildRouteParams () { | 95 | protected buildRouteParams () { |
@@ -79,8 +112,8 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy { | |||
79 | } | 112 | } |
80 | } | 113 | } |
81 | 114 | ||
82 | protected navigateToNewParams () { | 115 | protected setNewRouteParams () { |
83 | const routeParams = this.buildRouteParams() | 116 | const routeParams = this.buildRouteParams() |
84 | this.router.navigate([ '/videos/list', routeParams ]) | 117 | this.router.navigate([ this.currentRoute, routeParams ]) |
85 | } | 118 | } |
86 | } | 119 | } |
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' | |||
11 | }) | 11 | }) |
12 | export class VideoRecentlyAddedComponent extends AbstractVideoList implements OnInit, OnDestroy { | 12 | export class VideoRecentlyAddedComponent extends AbstractVideoList implements OnInit, OnDestroy { |
13 | titlePage = 'Recently added' | 13 | titlePage = 'Recently added' |
14 | currentRoute = '/videos/recently-added' | ||
14 | 15 | ||
15 | constructor (protected router: Router, | 16 | constructor (protected router: Router, |
16 | protected route: ActivatedRoute, | 17 | 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' | |||
11 | }) | 11 | }) |
12 | export class VideoTrendingComponent extends AbstractVideoList implements OnInit, OnDestroy { | 12 | export class VideoTrendingComponent extends AbstractVideoList implements OnInit, OnDestroy { |
13 | titlePage = 'Trending' | 13 | titlePage = 'Trending' |
14 | currentRoute = '/videos/trending' | ||
14 | 15 | ||
15 | constructor (protected router: Router, | 16 | constructor (protected router: Router, |
16 | protected route: ActivatedRoute, | 17 | 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 @@ | |||
1 | import { NgModule } from '@angular/core' | 1 | import { NgModule } from '@angular/core' |
2 | import { InfiniteScrollModule } from 'ngx-infinite-scroll' | ||
2 | import { SharedModule } from '../shared' | 3 | import { SharedModule } from '../shared' |
3 | import { VideoService } from './shared' | 4 | import { VideoService } from './shared' |
4 | import { MyVideosComponent, VideoMiniatureComponent } from './video-list' | 5 | import { MyVideosComponent, VideoMiniatureComponent } from './video-list' |
@@ -10,7 +11,8 @@ import { VideosComponent } from './videos.component' | |||
10 | @NgModule({ | 11 | @NgModule({ |
11 | imports: [ | 12 | imports: [ |
12 | VideosRoutingModule, | 13 | VideosRoutingModule, |
13 | SharedModule | 14 | SharedModule, |
15 | InfiniteScrollModule | ||
14 | ], | 16 | ], |
15 | 17 | ||
16 | declarations: [ | 18 | 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: | |||
4714 | dependencies: | 4714 | dependencies: |
4715 | ng2-material-dropdown "0.7.10" | 4715 | ng2-material-dropdown "0.7.10" |
4716 | 4716 | ||
4717 | ngx-infinite-scroll@^0.7.0: | ||
4718 | version "0.7.0" | ||
4719 | resolved "https://registry.yarnpkg.com/ngx-infinite-scroll/-/ngx-infinite-scroll-0.7.0.tgz#a390c61c6a05ac14485e1c5bc8b4e6f6bd62fd6a" | ||
4720 | |||
4717 | ngx-pipes@^2.0.5: | 4721 | ngx-pipes@^2.0.5: |
4718 | version "2.0.5" | 4722 | version "2.0.5" |
4719 | resolved "https://registry.yarnpkg.com/ngx-pipes/-/ngx-pipes-2.0.5.tgz#743b827e350b1e66f5bdae49e90a02fa631d4c54" | 4723 | resolved "https://registry.yarnpkg.com/ngx-pipes/-/ngx-pipes-2.0.5.tgz#743b827e350b1e66f5bdae49e90a02fa631d4c54" |