aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/videos/video-list/shared
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/app/videos/video-list/shared')
-rw-r--r--client/src/app/videos/video-list/shared/abstract-video-list.html28
-rw-r--r--client/src/app/videos/video-list/shared/abstract-video-list.scss37
-rw-r--r--client/src/app/videos/video-list/shared/abstract-video-list.ts104
-rw-r--r--client/src/app/videos/video-list/shared/index.ts4
-rw-r--r--client/src/app/videos/video-list/shared/loader.component.html3
-rw-r--r--client/src/app/videos/video-list/shared/loader.component.ts11
-rw-r--r--client/src/app/videos/video-list/shared/video-miniature.component.html33
-rw-r--r--client/src/app/videos/video-list/shared/video-miniature.component.scss102
-rw-r--r--client/src/app/videos/video-list/shared/video-miniature.component.ts19
-rw-r--r--client/src/app/videos/video-list/shared/video-sort.component.html5
-rw-r--r--client/src/app/videos/video-list/shared/video-sort.component.ts39
11 files changed, 385 insertions, 0 deletions
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
new file mode 100644
index 000000000..680fba3f5
--- /dev/null
+++ b/client/src/app/videos/video-list/shared/abstract-video-list.html
@@ -0,0 +1,28 @@
1<div class="row">
2 <div class="content-padding">
3 <div class="videos-info">
4 <div class="col-md-9 col-xs-5 videos-total-results">
5 <span *ngIf="pagination.totalItems !== null">{{ pagination.totalItems }} videos</span>
6
7 <my-loader [loading]="loading | async"></my-loader>
8 </div>
9
10 <my-video-sort class="col-md-3 col-xs-7" [currentSort]="sort" (sort)="onSort($event)"></my-video-sort>
11 </div>
12 </div>
13</div>
14
15<div class="content-padding videos-miniatures">
16 <div class="no-video" *ngIf="isThereNoVideo()">There is no video.</div>
17
18 <my-video-miniature
19 class="ng-animate"
20 *ngFor="let video of videos" [video]="video" [user]="user" [currentSort]="sort"
21 >
22 </my-video-miniature>
23</div>
24
25<pagination *ngIf="pagination.totalItems !== null && pagination.totalItems !== 0"
26 [totalItems]="pagination.totalItems" [itemsPerPage]="pagination.itemsPerPage" [maxSize]="6" [boundaryLinks]="true" [rotate]="false"
27 [(ngModel)]="pagination.currentPage" (pageChanged)="onPageChanged($event)"
28></pagination>
diff --git a/client/src/app/videos/video-list/shared/abstract-video-list.scss b/client/src/app/videos/video-list/shared/abstract-video-list.scss
new file mode 100644
index 000000000..4b4409602
--- /dev/null
+++ b/client/src/app/videos/video-list/shared/abstract-video-list.scss
@@ -0,0 +1,37 @@
1.videos-info {
2 @media screen and (max-width: 400px) {
3 margin-left: 0;
4 }
5
6 border-bottom: 1px solid #f1f1f1;
7 height: 40px;
8 line-height: 40px;
9
10 .videos-total-results {
11 font-size: 13px;
12 }
13
14 my-loader {
15 display: inline-block;
16 margin-left: 5px;
17 }
18}
19
20.videos-miniatures {
21 text-align: center;
22 padding-top: 0;
23
24 my-video-miniature {
25 text-align: left;
26 }
27
28 .no-video {
29 margin-top: 50px;
30 text-align: center;
31 }
32}
33
34pagination {
35 display: block;
36 text-align: center;
37}
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
new file mode 100644
index 000000000..87d5bc48a
--- /dev/null
+++ b/client/src/app/videos/video-list/shared/abstract-video-list.ts
@@ -0,0 +1,104 @@
1import { OnDestroy, OnInit } from '@angular/core'
2import { ActivatedRoute, Router } from '@angular/router'
3import { Subscription } from 'rxjs/Subscription'
4import { BehaviorSubject } from 'rxjs/BehaviorSubject'
5import { Observable } from 'rxjs/Observable'
6
7import { NotificationsService } from 'angular2-notifications'
8
9import {
10 SortField,
11 Video,
12 VideoPagination
13} from '../../shared'
14
15export abstract class AbstractVideoList implements OnInit, OnDestroy {
16 loading: BehaviorSubject<boolean> = new BehaviorSubject(false)
17 pagination: VideoPagination = {
18 currentPage: 1,
19 itemsPerPage: 25,
20 totalItems: null
21 }
22 sort: SortField
23 videos: Video[] = []
24
25 protected notificationsService: NotificationsService
26 protected router: Router
27 protected route: ActivatedRoute
28
29 protected subActivatedRoute: Subscription
30
31 abstract getVideosObservable (): Observable<{ videos: Video[], totalVideos: number}>
32
33 ngOnInit () {
34 // Subscribe to route changes
35 this.subActivatedRoute = this.route.params.subscribe(routeParams => {
36 this.loadRouteParams(routeParams)
37
38 this.getVideos()
39 })
40 }
41
42 ngOnDestroy () {
43 this.subActivatedRoute.unsubscribe()
44 }
45
46 getVideos () {
47 this.loading.next(true)
48 this.videos = []
49
50 const observable = this.getVideosObservable()
51
52 observable.subscribe(
53 ({ videos, totalVideos }) => {
54 this.videos = videos
55 this.pagination.totalItems = totalVideos
56
57 this.loading.next(false)
58 },
59 error => this.notificationsService.error('Error', error.text)
60 )
61 }
62
63 isThereNoVideo () {
64 return !this.loading.getValue() && this.videos.length === 0
65 }
66
67 onPageChanged (event: { page: number }) {
68 // Be sure the current page is set
69 this.pagination.currentPage = event.page
70
71 this.navigateToNewParams()
72 }
73
74 onSort (sort: SortField) {
75 this.sort = sort
76
77 this.navigateToNewParams()
78 }
79
80 protected buildRouteParams () {
81 // There is always a sort and a current page
82 const params = {
83 sort: this.sort,
84 page: this.pagination.currentPage
85 }
86
87 return params
88 }
89
90 protected loadRouteParams (routeParams: { [ key: string ]: any }) {
91 this.sort = routeParams['sort'] as SortField || '-createdAt'
92
93 if (routeParams['page'] !== undefined) {
94 this.pagination.currentPage = parseInt(routeParams['page'], 10)
95 } else {
96 this.pagination.currentPage = 1
97 }
98 }
99
100 protected navigateToNewParams () {
101 const routeParams = this.buildRouteParams()
102 this.router.navigate([ '/videos/list', routeParams ])
103 }
104}
diff --git a/client/src/app/videos/video-list/shared/index.ts b/client/src/app/videos/video-list/shared/index.ts
new file mode 100644
index 000000000..2c9804e6d
--- /dev/null
+++ b/client/src/app/videos/video-list/shared/index.ts
@@ -0,0 +1,4 @@
1export * from './abstract-video-list'
2export * from './loader.component'
3export * from './video-miniature.component'
4export * from './video-sort.component'
diff --git a/client/src/app/videos/video-list/shared/loader.component.html b/client/src/app/videos/video-list/shared/loader.component.html
new file mode 100644
index 000000000..38d06950e
--- /dev/null
+++ b/client/src/app/videos/video-list/shared/loader.component.html
@@ -0,0 +1,3 @@
1<div id="video-loading" *ngIf="loading">
2 <div class="glyphicon glyphicon-refresh glyphicon-refresh-animate"></div>
3</div>
diff --git a/client/src/app/videos/video-list/shared/loader.component.ts b/client/src/app/videos/video-list/shared/loader.component.ts
new file mode 100644
index 000000000..f37d70c85
--- /dev/null
+++ b/client/src/app/videos/video-list/shared/loader.component.ts
@@ -0,0 +1,11 @@
1import { Component, Input } from '@angular/core'
2
3@Component({
4 selector: 'my-loader',
5 styleUrls: [ ],
6 templateUrl: './loader.component.html'
7})
8
9export class LoaderComponent {
10 @Input() loading: boolean
11}
diff --git a/client/src/app/videos/video-list/shared/video-miniature.component.html b/client/src/app/videos/video-list/shared/video-miniature.component.html
new file mode 100644
index 000000000..abe87025f
--- /dev/null
+++ b/client/src/app/videos/video-list/shared/video-miniature.component.html
@@ -0,0 +1,33 @@
1<div class="video-miniature">
2 <a
3 [routerLink]="['/videos/watch', video.uuid]" [attr.title]="video.description"
4 class="video-miniature-thumbnail"
5 >
6 <img [attr.src]="video.thumbnailUrl" alt="video thumbnail" [ngClass]="{ 'blur-filter': isVideoNSFWForThisUser() }" />
7
8 <div class="video-miniature-thumbnail-overlay">
9 <span class="video-miniature-thumbnail-overlay-views">{{ video.views }} views</span>
10 <span class="video-miniature-thumbnail-overlay-duration">{{ video.durationLabel }}</span>
11 </div>
12 </a>
13
14 <div class="video-miniature-information">
15 <span class="video-miniature-name">
16 <a
17 class="video-miniature-name"
18 [routerLink]="['/videos/watch', video.uuid]" [attr.title]="video.name" [ngClass]="{ 'blur-filter': isVideoNSFWForThisUser() }"
19 >
20 {{ video.name }}
21 </a>
22 </span>
23
24 <div class="video-miniature-tags">
25 <span *ngFor="let tag of video.tags" class="video-miniature-tag">
26 <a [routerLink]="['/videos/list', { field: 'tags', search: tag, sort: currentSort }]" class="label label-primary">{{ tag }}</a>
27 </span>
28 </div>
29
30 <a [routerLink]="['/videos/list', { field: 'author', search: video.author, sort: currentSort }]" class="video-miniature-author">{{ video.by }}</a>
31 <span class="video-miniature-created-at">{{ video.createdAt | date:'short' }}</span>
32 </div>
33</div>
diff --git a/client/src/app/videos/video-list/shared/video-miniature.component.scss b/client/src/app/videos/video-list/shared/video-miniature.component.scss
new file mode 100644
index 000000000..066792d10
--- /dev/null
+++ b/client/src/app/videos/video-list/shared/video-miniature.component.scss
@@ -0,0 +1,102 @@
1.video-miniature {
2 margin-top: 30px;
3 display: inline-block;
4 position: relative;
5 height: 190px;
6 width: 220px;
7 vertical-align: top;
8
9 .video-miniature-thumbnail {
10 display: inline-block;
11 position: relative;
12 border-radius: 3px;
13 overflow: hidden;
14
15 &:hover {
16 text-decoration: none !important;
17 }
18
19 img.blur-filter {
20 filter: blur(5px);
21 transform : scale(1.03);
22 }
23
24 .video-miniature-thumbnail-overlay {
25 position: absolute;
26 right: 0px;
27 bottom: 0px;
28 display: inline-block;
29 background-color: rgba(0, 0, 0, 0.7);
30 color: #fff;
31 padding: 3px 5px;
32 font-size: 11px;
33 font-weight: bold;
34 width: 100%;
35
36 .video-miniature-thumbnail-overlay-views {
37
38 }
39
40 .video-miniature-thumbnail-overlay-duration {
41 float: right;
42 }
43 }
44 }
45
46 .video-miniature-information {
47 width: 200px;
48
49 .video-miniature-name {
50 height: 23px;
51 display: block;
52 overflow: hidden;
53 text-overflow: ellipsis;
54 white-space: nowrap;
55 font-weight: bold;
56 transition: color 0.2s;
57 font-size: 15px;
58
59 &:hover {
60 text-decoration: none;
61 }
62
63 &.blur-filter {
64 filter: blur(3px);
65 padding-left: 4px;
66 }
67
68 .video-miniature-tags {
69 // Fix for chrome when tags are long
70 width: 201px;
71
72 .video-miniature-tag {
73 font-size: 13px;
74 cursor: pointer;
75 position: relative;
76 top: -2px;
77
78 .label {
79 transition: background-color 0.2s;
80 }
81 }
82 }
83 }
84
85 .video-miniature-author, .video-miniature-created-at {
86 display: block;
87 margin-left: 1px;
88 font-size: 11px;
89 color: $video-miniature-other-infos;
90 opacity: 0.9;
91 }
92
93 .video-miniature-author {
94 transition: color 0.2s;
95
96 &:hover {
97 color: #23527c;
98 text-decoration: none;
99 }
100 }
101 }
102}
diff --git a/client/src/app/videos/video-list/shared/video-miniature.component.ts b/client/src/app/videos/video-list/shared/video-miniature.component.ts
new file mode 100644
index 000000000..e5a87907b
--- /dev/null
+++ b/client/src/app/videos/video-list/shared/video-miniature.component.ts
@@ -0,0 +1,19 @@
1import { Component, Input } from '@angular/core'
2
3import { SortField, Video } from '../../shared'
4import { User } from '../../../shared'
5
6@Component({
7 selector: 'my-video-miniature',
8 styleUrls: [ './video-miniature.component.scss' ],
9 templateUrl: './video-miniature.component.html'
10})
11export class VideoMiniatureComponent {
12 @Input() currentSort: SortField
13 @Input() user: User
14 @Input() video: Video
15
16 isVideoNSFWForThisUser () {
17 return this.video.isVideoNSFWForUser(this.user)
18 }
19}
diff --git a/client/src/app/videos/video-list/shared/video-sort.component.html b/client/src/app/videos/video-list/shared/video-sort.component.html
new file mode 100644
index 000000000..3bece0b22
--- /dev/null
+++ b/client/src/app/videos/video-list/shared/video-sort.component.html
@@ -0,0 +1,5 @@
1<select class="form-control input-sm" [(ngModel)]="currentSort" (ngModelChange)="onSortChange()">
2 <option *ngFor="let choice of choiceKeys" [value]="choice">
3 {{ getStringChoice(choice) }}
4 </option>
5</select>
diff --git a/client/src/app/videos/video-list/shared/video-sort.component.ts b/client/src/app/videos/video-list/shared/video-sort.component.ts
new file mode 100644
index 000000000..8aa89d32b
--- /dev/null
+++ b/client/src/app/videos/video-list/shared/video-sort.component.ts
@@ -0,0 +1,39 @@
1import { Component, EventEmitter, Input, Output } from '@angular/core'
2
3import { SortField } from '../../shared'
4
5@Component({
6 selector: 'my-video-sort',
7 templateUrl: './video-sort.component.html'
8})
9
10export class VideoSortComponent {
11 @Output() sort = new EventEmitter<any>()
12
13 @Input() currentSort: SortField
14
15 sortChoices: { [ P in SortField ]: string } = {
16 'name': 'Name - Asc',
17 '-name': 'Name - Desc',
18 'duration': 'Duration - Asc',
19 '-duration': 'Duration - Desc',
20 'createdAt': 'Created Date - Asc',
21 '-createdAt': 'Created Date - Desc',
22 'views': 'Views - Asc',
23 '-views': 'Views - Desc',
24 'likes': 'Likes - Asc',
25 '-likes': 'Likes - Desc'
26 }
27
28 get choiceKeys () {
29 return Object.keys(this.sortChoices)
30 }
31
32 getStringChoice (choiceKey: SortField) {
33 return this.sortChoices[choiceKey]
34 }
35
36 onSortChange () {
37 this.sort.emit(this.currentSort)
38 }
39}