Add pagination support to the client

pull/10/head
Chocobozzz 2016-05-22 10:43:06 +02:00
parent 46246b5f19
commit 322940742b
11 changed files with 74 additions and 28 deletions

View File

@ -5,7 +5,7 @@
<h4>PeerTube</h4> <h4>PeerTube</h4>
</div> </div>
<div class="col-md-8"> <div class="col-md-9">
<input <input
type="text" id="search_video" name="search_video" class="form-control" placeholder="Search a video..." type="text" id="search_video" name="search_video" class="form-control" placeholder="Search a video..."
#search (keyup.enter)="doSearch(search.value)" #search (keyup.enter)="doSearch(search.value)"

View File

@ -1,4 +1,4 @@
<div class="video-miniature" (mouseenter)="onHover()" (mouseleave)="onBlur()"> <div class="video-miniature col-md-4" (mouseenter)="onHover()" (mouseleave)="onBlur()">
<a <a
[routerLink]="['VideosWatch', { id: video.id }]" [attr.title]="video.description" [routerLink]="['VideosWatch', { id: video.id }]" [attr.title]="video.description"
class="video-miniature-thumbnail" class="video-miniature-thumbnail"

View File

@ -1,8 +1,6 @@
.video-miniature { .video-miniature {
width: 200px;
height: 200px; height: 200px;
display: inline-block; display: inline-block;
margin-right: 40px;
position: relative; position: relative;
.video-miniature-thumbnail { .video-miniature-thumbnail {
@ -11,7 +9,7 @@
.video-miniature-duration { .video-miniature-duration {
position: absolute; position: absolute;
right: 2px; right: 60px;
bottom: 2px; bottom: 2px;
display: inline-block; display: inline-block;
background-color: rgba(0, 0, 0, 0.8); background-color: rgba(0, 0, 0, 0.8);
@ -24,7 +22,7 @@
.video-miniature-remove { .video-miniature-remove {
display: inline-block; display: inline-block;
position: absolute; position: absolute;
left: 2px; left: 16px;
background-color: rgba(0, 0, 0, 0.8); background-color: rgba(0, 0, 0, 0.8);
color: rgba(255, 255, 255, 0.8); color: rgba(255, 255, 255, 0.8);
padding: 2px; padding: 2px;

View File

@ -1,3 +1,11 @@
<div *ngIf="videos.length === 0">There is no video.</div> <div class="videos-miniatures">
<my-video-miniature *ngFor="let video of videos" [video]="video" [user]="user" (removed)="onRemoved(video)"> <div *ngIf="videos.length === 0">There is no video.</div>
</my-video-miniature>
<my-video-miniature *ngFor="let video of videos" [video]="video" [user]="user" (removed)="onRemoved(video)">
</my-video-miniature>
</div>
<pagination
[totalItems]="pagination.total" [itemsPerPage]="pagination.itemsPerPage" [(ngModel)]="pagination.currentPage"
(ngModelChange)="getVideos()"
></pagination>

View File

@ -1,8 +1,12 @@
.loading { .videos-miniatures {
display: inline-block; min-height: 600px;
margin-top: 100px;
} }
my-videos-miniature { my-videos-miniature {
display: inline-block; display: inline-block;
} }
pagination {
display: block;
text-align: center;
}

View File

@ -1,7 +1,10 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { ROUTER_DIRECTIVES, RouteParams } from '@angular/router-deprecated'; import { ROUTER_DIRECTIVES, RouteParams } from '@angular/router-deprecated';
import { PAGINATION_DIRECTIVES } from 'ng2-bootstrap/components/pagination';
import { AuthService } from '../../../users/services/auth.service'; import { AuthService } from '../../../users/services/auth.service';
import { Pagination } from '../../pagination';
import { User } from '../../../users/models/user'; import { User } from '../../../users/models/user';
import { VideosService } from '../../videos.service'; import { VideosService } from '../../videos.service';
import { Video } from '../../video'; import { Video } from '../../video';
@ -11,21 +14,26 @@ import { VideoMiniatureComponent } from './video-miniature.component';
selector: 'my-videos-list', selector: 'my-videos-list',
styleUrls: [ 'app/angular/videos/components/list/videos-list.component.css' ], styleUrls: [ 'app/angular/videos/components/list/videos-list.component.css' ],
templateUrl: 'app/angular/videos/components/list/videos-list.component.html', templateUrl: 'app/angular/videos/components/list/videos-list.component.html',
directives: [ ROUTER_DIRECTIVES, VideoMiniatureComponent ] directives: [ ROUTER_DIRECTIVES, PAGINATION_DIRECTIVES, VideoMiniatureComponent ]
}) })
export class VideosListComponent implements OnInit { export class VideosListComponent implements OnInit {
user: User = null; user: User = null;
videos: Video[] = []; videos: Video[] = [];
pagination: Pagination = {
currentPage: 1,
itemsPerPage: 9,
total: 0
}
private search: string; private search: string;
constructor( constructor(
private _authService: AuthService, private _authService: AuthService,
private _videosService: VideosService, private _videosService: VideosService,
routeParams: RouteParams private _routeParams: RouteParams
) { ) {
this.search = routeParams.get('search'); this.search = this._routeParams.get('search');
} }
ngOnInit() { ngOnInit() {
@ -40,13 +48,16 @@ export class VideosListComponent implements OnInit {
let observable = null; let observable = null;
if (this.search !== null) { if (this.search !== null) {
observable = this._videosService.searchVideos(this.search); observable = this._videosService.searchVideos(this.search, this.pagination);
} else { } else {
observable = this._videosService.getVideos(); observable = this._videosService.getVideos(this.pagination);
} }
observable.subscribe( observable.subscribe(
videos => this.videos = videos, ({ videos, totalVideos }) => {
this.videos = videos;
this.pagination.total = totalVideos;
},
error => alert(error) error => alert(error)
); );
} }

View File

@ -0,0 +1,5 @@
export interface Pagination {
currentPage: number;
itemsPerPage: number;
total: number;
}

View File

@ -1,7 +1,8 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http'; import { Http, Response, RequestOptions, URLSearchParams } from '@angular/http';
import { Observable } from 'rxjs/Rx'; import { Observable } from 'rxjs/Rx';
import { Pagination } from './pagination';
import { Video } from './video'; import { Video } from './video';
import { AuthService } from '../users/services/auth.service'; import { AuthService } from '../users/services/auth.service';
@ -11,8 +12,9 @@ export class VideosService {
constructor (private http: Http, private _authService: AuthService) {} constructor (private http: Http, private _authService: AuthService) {}
getVideos() { getVideos(pagination: Pagination) {
return this.http.get(this._baseVideoUrl) const params = { search: this.createPaginationParams(pagination) };
return this.http.get(this._baseVideoUrl, params)
.map(res => res.json()) .map(res => res.json())
.map(this.extractVideos) .map(this.extractVideos)
.catch(this.handleError); .catch(this.handleError);
@ -31,24 +33,38 @@ export class VideosService {
.catch(this.handleError); .catch(this.handleError);
} }
searchVideos(search: string) { searchVideos(search: string, pagination: Pagination) {
return this.http.get(this._baseVideoUrl + 'search/' + search) const params = { search: this.createPaginationParams(pagination) };
return this.http.get(this._baseVideoUrl + 'search/' + encodeURIComponent(search), params)
.map(res => res.json()) .map(res => res.json())
.map(this.extractVideos) .map(this.extractVideos)
.catch(this.handleError); .catch(this.handleError);
} }
private extractVideos (body: any[]) { private extractVideos (body: any) {
const videos_json = body.data;
const totalVideos = body.total;
const videos = []; const videos = [];
for (const video_json of body) { for (const video_json of videos_json) {
videos.push(new Video(video_json)); videos.push(new Video(video_json));
} }
return videos; return { videos, totalVideos };
} }
private handleError (error: Response) { private handleError (error: Response) {
console.error(error); console.error(error);
return Observable.throw(error.json().error || 'Server error'); return Observable.throw(error.json().error || 'Server error');
} }
private createPaginationParams(pagination: Pagination) {
const params = new URLSearchParams();
const start: number = (pagination.currentPage - 1) * pagination.itemsPerPage;
const count: number = pagination.itemsPerPage;
params.set('start', start.toString());
params.set('count', count.toString());
return params;
}
} }

View File

@ -21,20 +21,21 @@
}, },
"license": "GPLv3", "license": "GPLv3",
"dependencies": { "dependencies": {
"angular-pipes": "^2.0.0",
"@angular/common": "2.0.0-rc.1", "@angular/common": "2.0.0-rc.1",
"@angular/compiler": "2.0.0-rc.1", "@angular/compiler": "2.0.0-rc.1",
"@angular/core": "2.0.0-rc.1", "@angular/core": "2.0.0-rc.1",
"@angular/http": "2.0.0-rc.1", "@angular/http": "2.0.0-rc.1",
"@angular/platform-browser-dynamic": "2.0.0-rc.1",
"@angular/platform-browser": "2.0.0-rc.1", "@angular/platform-browser": "2.0.0-rc.1",
"@angular/platform-browser-dynamic": "2.0.0-rc.1",
"@angular/router-deprecated": "2.0.0-rc.1", "@angular/router-deprecated": "2.0.0-rc.1",
"angular-pipes": "^2.0.0",
"blueimp-file-upload": "^9.12.1", "blueimp-file-upload": "^9.12.1",
"bootstrap-sass": "^3.3.6", "bootstrap-sass": "^3.3.6",
"es6-promise": "^3.0.2", "es6-promise": "^3.0.2",
"es6-shim": "^0.35.0", "es6-shim": "^0.35.0",
"jquery": "^2.2.3", "jquery": "^2.2.3",
"jquery.ui.widget": "^1.10.3", "jquery.ui.widget": "^1.10.3",
"ng2-bootstrap": "^1.0.16",
"reflect-metadata": "0.1.3", "reflect-metadata": "0.1.3",
"rxjs": "5.0.0-beta.6", "rxjs": "5.0.0-beta.6",
"systemjs": "0.19.27", "systemjs": "0.19.27",

View File

@ -2,11 +2,13 @@
var map = { var map = {
'app': 'app/angular', 'app': 'app/angular',
'angular-pipes': 'app/node_modules/angular-pipes', 'angular-pipes': 'app/node_modules/angular-pipes',
'ng2-bootstrap': 'app/node_modules/ng2-bootstrap',
'angular-rxjs.bundle': 'app/bundles/angular-rxjs.bundle.js' 'angular-rxjs.bundle': 'app/bundles/angular-rxjs.bundle.js'
} }
var packages = { var packages = {
'app': { main: 'main.js', defaultExtension: 'js' }, 'app': { main: 'main.js', defaultExtension: 'js' },
'ng2-bootstrap': { defaultExtension: 'js' },
'rxjs': { defaultExtension: 'js' } 'rxjs': { defaultExtension: 'js' }
} }
var packageNames = [ var packageNames = [

View File

@ -32,6 +32,7 @@
"angular/videos/components/list/video-miniature.component.ts", "angular/videos/components/list/video-miniature.component.ts",
"angular/videos/components/list/videos-list.component.ts", "angular/videos/components/list/videos-list.component.ts",
"angular/videos/components/watch/videos-watch.component.ts", "angular/videos/components/watch/videos-watch.component.ts",
"angular/videos/pagination.ts",
"angular/videos/video.ts", "angular/videos/video.ts",
"angular/videos/videos.service.ts", "angular/videos/videos.service.ts",
"typings/globals/es6-shim/index.d.ts", "typings/globals/es6-shim/index.d.ts",