Begin videos list new design
This commit is contained in:
parent
26c6ee80d0
commit
9bf9d2a5c2
|
@ -43,7 +43,6 @@
|
||||||
"@types/webpack": "^3.0.0",
|
"@types/webpack": "^3.0.0",
|
||||||
"@types/webtorrent": "^0.98.4",
|
"@types/webtorrent": "^0.98.4",
|
||||||
"add-asset-html-webpack-plugin": "^2.0.1",
|
"add-asset-html-webpack-plugin": "^2.0.1",
|
||||||
"angular-pipes": "^6.0.0",
|
|
||||||
"angular2-notifications": "^0.7.7",
|
"angular2-notifications": "^0.7.7",
|
||||||
"angular2-template-loader": "^0.6.0",
|
"angular2-template-loader": "^0.6.0",
|
||||||
"assets-webpack-plugin": "^3.4.0",
|
"assets-webpack-plugin": "^3.4.0",
|
||||||
|
@ -72,6 +71,7 @@
|
||||||
"ngc-webpack": "3.2.2",
|
"ngc-webpack": "3.2.2",
|
||||||
"ngx-bootstrap": "2.0.0-beta.9",
|
"ngx-bootstrap": "2.0.0-beta.9",
|
||||||
"ngx-chips": "1.5.3",
|
"ngx-chips": "1.5.3",
|
||||||
|
"ngx-pipes": "^2.0.5",
|
||||||
"node-sass": "^4.1.1",
|
"node-sass": "^4.1.1",
|
||||||
"normalize.css": "^7.0.0",
|
"normalize.css": "^7.0.0",
|
||||||
"optimize-js-plugin": "0.0.4",
|
"optimize-js-plugin": "0.0.4",
|
||||||
|
|
|
@ -26,12 +26,12 @@
|
||||||
<div class="panel-block">
|
<div class="panel-block">
|
||||||
<div class="block-title">Videos</div>
|
<div class="block-title">Videos</div>
|
||||||
|
|
||||||
<a routerLink="/videos/list" routerLinkActive="active">
|
<a routerLink="/videos/trending" routerLinkActive="active">
|
||||||
<span class="icon icon-videos-trending"></span>
|
<span class="icon icon-videos-trending"></span>
|
||||||
Trending
|
Trending
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a routerLink="/videos/list" routerLinkActive="active">
|
<a routerLink="/videos/recently-added" routerLinkActive="active">
|
||||||
<span class="icon icon-videos-recently-added"></span>
|
<span class="icon icon-videos-recently-added"></span>
|
||||||
Recently added
|
Recently added
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
import { Pipe, PipeTransform } from '@angular/core'
|
||||||
|
|
||||||
|
// Thanks: https://github.com/danrevah/ngx-pipes/blob/master/src/pipes/math/bytes.ts
|
||||||
|
|
||||||
|
@Pipe({name: 'fromNow'})
|
||||||
|
export class FromNowPipe implements PipeTransform {
|
||||||
|
|
||||||
|
transform (value: number) {
|
||||||
|
const seconds = Math.floor((Date.now() - value) / 1000)
|
||||||
|
|
||||||
|
let interval = Math.floor(seconds / 31536000)
|
||||||
|
if (interval > 1) {
|
||||||
|
return interval + ' years ago'
|
||||||
|
}
|
||||||
|
|
||||||
|
interval = Math.floor(seconds / 2592000)
|
||||||
|
if (interval > 1) return interval + ' months ago'
|
||||||
|
if (interval === 1) return interval + ' month ago'
|
||||||
|
|
||||||
|
interval = Math.floor(seconds / 604800)
|
||||||
|
if (interval > 1) return interval + ' weeks ago'
|
||||||
|
if (interval === 1) return interval + ' week ago'
|
||||||
|
|
||||||
|
interval = Math.floor(seconds / 86400)
|
||||||
|
if (interval > 1) return interval + ' days ago'
|
||||||
|
if (interval === 1) return interval + ' day ago'
|
||||||
|
|
||||||
|
interval = Math.floor(seconds / 3600)
|
||||||
|
if (interval > 1) return interval + ' hours ago'
|
||||||
|
if (interval === 1) return interval + ' hour ago'
|
||||||
|
|
||||||
|
interval = Math.floor(seconds / 60)
|
||||||
|
if (interval >= 1) return interval + ' min ago'
|
||||||
|
|
||||||
|
return Math.floor(seconds) + ' sec ago'
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { Pipe, PipeTransform } from '@angular/core'
|
||||||
|
|
||||||
|
// Thanks: https://github.com/danrevah/ngx-pipes/blob/master/src/pipes/math/bytes.ts
|
||||||
|
|
||||||
|
@Pipe({name: 'numberFormatter'})
|
||||||
|
export class NumberFormatterPipe implements PipeTransform {
|
||||||
|
private dictionary: Array<{max: number, type: string}> = [
|
||||||
|
{ max: 1000, type: '' },
|
||||||
|
{ max: 1000000, type: 'K' },
|
||||||
|
{ max: 1000000000, type: 'M' }
|
||||||
|
]
|
||||||
|
|
||||||
|
transform (value: number) {
|
||||||
|
const format = this.dictionary.find(d => value < d.max) || this.dictionary[this.dictionary.length - 1]
|
||||||
|
const calc = Math.floor(value / (format.max / 1000))
|
||||||
|
|
||||||
|
return `${calc}${format.type}`
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,25 +1,26 @@
|
||||||
import { NgModule } from '@angular/core'
|
|
||||||
import { HttpClientModule } from '@angular/common/http'
|
|
||||||
import { CommonModule } from '@angular/common'
|
import { CommonModule } from '@angular/common'
|
||||||
|
import { HttpClientModule } from '@angular/common/http'
|
||||||
|
import { NgModule } from '@angular/core'
|
||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||||
import { RouterModule } from '@angular/router'
|
import { RouterModule } from '@angular/router'
|
||||||
|
|
||||||
import { BytesPipe } from 'angular-pipes/src/math/bytes.pipe'
|
|
||||||
import { KeysPipe } from 'angular-pipes/src/object/keys.pipe'
|
|
||||||
import { BsDropdownModule } from 'ngx-bootstrap/dropdown'
|
import { BsDropdownModule } from 'ngx-bootstrap/dropdown'
|
||||||
import { ProgressbarModule } from 'ngx-bootstrap/progressbar'
|
|
||||||
import { PaginationModule } from 'ngx-bootstrap/pagination'
|
|
||||||
import { ModalModule } from 'ngx-bootstrap/modal'
|
import { ModalModule } from 'ngx-bootstrap/modal'
|
||||||
import { DataTableModule } from 'primeng/components/datatable/datatable'
|
import { PaginationModule } from 'ngx-bootstrap/pagination'
|
||||||
|
import { ProgressbarModule } from 'ngx-bootstrap/progressbar'
|
||||||
|
import { BytesPipe, KeysPipe } from 'ngx-pipes'
|
||||||
import { SharedModule as PrimeSharedModule } from 'primeng/components/common/shared'
|
import { SharedModule as PrimeSharedModule } from 'primeng/components/common/shared'
|
||||||
|
import { DataTableModule } from 'primeng/components/datatable/datatable'
|
||||||
|
|
||||||
import { AUTH_INTERCEPTOR_PROVIDER } from './auth'
|
import { AUTH_INTERCEPTOR_PROVIDER } from './auth'
|
||||||
|
import { LoaderComponent } from './misc/loader.component'
|
||||||
import { RestExtractor, RestService } from './rest'
|
import { RestExtractor, RestService } from './rest'
|
||||||
import { SearchComponent, SearchService } from './search'
|
import { SearchComponent, SearchService } from './search'
|
||||||
import { UserService } from './users'
|
import { UserService } from './users'
|
||||||
import { VideoAbuseService } from './video-abuse'
|
import { VideoAbuseService } from './video-abuse'
|
||||||
import { VideoBlacklistService } from './video-blacklist'
|
import { VideoBlacklistService } from './video-blacklist'
|
||||||
import { LoaderComponent } from './misc/loader.component'
|
import { NumberFormatterPipe } from './misc/number-formatter.pipe'
|
||||||
|
import { FromNowPipe } from './misc/from-now.pipe'
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
|
@ -42,7 +43,9 @@ import { LoaderComponent } from './misc/loader.component'
|
||||||
BytesPipe,
|
BytesPipe,
|
||||||
KeysPipe,
|
KeysPipe,
|
||||||
SearchComponent,
|
SearchComponent,
|
||||||
LoaderComponent
|
LoaderComponent,
|
||||||
|
NumberFormatterPipe,
|
||||||
|
FromNowPipe
|
||||||
],
|
],
|
||||||
|
|
||||||
exports: [
|
exports: [
|
||||||
|
@ -62,7 +65,10 @@ import { LoaderComponent } from './misc/loader.component'
|
||||||
KeysPipe,
|
KeysPipe,
|
||||||
|
|
||||||
SearchComponent,
|
SearchComponent,
|
||||||
LoaderComponent
|
LoaderComponent,
|
||||||
|
|
||||||
|
NumberFormatterPipe,
|
||||||
|
FromNowPipe
|
||||||
],
|
],
|
||||||
|
|
||||||
providers: [
|
providers: [
|
||||||
|
|
|
@ -184,7 +184,7 @@ export class VideoAddComponent extends FormReactive implements OnInit {
|
||||||
this.notificationsService.success('Success', 'Video uploaded.')
|
this.notificationsService.success('Success', 'Video uploaded.')
|
||||||
|
|
||||||
// Display all the videos once it's finished
|
// Display all the videos once it's finished
|
||||||
this.router.navigate([ '/videos/list' ])
|
this.router.navigate([ '/videos/trending' ])
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
export * from './my-videos.component'
|
export * from './my-videos.component'
|
||||||
export * from './video-list.component'
|
export * from './video-recently-added.component'
|
||||||
|
export * from './video-trending.component'
|
||||||
export * from './shared'
|
export * from './shared'
|
||||||
|
|
|
@ -27,7 +27,7 @@ export class MyVideosComponent extends AbstractVideoList implements OnInit, OnDe
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy () {
|
ngOnDestroy () {
|
||||||
this.subActivatedRoute.unsubscribe()
|
super.ngOnDestroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
getVideosObservable () {
|
getVideosObservable () {
|
||||||
|
|
|
@ -1,20 +1,8 @@
|
||||||
<div class="row">
|
<div class="title-page">
|
||||||
<div class="content-padding">
|
{{ titlePage }}
|
||||||
<div class="videos-info">
|
|
||||||
<div class="col-md-9 col-xs-5 videos-total-results">
|
|
||||||
<span *ngIf="pagination.totalItems !== null">{{ pagination.totalItems }} videos</span>
|
|
||||||
|
|
||||||
<my-loader [loading]="loading | async"></my-loader>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<my-video-sort class="col-md-3 col-xs-7" [currentSort]="sort" (sort)="onSort($event)"></my-video-sort>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="content-padding videos-miniatures">
|
<div class="videos-miniatures">
|
||||||
<div class="no-video" *ngIf="isThereNoVideo()">There is no video.</div>
|
|
||||||
|
|
||||||
<my-video-miniature
|
<my-video-miniature
|
||||||
class="ng-animate"
|
class="ng-animate"
|
||||||
*ngFor="let video of videos" [video]="video" [user]="user" [currentSort]="sort"
|
*ngFor="let video of videos" [video]="video" [user]="user" [currentSort]="sort"
|
||||||
|
|
|
@ -17,20 +17,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.videos-miniatures {
|
|
||||||
text-align: center;
|
|
||||||
padding-top: 0;
|
|
||||||
|
|
||||||
my-video-miniature {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-video {
|
|
||||||
margin-top: 50px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pagination {
|
pagination {
|
||||||
display: block;
|
display: block;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
|
@ -1,25 +1,19 @@
|
||||||
import { OnDestroy, OnInit } from '@angular/core'
|
import { OnDestroy, OnInit } from '@angular/core'
|
||||||
import { ActivatedRoute, Router } from '@angular/router'
|
import { ActivatedRoute, Router } from '@angular/router'
|
||||||
import { Subscription } from 'rxjs/Subscription'
|
|
||||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject'
|
|
||||||
import { Observable } from 'rxjs/Observable'
|
|
||||||
|
|
||||||
import { NotificationsService } from 'angular2-notifications'
|
import { NotificationsService } from 'angular2-notifications'
|
||||||
|
import { Observable } from 'rxjs/Observable'
|
||||||
|
import { Subscription } from 'rxjs/Subscription'
|
||||||
|
|
||||||
import {
|
import { SortField, Video, VideoPagination } from '../../shared'
|
||||||
SortField,
|
|
||||||
Video,
|
|
||||||
VideoPagination
|
|
||||||
} from '../../shared'
|
|
||||||
|
|
||||||
export abstract class AbstractVideoList implements OnInit, OnDestroy {
|
export abstract class AbstractVideoList implements OnInit, OnDestroy {
|
||||||
loading: BehaviorSubject<boolean> = new BehaviorSubject(false)
|
|
||||||
pagination: VideoPagination = {
|
pagination: VideoPagination = {
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
itemsPerPage: 25,
|
itemsPerPage: 25,
|
||||||
totalItems: null
|
totalItems: null
|
||||||
}
|
}
|
||||||
sort: SortField
|
sort: SortField = '-createdAt'
|
||||||
videos: Video[] = []
|
videos: Video[] = []
|
||||||
|
|
||||||
protected notificationsService: NotificationsService
|
protected notificationsService: NotificationsService
|
||||||
|
@ -28,6 +22,7 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy {
|
||||||
|
|
||||||
protected subActivatedRoute: Subscription
|
protected subActivatedRoute: Subscription
|
||||||
|
|
||||||
|
abstract titlePage: string
|
||||||
abstract getVideosObservable (): Observable<{ videos: Video[], totalVideos: number}>
|
abstract getVideosObservable (): Observable<{ videos: Video[], totalVideos: number}>
|
||||||
|
|
||||||
ngOnInit () {
|
ngOnInit () {
|
||||||
|
@ -44,7 +39,6 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
getVideos () {
|
getVideos () {
|
||||||
this.loading.next(true)
|
|
||||||
this.videos = []
|
this.videos = []
|
||||||
|
|
||||||
const observable = this.getVideosObservable()
|
const observable = this.getVideosObservable()
|
||||||
|
@ -53,17 +47,11 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy {
|
||||||
({ videos, totalVideos }) => {
|
({ videos, totalVideos }) => {
|
||||||
this.videos = videos
|
this.videos = videos
|
||||||
this.pagination.totalItems = totalVideos
|
this.pagination.totalItems = totalVideos
|
||||||
|
|
||||||
this.loading.next(false)
|
|
||||||
},
|
},
|
||||||
error => this.notificationsService.error('Error', error.text)
|
error => this.notificationsService.error('Error', error.text)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
isThereNoVideo () {
|
|
||||||
return !this.loading.getValue() && this.videos.length === 0
|
|
||||||
}
|
|
||||||
|
|
||||||
onPageChanged (event: { page: number }) {
|
onPageChanged (event: { page: number }) {
|
||||||
// Be sure the current page is set
|
// Be sure the current page is set
|
||||||
this.pagination.currentPage = event.page
|
this.pagination.currentPage = event.page
|
||||||
|
@ -71,12 +59,6 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy {
|
||||||
this.navigateToNewParams()
|
this.navigateToNewParams()
|
||||||
}
|
}
|
||||||
|
|
||||||
onSort (sort: SortField) {
|
|
||||||
this.sort = sort
|
|
||||||
|
|
||||||
this.navigateToNewParams()
|
|
||||||
}
|
|
||||||
|
|
||||||
protected buildRouteParams () {
|
protected buildRouteParams () {
|
||||||
// There is always a sort and a current page
|
// There is always a sort and a current page
|
||||||
const params = {
|
const params = {
|
||||||
|
|
|
@ -1,3 +1,2 @@
|
||||||
export * from './abstract-video-list'
|
export * from './abstract-video-list'
|
||||||
export * from './video-miniature.component'
|
export * from './video-miniature.component'
|
||||||
export * from './video-sort.component'
|
|
||||||
|
|
|
@ -6,8 +6,7 @@
|
||||||
<img [attr.src]="video.thumbnailUrl" alt="video thumbnail" [ngClass]="{ 'blur-filter': isVideoNSFWForThisUser() }" />
|
<img [attr.src]="video.thumbnailUrl" alt="video thumbnail" [ngClass]="{ 'blur-filter': isVideoNSFWForThisUser() }" />
|
||||||
|
|
||||||
<div class="video-miniature-thumbnail-overlay">
|
<div class="video-miniature-thumbnail-overlay">
|
||||||
<span class="video-miniature-thumbnail-overlay-views">{{ video.views }} views</span>
|
{{ video.durationLabel }}
|
||||||
<span class="video-miniature-thumbnail-overlay-duration">{{ video.durationLabel }}</span>
|
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
@ -21,13 +20,7 @@
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div class="video-miniature-tags">
|
<span class="video-miniature-created-at-views">{{ video.createdAt | fromNow }} - {{ video.views | numberFormatter }} views</span>
|
||||||
<span *ngFor="let tag of video.tags" class="video-miniature-tag">
|
<span class="video-miniature-account">{{ video.by }}</span>
|
||||||
<a [routerLink]="['/videos/list', { field: 'tags', search: tag, sort: currentSort }]" class="label label-primary">{{ tag }}</a>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<a [routerLink]="['/videos/list', { field: 'account', search: video.account, sort: currentSort }]" class="video-miniature-account">{{ video.by }}</a>
|
|
||||||
<span class="video-miniature-created-at">{{ video.createdAt | date:'short' }}</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
.video-miniature {
|
.video-miniature {
|
||||||
margin: 15px 10px;
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: relative;
|
padding-right: 15px;
|
||||||
height: 190px;
|
margin-bottom: 30px;
|
||||||
|
height: 175px;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
|
|
||||||
.video-miniature-thumbnail {
|
.video-miniature-thumbnail {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: relative;
|
position: relative;
|
||||||
border-radius: 3px;
|
border-radius: 4px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
@ -22,38 +22,33 @@
|
||||||
|
|
||||||
.video-miniature-thumbnail-overlay {
|
.video-miniature-thumbnail-overlay {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0px;
|
right: 5px;
|
||||||
bottom: 0px;
|
bottom: 5px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
background-color: rgba(0, 0, 0, 0.7);
|
background-color: rgba(0, 0, 0, 0.7);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
padding: 3px 5px;
|
font-size: 12px;
|
||||||
font-size: 11px;
|
font-weight: $font-bold;
|
||||||
font-weight: bold;
|
border-radius: 3px;
|
||||||
width: 100%;
|
padding: 0 5px;
|
||||||
|
|
||||||
.video-miniature-thumbnail-overlay-views {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.video-miniature-thumbnail-overlay-duration {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.video-miniature-information {
|
.video-miniature-information {
|
||||||
width: 200px;
|
width: 200px;
|
||||||
|
margin-top: 2px;
|
||||||
|
line-height: normal;
|
||||||
|
|
||||||
.video-miniature-name {
|
.video-miniature-name {
|
||||||
height: 23px;
|
|
||||||
display: block;
|
display: block;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
transition: color 0.2s;
|
transition: color 0.2s;
|
||||||
font-size: 15px;
|
font-size: 16px;
|
||||||
|
font-weight: $font-semibold;
|
||||||
|
color: #000;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
@ -63,39 +58,16 @@
|
||||||
filter: blur(3px);
|
filter: blur(3px);
|
||||||
padding-left: 4px;
|
padding-left: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.video-miniature-tags {
|
|
||||||
// Fix for chrome when tags are long
|
|
||||||
width: 201px;
|
|
||||||
|
|
||||||
.video-miniature-tag {
|
|
||||||
font-size: 13px;
|
|
||||||
cursor: pointer;
|
|
||||||
position: relative;
|
|
||||||
top: -2px;
|
|
||||||
|
|
||||||
.label {
|
|
||||||
transition: background-color 0.2s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.video-miniature-account, .video-miniature-created-at {
|
.video-miniature-created-at-views {
|
||||||
display: block;
|
display: block;
|
||||||
margin-left: 1px;
|
font-size: 13px;
|
||||||
font-size: 11px;
|
|
||||||
color: $video-miniature-other-infos;
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.video-miniature-account {
|
.video-miniature-account {
|
||||||
transition: color 0.2s;
|
font-size: 12px;
|
||||||
|
color: #585858;
|
||||||
&:hover {
|
|
||||||
color: #23527c;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
<select class="form-control input-sm" [(ngModel)]="currentSort" (ngModelChange)="onSortChange()">
|
|
||||||
<option *ngFor="let choice of choiceKeys" [value]="choice">
|
|
||||||
{{ getStringChoice(choice) }}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
|
@ -1,39 +0,0 @@
|
||||||
import { Component, EventEmitter, Input, Output } from '@angular/core'
|
|
||||||
|
|
||||||
import { SortField } from '../../shared'
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'my-video-sort',
|
|
||||||
templateUrl: './video-sort.component.html'
|
|
||||||
})
|
|
||||||
|
|
||||||
export class VideoSortComponent {
|
|
||||||
@Output() sort = new EventEmitter<any>()
|
|
||||||
|
|
||||||
@Input() currentSort: SortField
|
|
||||||
|
|
||||||
sortChoices: { [ P in SortField ]: string } = {
|
|
||||||
'name': 'Name - Asc',
|
|
||||||
'-name': 'Name - Desc',
|
|
||||||
'duration': 'Duration - Asc',
|
|
||||||
'-duration': 'Duration - Desc',
|
|
||||||
'createdAt': 'Created Date - Asc',
|
|
||||||
'-createdAt': 'Created Date - Desc',
|
|
||||||
'views': 'Views - Asc',
|
|
||||||
'-views': 'Views - Desc',
|
|
||||||
'likes': 'Likes - Asc',
|
|
||||||
'-likes': 'Likes - Desc'
|
|
||||||
}
|
|
||||||
|
|
||||||
get choiceKeys () {
|
|
||||||
return Object.keys(this.sortChoices)
|
|
||||||
}
|
|
||||||
|
|
||||||
getStringChoice (choiceKey: SortField) {
|
|
||||||
return this.sortChoices[choiceKey]
|
|
||||||
}
|
|
||||||
|
|
||||||
onSortChange () {
|
|
||||||
this.sort.emit(this.currentSort)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,94 +0,0 @@
|
||||||
import { Component, OnDestroy, OnInit } from '@angular/core'
|
|
||||||
import { ActivatedRoute, Router } from '@angular/router'
|
|
||||||
import { Subscription } from 'rxjs/Subscription'
|
|
||||||
|
|
||||||
import { NotificationsService } from 'angular2-notifications'
|
|
||||||
|
|
||||||
import { VideoService } from '../shared'
|
|
||||||
import { Search, SearchField, SearchService } from '../../shared'
|
|
||||||
import { AbstractVideoList } from './shared'
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'my-videos-list',
|
|
||||||
styleUrls: [ './shared/abstract-video-list.scss' ],
|
|
||||||
templateUrl: './shared/abstract-video-list.html'
|
|
||||||
})
|
|
||||||
export class VideoListComponent extends AbstractVideoList implements OnInit, OnDestroy {
|
|
||||||
private search: Search
|
|
||||||
private subSearch: Subscription
|
|
||||||
|
|
||||||
constructor (
|
|
||||||
protected router: Router,
|
|
||||||
protected route: ActivatedRoute,
|
|
||||||
protected notificationsService: NotificationsService,
|
|
||||||
private videoService: VideoService,
|
|
||||||
private searchService: SearchService
|
|
||||||
) {
|
|
||||||
super()
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit () {
|
|
||||||
// Subscribe to route changes
|
|
||||||
this.subActivatedRoute = this.route.params.subscribe(routeParams => {
|
|
||||||
this.loadRouteParams(routeParams)
|
|
||||||
|
|
||||||
// Update the search service component
|
|
||||||
this.searchService.updateSearch.next(this.search)
|
|
||||||
this.getVideos()
|
|
||||||
})
|
|
||||||
|
|
||||||
// Subscribe to search changes
|
|
||||||
this.subSearch = this.searchService.searchUpdated.subscribe(search => {
|
|
||||||
this.search = search
|
|
||||||
// Reset pagination
|
|
||||||
this.pagination.currentPage = 1
|
|
||||||
|
|
||||||
this.navigateToNewParams()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnDestroy () {
|
|
||||||
super.ngOnDestroy()
|
|
||||||
|
|
||||||
this.subSearch.unsubscribe()
|
|
||||||
}
|
|
||||||
|
|
||||||
getVideosObservable () {
|
|
||||||
let observable = null
|
|
||||||
if (this.search.value) {
|
|
||||||
observable = this.videoService.searchVideos(this.search, this.pagination, this.sort)
|
|
||||||
} else {
|
|
||||||
observable = this.videoService.getVideos(this.pagination, this.sort)
|
|
||||||
}
|
|
||||||
|
|
||||||
return observable
|
|
||||||
}
|
|
||||||
|
|
||||||
protected buildRouteParams () {
|
|
||||||
const params = super.buildRouteParams()
|
|
||||||
|
|
||||||
// Maybe there is a search
|
|
||||||
if (this.search.value) {
|
|
||||||
params['field'] = this.search.field
|
|
||||||
params['search'] = this.search.value
|
|
||||||
}
|
|
||||||
|
|
||||||
return params
|
|
||||||
}
|
|
||||||
|
|
||||||
protected loadRouteParams (routeParams: { [ key: string ]: any }) {
|
|
||||||
super.loadRouteParams(routeParams)
|
|
||||||
|
|
||||||
if (routeParams['search'] !== undefined) {
|
|
||||||
this.search = {
|
|
||||||
value: routeParams['search'],
|
|
||||||
field: routeParams['field'] as SearchField
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.search = {
|
|
||||||
value: '',
|
|
||||||
field: 'name'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { Component, OnDestroy, OnInit } from '@angular/core'
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router'
|
||||||
|
import { NotificationsService } from 'angular2-notifications'
|
||||||
|
import { VideoService } from '../shared'
|
||||||
|
import { AbstractVideoList } from './shared'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-videos-recently-added',
|
||||||
|
styleUrls: [ './shared/abstract-video-list.scss' ],
|
||||||
|
templateUrl: './shared/abstract-video-list.html'
|
||||||
|
})
|
||||||
|
export class VideoRecentlyAddedComponent extends AbstractVideoList implements OnInit, OnDestroy {
|
||||||
|
titlePage = 'Recently added'
|
||||||
|
|
||||||
|
constructor (protected router: Router,
|
||||||
|
protected route: ActivatedRoute,
|
||||||
|
protected notificationsService: NotificationsService,
|
||||||
|
private videoService: VideoService) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit () {
|
||||||
|
super.ngOnInit()
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy () {
|
||||||
|
super.ngOnDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
getVideosObservable () {
|
||||||
|
return this.videoService.getVideos(this.pagination, this.sort)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { Component, OnDestroy, OnInit } from '@angular/core'
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router'
|
||||||
|
import { NotificationsService } from 'angular2-notifications'
|
||||||
|
import { VideoService } from '../shared'
|
||||||
|
import { AbstractVideoList } from './shared'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-videos-trending',
|
||||||
|
styleUrls: [ './shared/abstract-video-list.scss' ],
|
||||||
|
templateUrl: './shared/abstract-video-list.html'
|
||||||
|
})
|
||||||
|
export class VideoTrendingComponent extends AbstractVideoList implements OnInit, OnDestroy {
|
||||||
|
titlePage = 'Trending'
|
||||||
|
|
||||||
|
constructor (protected router: Router,
|
||||||
|
protected route: ActivatedRoute,
|
||||||
|
protected notificationsService: NotificationsService,
|
||||||
|
private videoService: VideoService) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit () {
|
||||||
|
super.ngOnInit()
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy () {
|
||||||
|
super.ngOnDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
getVideosObservable () {
|
||||||
|
return this.videoService.getVideos(this.pagination, this.sort)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,9 @@
|
||||||
import { NgModule } from '@angular/core'
|
import { NgModule } from '@angular/core'
|
||||||
import { RouterModule, Routes } from '@angular/router'
|
import { RouterModule, Routes } from '@angular/router'
|
||||||
|
|
||||||
import { MetaGuard } from '@ngx-meta/core'
|
import { MetaGuard } from '@ngx-meta/core'
|
||||||
|
import { MyVideosComponent } from './video-list'
|
||||||
import { VideoListComponent, MyVideosComponent } from './video-list'
|
import { VideoRecentlyAddedComponent } from './video-list/video-recently-added.component'
|
||||||
|
import { VideoTrendingComponent } from './video-list/video-trending.component'
|
||||||
import { VideosComponent } from './videos.component'
|
import { VideosComponent } from './videos.component'
|
||||||
|
|
||||||
const videosRoutes: Routes = [
|
const videosRoutes: Routes = [
|
||||||
|
@ -12,6 +12,11 @@ const videosRoutes: Routes = [
|
||||||
component: VideosComponent,
|
component: VideosComponent,
|
||||||
canActivateChild: [ MetaGuard ],
|
canActivateChild: [ MetaGuard ],
|
||||||
children: [
|
children: [
|
||||||
|
{
|
||||||
|
path: 'list',
|
||||||
|
pathMatch: 'full',
|
||||||
|
redirectTo: 'recently-added'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'mine',
|
path: 'mine',
|
||||||
component: MyVideosComponent,
|
component: MyVideosComponent,
|
||||||
|
@ -22,11 +27,20 @@ const videosRoutes: Routes = [
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'list',
|
path: 'trending',
|
||||||
component: VideoListComponent,
|
component: VideoTrendingComponent,
|
||||||
data: {
|
data: {
|
||||||
meta: {
|
meta: {
|
||||||
title: 'Videos list'
|
title: 'Trending videos'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'recently-added',
|
||||||
|
component: VideoRecentlyAddedComponent,
|
||||||
|
data: {
|
||||||
|
meta: {
|
||||||
|
title: 'Recently added videos'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import { NgModule } from '@angular/core'
|
import { NgModule } from '@angular/core'
|
||||||
import { SharedModule } from '../shared'
|
import { SharedModule } from '../shared'
|
||||||
import { VideoService } from './shared'
|
import { VideoService } from './shared'
|
||||||
import { MyVideosComponent, VideoListComponent, VideoMiniatureComponent, VideoSortComponent } from './video-list'
|
import { MyVideosComponent, VideoMiniatureComponent } from './video-list'
|
||||||
|
import { VideoRecentlyAddedComponent } from './video-list/video-recently-added.component'
|
||||||
|
import { VideoTrendingComponent } from './video-list/video-trending.component'
|
||||||
import { VideosRoutingModule } from './videos-routing.module'
|
import { VideosRoutingModule } from './videos-routing.module'
|
||||||
import { VideosComponent } from './videos.component'
|
import { VideosComponent } from './videos.component'
|
||||||
|
|
||||||
|
@ -14,10 +16,10 @@ import { VideosComponent } from './videos.component'
|
||||||
declarations: [
|
declarations: [
|
||||||
VideosComponent,
|
VideosComponent,
|
||||||
|
|
||||||
VideoListComponent,
|
VideoTrendingComponent,
|
||||||
|
VideoRecentlyAddedComponent,
|
||||||
MyVideosComponent,
|
MyVideosComponent,
|
||||||
VideoMiniatureComponent,
|
VideoMiniatureComponent
|
||||||
VideoSortComponent
|
|
||||||
],
|
],
|
||||||
|
|
||||||
exports: [
|
exports: [
|
||||||
|
|
|
@ -33,24 +33,14 @@ input.readonly {
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-col {
|
.main-col {
|
||||||
.content-padding {
|
padding: 30px;
|
||||||
padding: 15px 30px;
|
|
||||||
|
|
||||||
@media screen and (max-width: 800px) {
|
.title-page {
|
||||||
padding: 15px 10px;
|
font-size: 16px;
|
||||||
}
|
font-weight: $font-bold;
|
||||||
|
display: inline-block;
|
||||||
@media screen and (min-width: 1400px) {
|
border-bottom: 2px solid $orange-color;
|
||||||
padding: 15px 40px;
|
margin-bottom: 25px;
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 1600px) {
|
|
||||||
padding: 15px 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 1800px) {
|
|
||||||
padding: 15px 60px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -264,10 +264,6 @@ amdefine@>=0.0.4:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
|
resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
|
||||||
|
|
||||||
angular-pipes@^6.0.0:
|
|
||||||
version "6.5.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/angular-pipes/-/angular-pipes-6.5.3.tgz#6bed37c51ebc2adaf3412663bfe25179d0489b02"
|
|
||||||
|
|
||||||
angular2-notifications@^0.7.7:
|
angular2-notifications@^0.7.7:
|
||||||
version "0.7.8"
|
version "0.7.8"
|
||||||
resolved "https://registry.yarnpkg.com/angular2-notifications/-/angular2-notifications-0.7.8.tgz#ecbcb95a8d2d402af94a9a080d6664c70d33a029"
|
resolved "https://registry.yarnpkg.com/angular2-notifications/-/angular2-notifications-0.7.8.tgz#ecbcb95a8d2d402af94a9a080d6664c70d33a029"
|
||||||
|
@ -4718,6 +4714,10 @@ ngx-chips@1.5.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
ng2-material-dropdown "0.7.10"
|
ng2-material-dropdown "0.7.10"
|
||||||
|
|
||||||
|
ngx-pipes@^2.0.5:
|
||||||
|
version "2.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/ngx-pipes/-/ngx-pipes-2.0.5.tgz#743b827e350b1e66f5bdae49e90a02fa631d4c54"
|
||||||
|
|
||||||
no-case@^2.2.0:
|
no-case@^2.2.0:
|
||||||
version "2.3.2"
|
version "2.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac"
|
resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac"
|
||||||
|
|
Loading…
Reference in New Issue