diff options
Diffstat (limited to 'client/src/app/videos')
12 files changed, 134 insertions, 150 deletions
diff --git a/client/src/app/videos/shared/index.ts b/client/src/app/videos/shared/index.ts index a54120f5d..67d16ead1 100644 --- a/client/src/app/videos/shared/index.ts +++ b/client/src/app/videos/shared/index.ts | |||
@@ -1,5 +1,4 @@ | |||
1 | export * from './loader'; | 1 | export * from './loader'; |
2 | export * from './pagination.model'; | ||
3 | export * from './sort-field.type'; | 2 | export * from './sort-field.type'; |
4 | export * from './video.model'; | 3 | export * from './video.model'; |
5 | export * from './video.service'; | 4 | export * from './video.service'; |
diff --git a/client/src/app/videos/shared/loader/loader.component.ts b/client/src/app/videos/shared/loader/loader.component.ts index cdd07d1b4..e72d2f3f3 100644 --- a/client/src/app/videos/shared/loader/loader.component.ts +++ b/client/src/app/videos/shared/loader/loader.component.ts | |||
@@ -2,8 +2,8 @@ import { Component, Input } from '@angular/core'; | |||
2 | 2 | ||
3 | @Component({ | 3 | @Component({ |
4 | selector: 'my-loader', | 4 | selector: 'my-loader', |
5 | styles: [ require('./loader.component.scss') ], | 5 | styleUrls: [ './loader.component.scss' ], |
6 | template: require('./loader.component.html') | 6 | templateUrl: './loader.component.html' |
7 | }) | 7 | }) |
8 | 8 | ||
9 | export class LoaderComponent { | 9 | export class LoaderComponent { |
diff --git a/client/src/app/videos/shared/pagination.model.ts b/client/src/app/videos/shared/pagination.model.ts deleted file mode 100644 index eda44ebfb..000000000 --- a/client/src/app/videos/shared/pagination.model.ts +++ /dev/null | |||
@@ -1,5 +0,0 @@ | |||
1 | export interface Pagination { | ||
2 | currentPage: number; | ||
3 | itemsPerPage: number; | ||
4 | totalItems: number; | ||
5 | } | ||
diff --git a/client/src/app/videos/shared/video.service.ts b/client/src/app/videos/shared/video.service.ts index b4396f767..ad8557533 100644 --- a/client/src/app/videos/shared/video.service.ts +++ b/client/src/app/videos/shared/video.service.ts | |||
@@ -1,11 +1,10 @@ | |||
1 | import { Injectable } from '@angular/core'; | 1 | import { Injectable } from '@angular/core'; |
2 | import { Http, Response, URLSearchParams } from '@angular/http'; | 2 | import { Http } from '@angular/http'; |
3 | import { Observable } from 'rxjs/Observable'; | 3 | import { Observable } from 'rxjs/Observable'; |
4 | 4 | ||
5 | import { Pagination } from './pagination.model'; | ||
6 | import { Search } from '../../shared'; | 5 | import { Search } from '../../shared'; |
7 | import { SortField } from './sort-field.type'; | 6 | import { SortField } from './sort-field.type'; |
8 | import { AuthHttp, AuthService } from '../../shared'; | 7 | import { AuthHttp, AuthService, RestExtractor, RestPagination, RestService, ResultList } from '../../shared'; |
9 | import { Video } from './video.model'; | 8 | import { Video } from './video.model'; |
10 | 9 | ||
11 | @Injectable() | 10 | @Injectable() |
@@ -15,68 +14,51 @@ export class VideoService { | |||
15 | constructor( | 14 | constructor( |
16 | private authService: AuthService, | 15 | private authService: AuthService, |
17 | private authHttp: AuthHttp, | 16 | private authHttp: AuthHttp, |
18 | private http: Http | 17 | private http: Http, |
18 | private restExtractor: RestExtractor, | ||
19 | private restService: RestService | ||
19 | ) {} | 20 | ) {} |
20 | 21 | ||
21 | getVideo(id: string) { | 22 | getVideo(id: string): Observable<Video> { |
22 | return this.http.get(VideoService.BASE_VIDEO_URL + id) | 23 | return this.http.get(VideoService.BASE_VIDEO_URL + id) |
23 | .map(res => <Video> res.json()) | 24 | .map(this.restExtractor.extractDataGet) |
24 | .catch(this.handleError); | 25 | .catch((res) => this.restExtractor.handleError(res)); |
25 | } | 26 | } |
26 | 27 | ||
27 | getVideos(pagination: Pagination, sort: SortField) { | 28 | getVideos(pagination: RestPagination, sort: SortField) { |
28 | const params = this.createPaginationParams(pagination); | 29 | const params = this.restService.buildRestGetParams(pagination, sort); |
29 | |||
30 | if (sort) params.set('sort', sort); | ||
31 | 30 | ||
32 | return this.http.get(VideoService.BASE_VIDEO_URL, { search: params }) | 31 | return this.http.get(VideoService.BASE_VIDEO_URL, { search: params }) |
33 | .map(res => res.json()) | 32 | .map(res => res.json()) |
34 | .map(this.extractVideos) | 33 | .map(this.extractVideos) |
35 | .catch(this.handleError); | 34 | .catch((res) => this.restExtractor.handleError(res)); |
36 | } | 35 | } |
37 | 36 | ||
38 | removeVideo(id: string) { | 37 | removeVideo(id: string) { |
39 | return this.authHttp.delete(VideoService.BASE_VIDEO_URL + id) | 38 | return this.authHttp.delete(VideoService.BASE_VIDEO_URL + id) |
40 | .map(res => <number> res.status) | 39 | .map(this.restExtractor.extractDataBool) |
41 | .catch(this.handleError); | 40 | .catch((res) => this.restExtractor.handleError(res)); |
42 | } | 41 | } |
43 | 42 | ||
44 | searchVideos(search: Search, pagination: Pagination, sort: SortField) { | 43 | searchVideos(search: Search, pagination: RestPagination, sort: SortField) { |
45 | const params = this.createPaginationParams(pagination); | 44 | const params = this.restService.buildRestGetParams(pagination, sort); |
46 | 45 | ||
47 | if (search.field) params.set('field', search.field); | 46 | if (search.field) params.set('field', search.field); |
48 | if (sort) params.set('sort', sort); | ||
49 | 47 | ||
50 | return this.http.get(VideoService.BASE_VIDEO_URL + 'search/' + encodeURIComponent(search.value), { search: params }) | 48 | return this.http.get(VideoService.BASE_VIDEO_URL + 'search/' + encodeURIComponent(search.value), { search: params }) |
51 | .map(res => res.json()) | 49 | .map(this.restExtractor.extractDataList) |
52 | .map(this.extractVideos) | 50 | .map(this.extractVideos) |
53 | .catch(this.handleError); | 51 | .catch((res) => this.restExtractor.handleError(res)); |
54 | } | ||
55 | |||
56 | private createPaginationParams(pagination: Pagination) { | ||
57 | const params = new URLSearchParams(); | ||
58 | const start: number = (pagination.currentPage - 1) * pagination.itemsPerPage; | ||
59 | const count: number = pagination.itemsPerPage; | ||
60 | |||
61 | params.set('start', start.toString()); | ||
62 | params.set('count', count.toString()); | ||
63 | |||
64 | return params; | ||
65 | } | 52 | } |
66 | 53 | ||
67 | private extractVideos(body: any) { | 54 | private extractVideos(result: ResultList) { |
68 | const videos_json = body.data; | 55 | const videosJson = result.data; |
69 | const totalVideos = body.total; | 56 | const totalVideos = result.total; |
70 | const videos = []; | 57 | const videos = []; |
71 | for (const video_json of videos_json) { | 58 | for (const videoJson of videosJson) { |
72 | videos.push(new Video(video_json)); | 59 | videos.push(new Video(videoJson)); |
73 | } | 60 | } |
74 | 61 | ||
75 | return { videos, totalVideos }; | 62 | return { videos, totalVideos }; |
76 | } | 63 | } |
77 | |||
78 | private handleError(error: Response) { | ||
79 | console.error(error); | ||
80 | return Observable.throw(error.json().error || 'Server error'); | ||
81 | } | ||
82 | } | 64 | } |
diff --git a/client/src/app/videos/video-add/video-add.component.html b/client/src/app/videos/video-add/video-add.component.html index bcd78c7cb..64320cae7 100644 --- a/client/src/app/videos/video-add/video-add.component.html +++ b/client/src/app/videos/video-add/video-add.component.html | |||
@@ -2,31 +2,31 @@ | |||
2 | 2 | ||
3 | <div *ngIf="error" class="alert alert-danger">{{ error }}</div> | 3 | <div *ngIf="error" class="alert alert-danger">{{ error }}</div> |
4 | 4 | ||
5 | <form novalidate (ngSubmit)="upload()" [ngFormModel]="videoForm"> | 5 | <form novalidate (ngSubmit)="upload()" [formGroup]="form"> |
6 | <div class="form-group"> | 6 | <div class="form-group"> |
7 | <label for="name">Name</label> | 7 | <label for="name">Name</label> |
8 | <input | 8 | <input |
9 | type="text" class="form-control" name="name" id="name" | 9 | type="text" class="form-control" id="name" |
10 | ngControl="name" #name="ngForm" [(ngModel)]="video.name" | 10 | formControlName="name" |
11 | > | 11 | > |
12 | <div [hidden]="name.valid || name.pristine" class="alert alert-warning"> | 12 | <div *ngIf="formErrors.name" class="alert alert-danger"> |
13 | A name is required and should be between 3 and 50 characters long | 13 | {{ formErrors.name }} |
14 | </div> | 14 | </div> |
15 | </div> | 15 | </div> |
16 | 16 | ||
17 | <div class="form-group"> | 17 | <div class="form-group"> |
18 | <label for="tags">Tags</label> | 18 | <label for="tags">Tags</label> |
19 | <input | 19 | <input |
20 | type="text" class="form-control" name="tags" id="tags" | 20 | type="text" class="form-control" id="currentTag" |
21 | ngControl="tags" #tags="ngForm" [disabled]="isTagsInputDisabled" (keyup)="onTagKeyPress($event)" [(ngModel)]="currentTag" | 21 | formControlName="currentTag" (keyup)="onTagKeyPress($event)" |
22 | > | 22 | > |
23 | <div [hidden]="tags.valid || tags.pristine" class="alert alert-warning"> | 23 | <div *ngIf="formErrors.currentTag" class="alert alert-danger"> |
24 | A tag should be between 2 and 10 characters (alphanumeric) long | 24 | {{ formErrors.currentTag }} |
25 | </div> | 25 | </div> |
26 | </div> | 26 | </div> |
27 | 27 | ||
28 | <div class="tags"> | 28 | <div class="tags"> |
29 | <div class="label label-primary tag" *ngFor="let tag of video.tags"> | 29 | <div class="label label-primary tag" *ngFor="let tag of tags"> |
30 | {{ tag }} | 30 | {{ tag }} |
31 | <span class="remove" (click)="removeTag(tag)">x</span> | 31 | <span class="remove" (click)="removeTag(tag)">x</span> |
32 | </div> | 32 | </div> |
@@ -53,12 +53,12 @@ | |||
53 | <div class="form-group"> | 53 | <div class="form-group"> |
54 | <label for="description">Description</label> | 54 | <label for="description">Description</label> |
55 | <textarea | 55 | <textarea |
56 | name="description" id="description" class="form-control" placeholder="Description..." | 56 | id="description" class="form-control" placeholder="Description..." |
57 | ngControl="description" #description="ngForm" [(ngModel)]="video.description" | 57 | formControlName="description" |
58 | > | 58 | > |
59 | </textarea> | 59 | </textarea> |
60 | <div [hidden]="description.valid || description.pristine" class="alert alert-warning"> | 60 | <div *ngIf="formErrors.description" class="alert alert-danger"> |
61 | A description is required and should be between 3 and 250 characters long | 61 | {{ formErrors.description }} |
62 | </div> | 62 | </div> |
63 | </div> | 63 | </div> |
64 | 64 | ||
@@ -69,7 +69,7 @@ | |||
69 | <div class="form-group"> | 69 | <div class="form-group"> |
70 | <input | 70 | <input |
71 | type="submit" value="Upload" class="btn btn-default form-control" [title]="getInvalidFieldsTitle()" | 71 | type="submit" value="Upload" class="btn btn-default form-control" [title]="getInvalidFieldsTitle()" |
72 | [disabled]="!videoForm.valid || video.tags.length === 0 || filename === null" | 72 | [disabled]="!form.valid || tags.length === 0 || filename === null" |
73 | > | 73 | > |
74 | </div> | 74 | </div> |
75 | </form> | 75 | </form> |
diff --git a/client/src/app/videos/video-add/video-add.component.ts b/client/src/app/videos/video-add/video-add.component.ts index c0f8cb9c4..d12a7d572 100644 --- a/client/src/app/videos/video-add/video-add.component.ts +++ b/client/src/app/videos/video-add/video-add.component.ts | |||
@@ -1,37 +1,42 @@ | |||
1 | import { Control, ControlGroup, Validators } from '@angular/common'; | ||
2 | import { Component, ElementRef, OnInit } from '@angular/core'; | 1 | import { Component, ElementRef, OnInit } from '@angular/core'; |
2 | import { FormBuilder, FormGroup } from '@angular/forms'; | ||
3 | import { Router } from '@angular/router'; | 3 | import { Router } from '@angular/router'; |
4 | 4 | ||
5 | import { BytesPipe } from 'angular-pipes/src/math/bytes.pipe'; | 5 | import { FileUploader } from 'ng2-file-upload/ng2-file-upload'; |
6 | import { PROGRESSBAR_DIRECTIVES } from 'ng2-bootstrap/components/progressbar'; | ||
7 | import { FileSelectDirective, FileUploader } from 'ng2-file-upload/ng2-file-upload'; | ||
8 | 6 | ||
9 | import { AuthService } from '../../shared'; | 7 | import { AuthService, FormReactive, VIDEO_NAME, VIDEO_DESCRIPTION, VIDEO_TAGS } from '../../shared'; |
10 | 8 | ||
11 | @Component({ | 9 | @Component({ |
12 | selector: 'my-videos-add', | 10 | selector: 'my-videos-add', |
13 | styles: [ require('./video-add.component.scss') ], | 11 | styleUrls: [ './video-add.component.scss' ], |
14 | template: require('./video-add.component.html'), | 12 | templateUrl: './video-add.component.html' |
15 | directives: [ FileSelectDirective, PROGRESSBAR_DIRECTIVES ], | ||
16 | pipes: [ BytesPipe ] | ||
17 | }) | 13 | }) |
18 | 14 | ||
19 | export class VideoAddComponent implements OnInit { | 15 | export class VideoAddComponent extends FormReactive implements OnInit { |
20 | currentTag: string; // Tag the user is writing in the input | 16 | tags: string[] = []; |
21 | error: string = null; | ||
22 | videoForm: ControlGroup; | ||
23 | uploader: FileUploader; | 17 | uploader: FileUploader; |
24 | video = { | 18 | |
19 | error: string = null; | ||
20 | form: FormGroup; | ||
21 | formErrors = { | ||
25 | name: '', | 22 | name: '', |
26 | tags: [], | 23 | description: '', |
27 | description: '' | 24 | currentTag: '' |
25 | }; | ||
26 | validationMessages = { | ||
27 | name: VIDEO_NAME.MESSAGES, | ||
28 | description: VIDEO_DESCRIPTION.MESSAGES, | ||
29 | currentTag: VIDEO_TAGS.MESSAGES | ||
28 | }; | 30 | }; |
29 | 31 | ||
30 | constructor( | 32 | constructor( |
31 | private authService: AuthService, | 33 | private authService: AuthService, |
32 | private elementRef: ElementRef, | 34 | private elementRef: ElementRef, |
35 | private formBuilder: FormBuilder, | ||
33 | private router: Router | 36 | private router: Router |
34 | ) {} | 37 | ) { |
38 | super(); | ||
39 | } | ||
35 | 40 | ||
36 | get filename() { | 41 | get filename() { |
37 | if (this.uploader.queue.length === 0) { | 42 | if (this.uploader.queue.length === 0) { |
@@ -41,20 +46,26 @@ export class VideoAddComponent implements OnInit { | |||
41 | return this.uploader.queue[0].file.name; | 46 | return this.uploader.queue[0].file.name; |
42 | } | 47 | } |
43 | 48 | ||
44 | get isTagsInputDisabled () { | 49 | buildForm() { |
45 | return this.video.tags.length >= 3; | 50 | this.form = this.formBuilder.group({ |
51 | name: [ '', VIDEO_NAME.VALIDATORS ], | ||
52 | description: [ '', VIDEO_DESCRIPTION.VALIDATORS ], | ||
53 | currentTag: [ '', VIDEO_TAGS.VALIDATORS ] | ||
54 | }); | ||
55 | |||
56 | this.form.valueChanges.subscribe(data => this.onValueChanged(data)); | ||
46 | } | 57 | } |
47 | 58 | ||
48 | getInvalidFieldsTitle() { | 59 | getInvalidFieldsTitle() { |
49 | let title = ''; | 60 | let title = ''; |
50 | const nameControl = this.videoForm.controls['name']; | 61 | const nameControl = this.form.controls['name']; |
51 | const descriptionControl = this.videoForm.controls['description']; | 62 | const descriptionControl = this.form.controls['description']; |
52 | 63 | ||
53 | if (!nameControl.valid) { | 64 | if (!nameControl.valid) { |
54 | title += 'A name is required\n'; | 65 | title += 'A name is required\n'; |
55 | } | 66 | } |
56 | 67 | ||
57 | if (this.video.tags.length === 0) { | 68 | if (this.tags.length === 0) { |
58 | title += 'At least one tag is required\n'; | 69 | title += 'At least one tag is required\n'; |
59 | } | 70 | } |
60 | 71 | ||
@@ -70,13 +81,6 @@ export class VideoAddComponent implements OnInit { | |||
70 | } | 81 | } |
71 | 82 | ||
72 | ngOnInit() { | 83 | ngOnInit() { |
73 | this.videoForm = new ControlGroup({ | ||
74 | name: new Control('', Validators.compose([ Validators.required, Validators.minLength(3), Validators.maxLength(50) ])), | ||
75 | description: new Control('', Validators.compose([ Validators.required, Validators.minLength(3), Validators.maxLength(250) ])), | ||
76 | tags: new Control('', Validators.pattern('^[a-zA-Z0-9]{2,10}$')) | ||
77 | }); | ||
78 | |||
79 | |||
80 | this.uploader = new FileUploader({ | 84 | this.uploader = new FileUploader({ |
81 | authToken: this.authService.getRequestHeaderValue(), | 85 | authToken: this.authService.getRequestHeaderValue(), |
82 | queueLimit: 1, | 86 | queueLimit: 1, |
@@ -85,26 +89,37 @@ export class VideoAddComponent implements OnInit { | |||
85 | }); | 89 | }); |
86 | 90 | ||
87 | this.uploader.onBuildItemForm = (item, form) => { | 91 | this.uploader.onBuildItemForm = (item, form) => { |
88 | form.append('name', this.video.name); | 92 | const name = this.form.value['name']; |
89 | form.append('description', this.video.description); | 93 | const description = this.form.value['description']; |
94 | |||
95 | form.append('name', name); | ||
96 | form.append('description', description); | ||
90 | 97 | ||
91 | for (let i = 0; i < this.video.tags.length; i++) { | 98 | for (let i = 0; i < this.tags.length; i++) { |
92 | form.append(`tags[${i}]`, this.video.tags[i]); | 99 | form.append(`tags[${i}]`, this.tags[i]); |
93 | } | 100 | } |
94 | }; | 101 | }; |
102 | |||
103 | this.buildForm(); | ||
95 | } | 104 | } |
96 | 105 | ||
97 | onTagKeyPress(event: KeyboardEvent) { | 106 | onTagKeyPress(event: KeyboardEvent) { |
107 | const currentTag = this.form.value['currentTag']; | ||
108 | |||
98 | // Enter press | 109 | // Enter press |
99 | if (event.keyCode === 13) { | 110 | if (event.keyCode === 13) { |
100 | // Check if the tag is valid and does not already exist | 111 | // Check if the tag is valid and does not already exist |
101 | if ( | 112 | if ( |
102 | this.currentTag !== '' && | 113 | currentTag !== '' && |
103 | this.videoForm.controls['tags'].valid && | 114 | this.form.controls['currentTag'].valid && |
104 | this.video.tags.indexOf(this.currentTag) === -1 | 115 | this.tags.indexOf(currentTag) === -1 |
105 | ) { | 116 | ) { |
106 | this.video.tags.push(this.currentTag); | 117 | this.tags.push(currentTag); |
107 | this.currentTag = ''; | 118 | this.form.patchValue({ currentTag: '' }); |
119 | |||
120 | if (this.tags.length >= 3) { | ||
121 | this.form.get('currentTag').disable(); | ||
122 | } | ||
108 | } | 123 | } |
109 | } | 124 | } |
110 | } | 125 | } |
@@ -114,7 +129,7 @@ export class VideoAddComponent implements OnInit { | |||
114 | } | 129 | } |
115 | 130 | ||
116 | removeTag(tag: string) { | 131 | removeTag(tag: string) { |
117 | this.video.tags.splice(this.video.tags.indexOf(tag), 1); | 132 | this.tags.splice(this.tags.indexOf(tag), 1); |
118 | } | 133 | } |
119 | 134 | ||
120 | upload() { | 135 | upload() { |
diff --git a/client/src/app/videos/video-list/video-list.component.ts b/client/src/app/videos/video-list/video-list.component.ts index 5691d684e..6b086e938 100644 --- a/client/src/app/videos/video-list/video-list.component.ts +++ b/client/src/app/videos/video-list/video-list.component.ts | |||
@@ -1,39 +1,30 @@ | |||
1 | import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; | 1 | import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; |
2 | import { AsyncPipe } from '@angular/common'; | 2 | import { ActivatedRoute, Router } from '@angular/router'; |
3 | import { ActivatedRoute, Router, ROUTER_DIRECTIVES } from '@angular/router'; | ||
4 | import { BehaviorSubject } from 'rxjs/BehaviorSubject'; | 3 | import { BehaviorSubject } from 'rxjs/BehaviorSubject'; |
5 | 4 | ||
6 | import { PAGINATION_DIRECTIVES } from 'ng2-bootstrap/components/pagination'; | ||
7 | |||
8 | import { | 5 | import { |
9 | LoaderComponent, | ||
10 | Pagination, | ||
11 | SortField, | 6 | SortField, |
12 | Video, | 7 | Video, |
13 | VideoService | 8 | VideoService |
14 | } from '../shared'; | 9 | } from '../shared'; |
15 | import { AuthService, Search, SearchField, User } from '../../shared'; | 10 | import { AuthService, AuthUser, RestPagination, Search, SearchField } from '../../shared'; |
16 | import { VideoMiniatureComponent } from './video-miniature.component'; | ||
17 | import { VideoSortComponent } from './video-sort.component'; | ||
18 | import { SearchService } from '../../shared'; | 11 | import { SearchService } from '../../shared'; |
19 | 12 | ||
20 | @Component({ | 13 | @Component({ |
21 | selector: 'my-videos-list', | 14 | selector: 'my-videos-list', |
22 | styles: [ require('./video-list.component.scss') ], | 15 | styleUrls: [ './video-list.component.scss' ], |
23 | pipes: [ AsyncPipe ], | 16 | templateUrl: './video-list.component.html' |
24 | template: require('./video-list.component.html'), | ||
25 | directives: [ LoaderComponent, PAGINATION_DIRECTIVES, ROUTER_DIRECTIVES, VideoMiniatureComponent, VideoSortComponent ] | ||
26 | }) | 17 | }) |
27 | 18 | ||
28 | export class VideoListComponent implements OnInit, OnDestroy { | 19 | export class VideoListComponent implements OnInit, OnDestroy { |
29 | loading: BehaviorSubject<boolean> = new BehaviorSubject(false); | 20 | loading: BehaviorSubject<boolean> = new BehaviorSubject(false); |
30 | pagination: Pagination = { | 21 | pagination: RestPagination = { |
31 | currentPage: 1, | 22 | currentPage: 1, |
32 | itemsPerPage: 9, | 23 | itemsPerPage: 9, |
33 | totalItems: null | 24 | totalItems: null |
34 | }; | 25 | }; |
35 | sort: SortField; | 26 | sort: SortField; |
36 | user: User = null; | 27 | user: AuthUser = null; |
37 | videos: Video[] = []; | 28 | videos: Video[] = []; |
38 | 29 | ||
39 | private search: Search; | 30 | private search: Search; |
@@ -51,7 +42,7 @@ export class VideoListComponent implements OnInit, OnDestroy { | |||
51 | 42 | ||
52 | ngOnInit() { | 43 | ngOnInit() { |
53 | if (this.authService.isLoggedIn()) { | 44 | if (this.authService.isLoggedIn()) { |
54 | this.user = User.load(); | 45 | this.user = AuthUser.load(); |
55 | } | 46 | } |
56 | 47 | ||
57 | // Subscribe to route changes | 48 | // Subscribe to route changes |
@@ -66,6 +57,8 @@ export class VideoListComponent implements OnInit, OnDestroy { | |||
66 | // Subscribe to search changes | 57 | // Subscribe to search changes |
67 | this.subSearch = this.searchService.searchUpdated.subscribe(search => { | 58 | this.subSearch = this.searchService.searchUpdated.subscribe(search => { |
68 | this.search = search; | 59 | this.search = search; |
60 | // Reset pagination | ||
61 | this.pagination.currentPage = 1; | ||
69 | 62 | ||
70 | this.navigateToNewParams(); | 63 | this.navigateToNewParams(); |
71 | }); | 64 | }); |
@@ -76,7 +69,7 @@ export class VideoListComponent implements OnInit, OnDestroy { | |||
76 | this.subSearch.unsubscribe(); | 69 | this.subSearch.unsubscribe(); |
77 | } | 70 | } |
78 | 71 | ||
79 | getVideos(detectChanges = true) { | 72 | getVideos() { |
80 | this.loading.next(true); | 73 | this.loading.next(true); |
81 | this.videos = []; | 74 | this.videos = []; |
82 | 75 | ||
@@ -97,7 +90,7 @@ export class VideoListComponent implements OnInit, OnDestroy { | |||
97 | 90 | ||
98 | this.loading.next(false); | 91 | this.loading.next(false); |
99 | }, | 92 | }, |
100 | error => alert(error) | 93 | error => alert(error.text) |
101 | ); | 94 | ); |
102 | } | 95 | } |
103 | 96 | ||
@@ -153,7 +146,11 @@ export class VideoListComponent implements OnInit, OnDestroy { | |||
153 | 146 | ||
154 | this.sort = <SortField>routeParams['sort'] || '-createdDate'; | 147 | this.sort = <SortField>routeParams['sort'] || '-createdDate'; |
155 | 148 | ||
156 | this.pagination.currentPage = parseInt(routeParams['page']) || 1; | 149 | if (routeParams['page'] !== undefined) { |
150 | this.pagination.currentPage = parseInt(routeParams['page']); | ||
151 | } else { | ||
152 | this.pagination.currentPage = 1; | ||
153 | } | ||
157 | 154 | ||
158 | this.changeDetector.detectChanges(); | 155 | this.changeDetector.detectChanges(); |
159 | } | 156 | } |
diff --git a/client/src/app/videos/video-list/video-miniature.component.ts b/client/src/app/videos/video-list/video-miniature.component.ts index 84bab950e..398d2db75 100644 --- a/client/src/app/videos/video-list/video-miniature.component.ts +++ b/client/src/app/videos/video-list/video-miniature.component.ts | |||
@@ -1,16 +1,12 @@ | |||
1 | import { DatePipe } from '@angular/common'; | ||
2 | import { Component, Input, Output, EventEmitter } from '@angular/core'; | 1 | import { Component, Input, Output, EventEmitter } from '@angular/core'; |
3 | import { ROUTER_DIRECTIVES } from '@angular/router'; | ||
4 | 2 | ||
5 | import { SortField, Video, VideoService } from '../shared'; | 3 | import { SortField, Video, VideoService } from '../shared'; |
6 | import { User } from '../../shared'; | 4 | import { User } from '../../shared'; |
7 | 5 | ||
8 | @Component({ | 6 | @Component({ |
9 | selector: 'my-video-miniature', | 7 | selector: 'my-video-miniature', |
10 | styles: [ require('./video-miniature.component.scss') ], | 8 | styleUrls: [ './video-miniature.component.scss' ], |
11 | template: require('./video-miniature.component.html'), | 9 | templateUrl: './video-miniature.component.html' |
12 | directives: [ ROUTER_DIRECTIVES ], | ||
13 | pipes: [ DatePipe ] | ||
14 | }) | 10 | }) |
15 | 11 | ||
16 | export class VideoMiniatureComponent { | 12 | export class VideoMiniatureComponent { |
@@ -40,7 +36,7 @@ export class VideoMiniatureComponent { | |||
40 | if (confirm('Do you really want to remove this video?')) { | 36 | if (confirm('Do you really want to remove this video?')) { |
41 | this.videoService.removeVideo(id).subscribe( | 37 | this.videoService.removeVideo(id).subscribe( |
42 | status => this.removed.emit(true), | 38 | status => this.removed.emit(true), |
43 | error => alert(error) | 39 | error => alert(error.text) |
44 | ); | 40 | ); |
45 | } | 41 | } |
46 | } | 42 | } |
diff --git a/client/src/app/videos/video-list/video-sort.component.ts b/client/src/app/videos/video-list/video-sort.component.ts index 0d76b54b7..ca94b07c2 100644 --- a/client/src/app/videos/video-list/video-sort.component.ts +++ b/client/src/app/videos/video-list/video-sort.component.ts | |||
@@ -4,7 +4,7 @@ import { SortField } from '../shared'; | |||
4 | 4 | ||
5 | @Component({ | 5 | @Component({ |
6 | selector: 'my-video-sort', | 6 | selector: 'my-video-sort', |
7 | template: require('./video-sort.component.html') | 7 | templateUrl: './video-sort.component.html' |
8 | }) | 8 | }) |
9 | 9 | ||
10 | export class VideoSortComponent { | 10 | export class VideoSortComponent { |
diff --git a/client/src/app/videos/video-watch/video-watch.component.ts b/client/src/app/videos/video-watch/video-watch.component.ts index 3aaed0487..239e24c99 100644 --- a/client/src/app/videos/video-watch/video-watch.component.ts +++ b/client/src/app/videos/video-watch/video-watch.component.ts | |||
@@ -1,18 +1,13 @@ | |||
1 | import { Component, ElementRef, OnDestroy, OnInit } from '@angular/core'; | 1 | import { Component, ElementRef, NgZone, OnDestroy, OnInit } from '@angular/core'; |
2 | import { ActivatedRoute } from '@angular/router'; | 2 | import { ActivatedRoute } from '@angular/router'; |
3 | 3 | ||
4 | import { BytesPipe } from 'angular-pipes/src/math/bytes.pipe'; | 4 | import { Video, VideoService } from '../shared'; |
5 | |||
6 | import { LoaderComponent, Video, VideoService } from '../shared'; | ||
7 | import { WebTorrentService } from './webtorrent.service'; | 5 | import { WebTorrentService } from './webtorrent.service'; |
8 | 6 | ||
9 | @Component({ | 7 | @Component({ |
10 | selector: 'my-video-watch', | 8 | selector: 'my-video-watch', |
11 | template: require('./video-watch.component.html'), | 9 | templateUrl: './video-watch.component.html', |
12 | styles: [ require('./video-watch.component.scss') ], | 10 | styleUrls: [ './video-watch.component.scss' ] |
13 | providers: [ WebTorrentService ], | ||
14 | directives: [ LoaderComponent ], | ||
15 | pipes: [ BytesPipe ] | ||
16 | }) | 11 | }) |
17 | 12 | ||
18 | export class VideoWatchComponent implements OnInit, OnDestroy { | 13 | export class VideoWatchComponent implements OnInit, OnDestroy { |
@@ -31,6 +26,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
31 | 26 | ||
32 | constructor( | 27 | constructor( |
33 | private elementRef: ElementRef, | 28 | private elementRef: ElementRef, |
29 | private ngZone: NgZone, | ||
34 | private route: ActivatedRoute, | 30 | private route: ActivatedRoute, |
35 | private videoService: VideoService, | 31 | private videoService: VideoService, |
36 | private webTorrentService: WebTorrentService | 32 | private webTorrentService: WebTorrentService |
@@ -65,12 +61,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
65 | } | 61 | } |
66 | }); | 62 | }); |
67 | 63 | ||
68 | // Refresh each second | 64 | this.runInProgress(torrent); |
69 | this.torrentInfosInterval = setInterval(() => { | ||
70 | this.downloadSpeed = torrent.downloadSpeed; | ||
71 | this.numPeers = torrent.numPeers; | ||
72 | this.uploadSpeed = torrent.uploadSpeed; | ||
73 | }, 1000); | ||
74 | }); | 65 | }); |
75 | } | 66 | } |
76 | 67 | ||
@@ -91,7 +82,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
91 | this.video = video; | 82 | this.video = video; |
92 | this.loadVideo(); | 83 | this.loadVideo(); |
93 | }, | 84 | }, |
94 | error => alert(error) | 85 | error => alert(error.text) |
95 | ); | 86 | ); |
96 | }); | 87 | }); |
97 | } | 88 | } |
@@ -100,4 +91,15 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
100 | this.error = true; | 91 | this.error = true; |
101 | console.error('The video load seems to be abnormally long.'); | 92 | console.error('The video load seems to be abnormally long.'); |
102 | } | 93 | } |
94 | |||
95 | private runInProgress(torrent: any) { | ||
96 | // Refresh each second | ||
97 | this.torrentInfosInterval = setInterval(() => { | ||
98 | this.ngZone.run(() => { | ||
99 | this.downloadSpeed = torrent.downloadSpeed; | ||
100 | this.numPeers = torrent.numPeers; | ||
101 | this.uploadSpeed = torrent.uploadSpeed; | ||
102 | }); | ||
103 | }, 1000); | ||
104 | } | ||
103 | } | 105 | } |
diff --git a/client/src/app/videos/videos.component.ts b/client/src/app/videos/videos.component.ts index 76252afbb..591e7523d 100644 --- a/client/src/app/videos/videos.component.ts +++ b/client/src/app/videos/videos.component.ts | |||
@@ -1,9 +1,7 @@ | |||
1 | import { Component } from '@angular/core'; | 1 | import { Component } from '@angular/core'; |
2 | import { ROUTER_DIRECTIVES } from '@angular/router'; | ||
3 | 2 | ||
4 | @Component({ | 3 | @Component({ |
5 | template: '<router-outlet></router-outlet>', | 4 | template: '<router-outlet></router-outlet>' |
6 | directives: [ ROUTER_DIRECTIVES ] | ||
7 | }) | 5 | }) |
8 | 6 | ||
9 | export class VideosComponent { | 7 | export class VideosComponent { |
diff --git a/client/src/app/videos/videos.routes.ts b/client/src/app/videos/videos.routes.ts index 1f088b376..074f96596 100644 --- a/client/src/app/videos/videos.routes.ts +++ b/client/src/app/videos/videos.routes.ts | |||
@@ -1,11 +1,11 @@ | |||
1 | import { RouterConfig } from '@angular/router'; | 1 | import { Routes } from '@angular/router'; |
2 | 2 | ||
3 | import { VideoAddComponent } from './video-add'; | 3 | import { VideoAddComponent } from './video-add'; |
4 | import { VideoListComponent } from './video-list'; | 4 | import { VideoListComponent } from './video-list'; |
5 | import { VideosComponent } from './videos.component'; | 5 | import { VideosComponent } from './videos.component'; |
6 | import { VideoWatchComponent } from './video-watch'; | 6 | import { VideoWatchComponent } from './video-watch'; |
7 | 7 | ||
8 | export const VideosRoutes: RouterConfig = [ | 8 | export const VideosRoutes: Routes = [ |
9 | { | 9 | { |
10 | path: 'videos', | 10 | path: 'videos', |
11 | component: VideosComponent, | 11 | component: VideosComponent, |