diff options
Diffstat (limited to 'client/src/app/videos')
10 files changed, 139 insertions, 75 deletions
diff --git a/client/src/app/videos/index.ts b/client/src/app/videos/index.ts index 9a92fa57a..a9088a907 100644 --- a/client/src/app/videos/index.ts +++ b/client/src/app/videos/index.ts | |||
@@ -2,3 +2,5 @@ export * from './shared'; | |||
2 | export * from './video-add'; | 2 | export * from './video-add'; |
3 | export * from './video-list'; | 3 | export * from './video-list'; |
4 | export * from './video-watch'; | 4 | export * from './video-watch'; |
5 | export * from './videos.component'; | ||
6 | export * from './videos.routes'; | ||
diff --git a/client/src/app/videos/shared/pagination.model.ts b/client/src/app/videos/shared/pagination.model.ts index 06f7a7875..eda44ebfb 100644 --- a/client/src/app/videos/shared/pagination.model.ts +++ b/client/src/app/videos/shared/pagination.model.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | export interface Pagination { | 1 | export interface Pagination { |
2 | currentPage: number; | 2 | currentPage: number; |
3 | itemsPerPage: number; | 3 | itemsPerPage: number; |
4 | total: number; | 4 | totalItems: number; |
5 | } | 5 | } |
diff --git a/client/src/app/videos/video-list/video-list.component.html b/client/src/app/videos/video-list/video-list.component.html index 52abc3dc2..0e17ef2c4 100644 --- a/client/src/app/videos/video-list/video-list.component.html +++ b/client/src/app/videos/video-list/video-list.component.html | |||
@@ -1,8 +1,8 @@ | |||
1 | <div class="row videos-info"> | 1 | <div class="row videos-info"> |
2 | <div class="col-md-9 videos-total-results"> | 2 | <div class="col-md-9 videos-total-results"> |
3 | {{ pagination.total }} videos | 3 | <span *ngIf="pagination.totalItems !== null">{{ pagination.totalItems }} videos</span> |
4 | 4 | ||
5 | <my-loader [loading]="loading"></my-loader> | 5 | <my-loader [loading]="loading | async"></my-loader> |
6 | </div> | 6 | </div> |
7 | <my-video-sort class="col-md-3" [currentSort]="sort" (sort)="onSort($event)"></my-video-sort> | 7 | <my-video-sort class="col-md-3" [currentSort]="sort" (sort)="onSort($event)"></my-video-sort> |
8 | </div> | 8 | </div> |
@@ -14,7 +14,7 @@ | |||
14 | </my-video-miniature> | 14 | </my-video-miniature> |
15 | </div> | 15 | </div> |
16 | 16 | ||
17 | <pagination | 17 | <pagination *ngIf="pagination.totalItems !== null" |
18 | [totalItems]="pagination.total" [itemsPerPage]="pagination.itemsPerPage" [maxSize]="6" [boundaryLinks]="true" [rotate]="false" | 18 | [totalItems]="pagination.totalItems" [itemsPerPage]="pagination.itemsPerPage" [maxSize]="6" [boundaryLinks]="true" [rotate]="false" |
19 | (ngModelChange)="getVideos()" [(ngModel)]="pagination.currentPage" | 19 | [(ngModel)]="pagination.currentPage" (pageChanged)="getVideos()" |
20 | ></pagination> | 20 | ></pagination> |
diff --git a/client/src/app/videos/video-list/video-list.component.scss b/client/src/app/videos/video-list/video-list.component.scss index dc2c065d8..1f491a6c3 100644 --- a/client/src/app/videos/video-list/video-list.component.scss +++ b/client/src/app/videos/video-list/video-list.component.scss | |||
@@ -24,7 +24,7 @@ | |||
24 | } | 24 | } |
25 | 25 | ||
26 | .videos-miniatures { | 26 | .videos-miniatures { |
27 | min-height: 600px; | 27 | min-height: 650px; |
28 | 28 | ||
29 | my-video-miniature { | 29 | my-video-miniature { |
30 | transition: all 0.5s ease; | 30 | transition: all 0.5s ease; |
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 46263eb65..0ebf0ef5c 100644 --- a/client/src/app/videos/video-list/video-list.component.ts +++ b/client/src/app/videos/video-list/video-list.component.ts | |||
@@ -1,5 +1,7 @@ | |||
1 | import { Component, OnInit } from '@angular/core'; | 1 | import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; |
2 | import { Router, ROUTER_DIRECTIVES, RouteSegment } from '@angular/router'; | 2 | import { AsyncPipe } from '@angular/common'; |
3 | import { ActivatedRoute, Router, ROUTER_DIRECTIVES } from '@angular/router'; | ||
4 | import { BehaviorSubject } from 'rxjs/BehaviorSubject'; | ||
3 | 5 | ||
4 | import { PAGINATION_DIRECTIVES } from 'ng2-bootstrap/components/pagination'; | 6 | import { PAGINATION_DIRECTIVES } from 'ng2-bootstrap/components/pagination'; |
5 | 7 | ||
@@ -18,52 +20,64 @@ import { SearchService } from '../../shared'; | |||
18 | @Component({ | 20 | @Component({ |
19 | selector: 'my-videos-list', | 21 | selector: 'my-videos-list', |
20 | styles: [ require('./video-list.component.scss') ], | 22 | styles: [ require('./video-list.component.scss') ], |
23 | pipes: [ AsyncPipe ], | ||
21 | template: require('./video-list.component.html'), | 24 | template: require('./video-list.component.html'), |
22 | directives: [ LoaderComponent, PAGINATION_DIRECTIVES, ROUTER_DIRECTIVES, VideoMiniatureComponent, VideoSortComponent ] | 25 | directives: [ LoaderComponent, PAGINATION_DIRECTIVES, ROUTER_DIRECTIVES, VideoMiniatureComponent, VideoSortComponent ] |
23 | }) | 26 | }) |
24 | 27 | ||
25 | export class VideoListComponent implements OnInit { | 28 | export class VideoListComponent implements OnInit, OnDestroy { |
26 | loading = false; | 29 | loading: BehaviorSubject<boolean> = new BehaviorSubject(false); |
27 | pagination: Pagination = { | 30 | pagination: Pagination = { |
28 | currentPage: 1, | 31 | currentPage: 1, |
29 | itemsPerPage: 9, | 32 | itemsPerPage: 9, |
30 | total: 0 | 33 | totalItems: null |
31 | }; | 34 | }; |
32 | sort: SortField; | 35 | sort: SortField; |
33 | user: User = null; | 36 | user: User = null; |
34 | videos: Video[] = []; | 37 | videos: Video[] = []; |
35 | 38 | ||
36 | private search: Search; | 39 | private search: Search; |
40 | private sub: any; | ||
37 | 41 | ||
38 | constructor( | 42 | constructor( |
39 | private authService: AuthService, | 43 | private authService: AuthService, |
44 | private changeDetector: ChangeDetectorRef, | ||
40 | private router: Router, | 45 | private router: Router, |
41 | private routeSegment: RouteSegment, | 46 | private route: ActivatedRoute, |
42 | private videoService: VideoService, | 47 | private videoService: VideoService, |
43 | private searchService: SearchService // Temporary | 48 | private searchService: SearchService |
44 | ) {} | 49 | ) {} |
45 | 50 | ||
46 | ngOnInit() { | 51 | ngOnInit() { |
47 | if (this.authService.isLoggedIn()) { | 52 | this.sub = this.route.params.subscribe(routeParams => { |
48 | this.user = User.load(); | 53 | if (this.authService.isLoggedIn()) { |
49 | } | 54 | this.user = User.load(); |
55 | } | ||
50 | 56 | ||
51 | this.search = { | 57 | this.search = { |
52 | value: this.routeSegment.getParam('search'), | 58 | value: routeParams['search'], |
53 | field: <SearchField>this.routeSegment.getParam('field') | 59 | field: <SearchField>routeParams['field'] |
54 | }; | 60 | }; |
55 | 61 | ||
56 | // Temporary | 62 | // Update the search service component |
57 | this.searchChanged(this.search); | 63 | this.searchService.searchChanged.next(this.search); |
58 | 64 | ||
59 | this.sort = <SortField>this.routeSegment.getParam('sort') || '-createdDate'; | 65 | this.sort = <SortField>routeParams['sort'] || '-createdDate'; |
66 | |||
67 | this.getVideos(); | ||
68 | }); | ||
69 | } | ||
60 | 70 | ||
61 | this.getVideos(); | 71 | ngOnDestroy() { |
72 | this.sub.unsubscribe(); | ||
62 | } | 73 | } |
63 | 74 | ||
64 | getVideos() { | 75 | getVideos(detectChanges = true) { |
65 | this.loading = true; | 76 | this.loading.next(true); |
66 | this.videos = []; | 77 | this.videos = []; |
78 | this.pagination.currentPage = 1; | ||
79 | |||
80 | this.changeDetector.detectChanges(); | ||
67 | 81 | ||
68 | let observable = null; | 82 | let observable = null; |
69 | 83 | ||
@@ -76,9 +90,9 @@ export class VideoListComponent implements OnInit { | |||
76 | observable.subscribe( | 90 | observable.subscribe( |
77 | ({ videos, totalVideos }) => { | 91 | ({ videos, totalVideos }) => { |
78 | this.videos = videos; | 92 | this.videos = videos; |
79 | this.pagination.total = totalVideos; | 93 | this.pagination.totalItems = totalVideos; |
80 | 94 | ||
81 | this.loading = false; | 95 | this.loading.next(false); |
82 | }, | 96 | }, |
83 | error => alert(error) | 97 | error => alert(error) |
84 | ); | 98 | ); |
@@ -89,7 +103,7 @@ export class VideoListComponent implements OnInit { | |||
89 | } | 103 | } |
90 | 104 | ||
91 | onRemoved(video: Video) { | 105 | onRemoved(video: Video) { |
92 | this.getVideos(); | 106 | this.getVideos(false); |
93 | } | 107 | } |
94 | 108 | ||
95 | onSort(sort: SortField) { | 109 | onSort(sort: SortField) { |
@@ -106,8 +120,4 @@ export class VideoListComponent implements OnInit { | |||
106 | 120 | ||
107 | this.router.navigate(['/videos/list', params]); | 121 | this.router.navigate(['/videos/list', params]); |
108 | } | 122 | } |
109 | |||
110 | searchChanged(search: Search) { | ||
111 | this.searchService.searchChanged.next(search); | ||
112 | } | ||
113 | } | 123 | } |
diff --git a/client/src/app/videos/video-list/video-miniature.component.html b/client/src/app/videos/video-list/video-miniature.component.html index 92e19bb8b..3cf28620e 100644 --- a/client/src/app/videos/video-list/video-miniature.component.html +++ b/client/src/app/videos/video-list/video-miniature.component.html | |||
@@ -13,14 +13,16 @@ | |||
13 | 13 | ||
14 | <div class="video-miniature-informations"> | 14 | <div class="video-miniature-informations"> |
15 | <span class="video-miniature-name-tags"> | 15 | <span class="video-miniature-name-tags"> |
16 | <a [routerLink]="['/videos/watch', video.id]" class="video-miniature-name">{{ video.name }}</a> | 16 | <a [routerLink]="['/videos/watch', video.id]" [attr.title] class="video-miniature-name">{{ video.name }}</a> |
17 | 17 | ||
18 | <span *ngFor="let tag of video.tags" class="video-miniature-tag"> | 18 | <div class="video-miniature-tags"> |
19 | <a [routerLink]="['/videos/list', { field: 'tags', search: tag }]" class="label label-primary">{{ tag }}</a> | 19 | <span *ngFor="let tag of video.tags" class="video-miniature-tag"> |
20 | </span> | 20 | <a [routerLink]="['/videos/list', { field: 'tags', search: tag }]" class="label label-primary">{{ tag }}</a> |
21 | </span> | ||
22 | </div> | ||
21 | </span> | 23 | </span> |
22 | 24 | ||
23 | <a [routerLink]="['/videos/list', { field: 'author', search: video.author }]" class="video-miniature-author">by {{ video.by }}</a> | 25 | <a [routerLink]="['/videos/list', { field: 'author', search: video.author }]" class="video-miniature-author">{{ video.by }}</a> |
24 | <span class="video-miniature-created-date">on {{ video.createdDate | date:'short' }}</span> | 26 | <span class="video-miniature-created-date">{{ video.createdDate | date:'short' }}</span> |
25 | </div> | 27 | </div> |
26 | </div> | 28 | </div> |
diff --git a/client/src/app/videos/video-list/video-miniature.component.scss b/client/src/app/videos/video-list/video-miniature.component.scss index 40d37b83f..3a096dabd 100644 --- a/client/src/app/videos/video-list/video-miniature.component.scss +++ b/client/src/app/videos/video-list/video-miniature.component.scss | |||
@@ -1,3 +1,5 @@ | |||
1 | @import "../../../sass/pre-customizations.scss"; | ||
2 | |||
1 | .video-miniature { | 3 | .video-miniature { |
2 | margin-top: 30px; | 4 | margin-top: 30px; |
3 | display: inline-block; | 5 | display: inline-block; |
@@ -34,34 +36,40 @@ | |||
34 | } | 36 | } |
35 | 37 | ||
36 | .video-miniature-informations { | 38 | .video-miniature-informations { |
37 | margin-left: 3px; | ||
38 | width: 200px; | 39 | width: 200px; |
39 | 40 | ||
40 | .video-miniature-name-tags { | 41 | .video-miniature-name-tags { |
41 | display: block; | 42 | display: block; |
42 | 43 | ||
43 | .video-miniature-name { | 44 | .video-miniature-name { |
45 | height: 23px; | ||
46 | display: block; | ||
47 | overflow: hidden; | ||
48 | text-overflow: ellipsis; | ||
49 | white-space: nowrap; | ||
44 | font-weight: bold; | 50 | font-weight: bold; |
51 | transition: color 0.2s; | ||
52 | font-size: 15px; | ||
45 | 53 | ||
46 | &:hover { | 54 | &:hover { |
47 | text-decoration: none; | 55 | text-decoration: none; |
48 | } | 56 | } |
49 | |||
50 | &::after { | ||
51 | content: '\002022'; | ||
52 | margin-left: 3px; | ||
53 | } | ||
54 | } | 57 | } |
55 | 58 | ||
56 | .video-miniature-tag { | 59 | .video-miniature-tags { |
57 | font-size: 12px; | 60 | // Fix for chrome when tags a long |
58 | cursor: pointer; | 61 | width: 201px; |
59 | transition: opacity 0.5s; | ||
60 | position: relative; | ||
61 | top: -2px; | ||
62 | 62 | ||
63 | &:hover { | 63 | .video-miniature-tag { |
64 | opacity: 0.9; | 64 | font-size: 13px; |
65 | cursor: pointer; | ||
66 | position: relative; | ||
67 | top: -2px; | ||
68 | |||
69 | .label { | ||
70 | line-height: $line-height-base; | ||
71 | transition: background-color 0.2s; | ||
72 | } | ||
65 | } | 73 | } |
66 | } | 74 | } |
67 | } | 75 | } |
@@ -69,16 +77,17 @@ | |||
69 | .video-miniature-author, .video-miniature-created-date { | 77 | .video-miniature-author, .video-miniature-created-date { |
70 | display: block; | 78 | display: block; |
71 | margin-left: 1px; | 79 | margin-left: 1px; |
72 | font-size: 11px; | 80 | font-size: 12px; |
73 | color: rgb(54, 118, 173); | 81 | color: #337ab7; |
82 | opacity: 0.9; | ||
74 | } | 83 | } |
75 | 84 | ||
76 | .video-miniature-author { | 85 | .video-miniature-author { |
77 | transition: opacity 0.5s; | 86 | transition: color 0.2s; |
78 | 87 | ||
79 | &:hover { | 88 | &:hover { |
89 | color: #23527c; | ||
80 | text-decoration: none; | 90 | text-decoration: none; |
81 | opacity: 0.9; | ||
82 | } | 91 | } |
83 | } | 92 | } |
84 | } | 93 | } |
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 99188bfb3..09255de5d 100644 --- a/client/src/app/videos/video-watch/video-watch.component.ts +++ b/client/src/app/videos/video-watch/video-watch.component.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import { Component, ElementRef, OnInit } from '@angular/core'; | 1 | import { Component, ElementRef, OnDestroy, OnInit } from '@angular/core'; |
2 | import { CanDeactivate, RouteSegment } from '@angular/router'; | 2 | import { ActivatedRoute } from '@angular/router'; |
3 | 3 | ||
4 | import { BytesPipe } from 'angular-pipes/src/math/bytes.pipe'; | 4 | import { BytesPipe } from 'angular-pipes/src/math/bytes.pipe'; |
5 | 5 | ||
@@ -15,7 +15,7 @@ import { WebTorrentService } from './webtorrent.service'; | |||
15 | pipes: [ BytesPipe ] | 15 | pipes: [ BytesPipe ] |
16 | }) | 16 | }) |
17 | 17 | ||
18 | export class VideoWatchComponent implements OnInit, CanDeactivate { | 18 | export class VideoWatchComponent implements OnInit, OnDestroy { |
19 | private static LOADTIME_TOO_LONG: number = 30000; | 19 | private static LOADTIME_TOO_LONG: number = 30000; |
20 | 20 | ||
21 | downloadSpeed: number; | 21 | downloadSpeed: number; |
@@ -26,11 +26,12 @@ export class VideoWatchComponent implements OnInit, CanDeactivate { | |||
26 | video: Video; | 26 | video: Video; |
27 | 27 | ||
28 | private errorTimer: NodeJS.Timer; | 28 | private errorTimer: NodeJS.Timer; |
29 | private sub: any; | ||
29 | private torrentInfosInterval: NodeJS.Timer; | 30 | private torrentInfosInterval: NodeJS.Timer; |
30 | 31 | ||
31 | constructor( | 32 | constructor( |
32 | private elementRef: ElementRef, | 33 | private elementRef: ElementRef, |
33 | private routeSegment: RouteSegment, | 34 | private route: ActivatedRoute, |
34 | private videoService: VideoService, | 35 | private videoService: VideoService, |
35 | private webTorrentService: WebTorrentService | 36 | private webTorrentService: WebTorrentService |
36 | ) {} | 37 | ) {} |
@@ -73,22 +74,25 @@ export class VideoWatchComponent implements OnInit, CanDeactivate { | |||
73 | }); | 74 | }); |
74 | } | 75 | } |
75 | 76 | ||
76 | ngOnInit() { | 77 | ngOnDestroy() { |
77 | let id = this.routeSegment.getParam('id'); | ||
78 | this.videoService.getVideo(id).subscribe( | ||
79 | video => { | ||
80 | this.video = video; | ||
81 | this.loadVideo(); | ||
82 | }, | ||
83 | error => alert(error) | ||
84 | ); | ||
85 | } | ||
86 | |||
87 | routerCanDeactivate() { | ||
88 | console.log('Removing video from webtorrent.'); | 78 | console.log('Removing video from webtorrent.'); |
89 | clearInterval(this.torrentInfosInterval); | 79 | clearInterval(this.torrentInfosInterval); |
90 | this.webTorrentService.remove(this.video.magnetUri); | 80 | this.webTorrentService.remove(this.video.magnetUri); |
91 | return Promise.resolve(true); | 81 | |
82 | this.sub.unsubscribe(); | ||
83 | } | ||
84 | |||
85 | ngOnInit() { | ||
86 | this.sub = this.route.params.subscribe(routeParams => { | ||
87 | let id = routeParams['id']; | ||
88 | this.videoService.getVideo(id).subscribe( | ||
89 | video => { | ||
90 | this.video = video; | ||
91 | this.loadVideo(); | ||
92 | }, | ||
93 | error => alert(error) | ||
94 | ); | ||
95 | }); | ||
92 | } | 96 | } |
93 | 97 | ||
94 | private loadTooLong() { | 98 | private loadTooLong() { |
diff --git a/client/src/app/videos/videos.component.ts b/client/src/app/videos/videos.component.ts new file mode 100644 index 000000000..76252afbb --- /dev/null +++ b/client/src/app/videos/videos.component.ts | |||
@@ -0,0 +1,10 @@ | |||
1 | import { Component } from '@angular/core'; | ||
2 | import { ROUTER_DIRECTIVES } from '@angular/router'; | ||
3 | |||
4 | @Component({ | ||
5 | template: '<router-outlet></router-outlet>', | ||
6 | directives: [ ROUTER_DIRECTIVES ] | ||
7 | }) | ||
8 | |||
9 | export class VideosComponent { | ||
10 | } | ||
diff --git a/client/src/app/videos/videos.routes.ts b/client/src/app/videos/videos.routes.ts new file mode 100644 index 000000000..1f088b376 --- /dev/null +++ b/client/src/app/videos/videos.routes.ts | |||
@@ -0,0 +1,27 @@ | |||
1 | import { RouterConfig } from '@angular/router'; | ||
2 | |||
3 | import { VideoAddComponent } from './video-add'; | ||
4 | import { VideoListComponent } from './video-list'; | ||
5 | import { VideosComponent } from './videos.component'; | ||
6 | import { VideoWatchComponent } from './video-watch'; | ||
7 | |||
8 | export const VideosRoutes: RouterConfig = [ | ||
9 | { | ||
10 | path: 'videos', | ||
11 | component: VideosComponent, | ||
12 | children: [ | ||
13 | { | ||
14 | path: 'list', | ||
15 | component: VideoListComponent | ||
16 | }, | ||
17 | { | ||
18 | path: 'add', | ||
19 | component: VideoAddComponent | ||
20 | }, | ||
21 | { | ||
22 | path: 'watch/:id', | ||
23 | component: VideoWatchComponent | ||
24 | } | ||
25 | ] | ||
26 | } | ||
27 | ]; | ||