mirror of
https://github.com/Chocobozzz/PeerTube.git
synced 2024-11-25 18:20:31 -06:00
Add tags support to the video list
This commit is contained in:
parent
e822fdaeee
commit
00a446454d
@ -14,40 +14,40 @@
|
||||
<div class="row">
|
||||
|
||||
<menu class="col-md-2 col-xs-3">
|
||||
<div class="panel_block">
|
||||
<div id="panel_user_login" class="panel_button">
|
||||
<div class="panel-block">
|
||||
<div id="panel-user-login" class="panel-button">
|
||||
<span class="glyphicon glyphicon-user"></span>
|
||||
<a *ngIf="!isLoggedIn" [routerLink]="['UserLogin']">Login</a>
|
||||
<a *ngIf="!isLoggedIn" [routerLink]="['/users/login']">Login</a>
|
||||
<a *ngIf="isLoggedIn" (click)="logout()">Logout</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel_block">
|
||||
<div id="panel_get_videos" class="panel_button">
|
||||
<div class="panel-block">
|
||||
<div id="panel-get-videos" class="panel-button">
|
||||
<span class="glyphicon glyphicon-list"></span>
|
||||
<a [routerLink]="['VideosList']">Get videos</a>
|
||||
<a [routerLink]="['/videos/list']">Get videos</a>
|
||||
</div>
|
||||
|
||||
<div id="panel_upload_video" class="panel_button" *ngIf="isLoggedIn">
|
||||
<div id="panel-upload-video" class="panel-button" *ngIf="isLoggedIn">
|
||||
<span class="glyphicon glyphicon-cloud-upload"></span>
|
||||
<a [routerLink]="['VideosAdd']">Upload a video</a>
|
||||
<a [routerLink]="['/videos/add']">Upload a video</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel_block" *ngIf="isLoggedIn">
|
||||
<div id="panel_make_friends" class="panel_button">
|
||||
<div class="panel-block" *ngIf="isLoggedIn">
|
||||
<div id="panel-make-friends" class="panel-button">
|
||||
<span class="glyphicon glyphicon-cloud"></span>
|
||||
<a (click)='makeFriends()'>Make friends</a>
|
||||
</div>
|
||||
|
||||
<div id="panel_quit_friends" class="panel_button">
|
||||
<div id="panel-quit-friends" class="panel-button">
|
||||
<span class="glyphicon glyphicon-plane"></span>
|
||||
<a (click)='quitFriends()'>Quit friends</a>
|
||||
</div>
|
||||
</div>
|
||||
</menu>
|
||||
|
||||
<div class="col-md-9 col-xs-8 router_outler_container">
|
||||
<div class="col-md-9 col-xs-8 router-outler-container">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
|
||||
|
@ -8,7 +8,7 @@ menu {
|
||||
margin-right: 20px;
|
||||
border-right: 1px solid rgba(0, 0, 0, 0.2);
|
||||
|
||||
.panel_button {
|
||||
.panel-button {
|
||||
margin: 8px;
|
||||
cursor: pointer;
|
||||
transition: margin 0.2s;
|
||||
@ -27,6 +27,6 @@ menu {
|
||||
}
|
||||
}
|
||||
|
||||
.panel_block:not(:last-child) {
|
||||
.panel-block:not(:last-child) {
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { HTTP_PROVIDERS } from '@angular/http';
|
||||
import { RouteConfig, Router, ROUTER_DIRECTIVES, ROUTER_PROVIDERS } from '@angular/router-deprecated';
|
||||
import { Router, ROUTER_DIRECTIVES, ROUTER_PROVIDERS, Routes } from '@angular/router';
|
||||
|
||||
import { FriendService } from './friends';
|
||||
import { LoginComponent } from './login';
|
||||
@ -16,27 +16,23 @@ import {
|
||||
VideoWatchComponent,
|
||||
VideoService
|
||||
} from './videos';
|
||||
import { SearchService } from './shared'; // Temporary
|
||||
|
||||
@RouteConfig([
|
||||
@Routes([
|
||||
{
|
||||
path: '/users/login',
|
||||
name: 'UserLogin',
|
||||
component: LoginComponent
|
||||
},
|
||||
{
|
||||
path: '/videos/list',
|
||||
name: 'VideosList',
|
||||
component: VideoListComponent,
|
||||
useAsDefault: true
|
||||
component: VideoListComponent
|
||||
},
|
||||
{
|
||||
path: '/videos/watch/:id',
|
||||
name: 'VideosWatch',
|
||||
component: VideoWatchComponent
|
||||
},
|
||||
{
|
||||
path: '/videos/add',
|
||||
name: 'VideosAdd',
|
||||
component: VideoAddComponent
|
||||
}
|
||||
])
|
||||
@ -46,7 +42,7 @@ import {
|
||||
template: require('./app.component.html'),
|
||||
styles: [ require('./app.component.scss') ],
|
||||
directives: [ ROUTER_DIRECTIVES, SearchComponent ],
|
||||
providers: [ AuthService, FriendService, HTTP_PROVIDERS, ROUTER_PROVIDERS, VideoService ]
|
||||
providers: [ AuthService, FriendService, HTTP_PROVIDERS, ROUTER_PROVIDERS, VideoService, SearchService ]
|
||||
})
|
||||
|
||||
export class AppComponent {
|
||||
@ -75,12 +71,13 @@ export class AppComponent {
|
||||
field: search.field,
|
||||
search: search.value
|
||||
};
|
||||
this.router.navigate(['VideosList', params]);
|
||||
this.router.navigate(['/videos/list', params]);
|
||||
} else {
|
||||
this.router.navigate(['VideosList']);
|
||||
this.router.navigate(['/videos/list']);
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME
|
||||
logout() {
|
||||
// this._authService.logout();
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Router } from '@angular/router-deprecated';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
import { AuthService, AuthStatus, User } from '../shared';
|
||||
|
||||
@ -26,7 +26,7 @@ export class LoginComponent {
|
||||
|
||||
this.authService.setStatus(AuthStatus.LoggedIn);
|
||||
|
||||
this.router.navigate(['VideosList']);
|
||||
this.router.navigate(['/videos/list']);
|
||||
},
|
||||
error => {
|
||||
if (error.error === 'invalid_grant') {
|
||||
|
@ -1,3 +1,4 @@
|
||||
export * from './search-field.type';
|
||||
export * from './search.component';
|
||||
export * from './search.model';
|
||||
export * from './search.service';
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { Component, EventEmitter, Output } from '@angular/core';
|
||||
import { Component, EventEmitter, Output, OnInit } from '@angular/core';
|
||||
|
||||
import { DROPDOWN_DIRECTIVES} from 'ng2-bootstrap/components/dropdown';
|
||||
|
||||
import { Search } from './search.model';
|
||||
import { SearchField } from './search-field.type';
|
||||
import { SearchService } from './search.service'; // Temporary
|
||||
|
||||
@Component({
|
||||
selector: 'my-search',
|
||||
@ -11,7 +12,7 @@ import { SearchField } from './search-field.type';
|
||||
directives: [ DROPDOWN_DIRECTIVES ]
|
||||
})
|
||||
|
||||
export class SearchComponent {
|
||||
export class SearchComponent implements OnInit {
|
||||
@Output() search = new EventEmitter<Search>();
|
||||
|
||||
fieldChoices = {
|
||||
@ -26,6 +27,21 @@ export class SearchComponent {
|
||||
value: ''
|
||||
};
|
||||
|
||||
constructor(private searchService: SearchService) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.searchService.searchChanged.subscribe(
|
||||
newSearchCriterias => {
|
||||
// Put a field by default
|
||||
if (!newSearchCriterias.field) {
|
||||
newSearchCriterias.field = 'name';
|
||||
}
|
||||
|
||||
this.searchCriterias = newSearchCriterias;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
get choiceKeys() {
|
||||
return Object.keys(this.fieldChoices);
|
||||
}
|
||||
@ -35,6 +51,7 @@ export class SearchComponent {
|
||||
$event.stopPropagation();
|
||||
|
||||
this.searchCriterias.field = choice;
|
||||
this.doSearch();
|
||||
}
|
||||
|
||||
doSearch() {
|
||||
|
15
client/src/app/shared/search/search.service.ts
Normal file
15
client/src/app/shared/search/search.service.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Subject } from 'rxjs/Subject';
|
||||
|
||||
import { Search } from './search.model';
|
||||
|
||||
// This class is needed to communicate between videos/list and search component
|
||||
// Remove it when we'll be able to subscribe to router changes
|
||||
@Injectable()
|
||||
export class SearchService {
|
||||
searchChanged: Subject<Search>;
|
||||
|
||||
constructor() {
|
||||
this.searchChanged = new Subject<Search>();
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@ export class Video {
|
||||
magnetUri: string;
|
||||
name: string;
|
||||
podUrl: string;
|
||||
tags: string[];
|
||||
thumbnailPath: string;
|
||||
|
||||
private static createByString(author: string, podUrl: string) {
|
||||
@ -42,6 +43,7 @@ export class Video {
|
||||
magnetUri: string,
|
||||
name: string,
|
||||
podUrl: string,
|
||||
tags: string[],
|
||||
thumbnailPath: string
|
||||
}) {
|
||||
this.author = hash.author;
|
||||
@ -53,6 +55,7 @@ export class Video {
|
||||
this.magnetUri = hash.magnetUri;
|
||||
this.name = hash.name;
|
||||
this.podUrl = hash.podUrl;
|
||||
this.tags = hash.tags;
|
||||
this.thumbnailPath = hash.thumbnailPath;
|
||||
|
||||
this.by = Video.createByString(hash.author, hash.podUrl);
|
||||
|
@ -21,12 +21,12 @@
|
||||
ngControl="tags" #tags="ngForm" [disabled]="isTagsInputDisabled" (keyup)="onTagKeyPress($event)" [(ngModel)]="currentTag"
|
||||
>
|
||||
<div [hidden]="tags.valid || tags.pristine" class="alert alert-warning">
|
||||
A tag should be between 2 and 10 characters long
|
||||
A tag should be between 2 and 10 characters (alphanumeric) long
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tags">
|
||||
<div class="label label-info tag" *ngFor="let tag of video.tags">
|
||||
<div class="label label-primary tag" *ngFor="let tag of video.tags">
|
||||
{{ tag }}
|
||||
<span class="remove" (click)="removeTag(tag)">x</span>
|
||||
</div>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Control, ControlGroup, Validators } from '@angular/common';
|
||||
import { Component, ElementRef, OnInit } from '@angular/core';
|
||||
import { Router } from '@angular/router-deprecated';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
import { BytesPipe } from 'angular-pipes/src/math/bytes.pipe';
|
||||
import { PROGRESSBAR_DIRECTIVES } from 'ng2-bootstrap/components/progressbar';
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Router, ROUTER_DIRECTIVES, RouteParams } from '@angular/router-deprecated';
|
||||
import { Router, ROUTER_DIRECTIVES, RouteSegment } from '@angular/router';
|
||||
|
||||
import { PAGINATION_DIRECTIVES } from 'ng2-bootstrap/components/pagination';
|
||||
|
||||
@ -13,6 +13,7 @@ import {
|
||||
import { AuthService, Search, SearchField, User } from '../../shared';
|
||||
import { VideoMiniatureComponent } from './video-miniature.component';
|
||||
import { VideoSortComponent } from './video-sort.component';
|
||||
import { SearchService } from '../../shared';
|
||||
|
||||
@Component({
|
||||
selector: 'my-videos-list',
|
||||
@ -37,22 +38,26 @@ export class VideoListComponent implements OnInit {
|
||||
constructor(
|
||||
private authService: AuthService,
|
||||
private router: Router,
|
||||
private routeParams: RouteParams,
|
||||
private videoService: VideoService
|
||||
) {
|
||||
this.search = {
|
||||
value: this.routeParams.get('search'),
|
||||
field: <SearchField>this.routeParams.get('field')
|
||||
};
|
||||
|
||||
this.sort = <SortField>this.routeParams.get('sort') || '-createdDate';
|
||||
}
|
||||
private routeSegment: RouteSegment,
|
||||
private videoService: VideoService,
|
||||
private searchService: SearchService // Temporary
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
if (this.authService.isLoggedIn()) {
|
||||
this.user = User.load();
|
||||
}
|
||||
|
||||
this.search = {
|
||||
value: this.routeSegment.getParam('search'),
|
||||
field: <SearchField>this.routeSegment.getParam('field')
|
||||
};
|
||||
|
||||
// Temporary
|
||||
this.searchChanged(this.search);
|
||||
|
||||
this.sort = <SortField>this.routeSegment.getParam('sort') || '-createdDate';
|
||||
|
||||
this.getVideos();
|
||||
}
|
||||
|
||||
@ -62,7 +67,7 @@ export class VideoListComponent implements OnInit {
|
||||
|
||||
let observable = null;
|
||||
|
||||
if (this.search.value !== null) {
|
||||
if (this.search.value) {
|
||||
observable = this.videoService.searchVideos(this.search, this.pagination, this.sort);
|
||||
} else {
|
||||
observable = this.videoService.getVideos(this.pagination, this.sort);
|
||||
@ -99,7 +104,10 @@ export class VideoListComponent implements OnInit {
|
||||
params.search = this.search.value;
|
||||
}
|
||||
|
||||
this.router.navigate(['VideosList', params]);
|
||||
this.getVideos();
|
||||
this.router.navigate(['/videos/list', params]);
|
||||
}
|
||||
|
||||
searchChanged(search: Search) {
|
||||
this.searchService.searchChanged.next(search);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<div class="video-miniature col-md-4" (mouseenter)="onHover()" (mouseleave)="onBlur()">
|
||||
<a
|
||||
[routerLink]="['VideosWatch', { id: video.id }]" [attr.title]="video.description"
|
||||
[routerLink]="['/videos/watch', video.id]" [attr.title]="video.description"
|
||||
class="video-miniature-thumbnail"
|
||||
>
|
||||
<img [attr.src]="video.thumbnailPath" alt="video thumbnail" />
|
||||
@ -12,11 +12,15 @@
|
||||
></span>
|
||||
|
||||
<div class="video-miniature-informations">
|
||||
<a [routerLink]="['VideosWatch', { id: video.id }]" class="video-miniature-name">
|
||||
<span>{{ video.name }}</span>
|
||||
</a>
|
||||
<span class="video-miniature-name-tags">
|
||||
<a [routerLink]="['/videos/watch', video.id]" class="video-miniature-name">{{ video.name }}</a>
|
||||
|
||||
<span class="video-miniature-author">by {{ video.by }}</span>
|
||||
<span *ngFor="let tag of video.tags" class="video-miniature-tag">
|
||||
<a [routerLink]="['/videos/list', { field: 'tags', search: tag }]" class="label label-primary">{{ tag }}</a>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<a [routerLink]="['/videos/list', { field: 'author', search: video.author }]" class="video-miniature-author">by {{ video.by }}</a>
|
||||
<span class="video-miniature-created-date">on {{ video.createdDate | date:'short' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,5 +1,5 @@
|
||||
.video-miniature {
|
||||
height: 200px;
|
||||
margin-top: 30px;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
|
||||
@ -35,13 +35,34 @@
|
||||
|
||||
.video-miniature-informations {
|
||||
margin-left: 3px;
|
||||
width: 200px;
|
||||
|
||||
.video-miniature-name {
|
||||
.video-miniature-name-tags {
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
.video-miniature-name {
|
||||
font-weight: bold;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '\002022';
|
||||
margin-left: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
.video-miniature-tag {
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
transition: opacity 0.5s;
|
||||
position: relative;
|
||||
top: -2px;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,7 +70,16 @@
|
||||
display: block;
|
||||
margin-left: 1px;
|
||||
font-size: 11px;
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
color: rgb(54, 118, 173);
|
||||
}
|
||||
|
||||
.video-miniature-author {
|
||||
transition: opacity 0.5s;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
opacity: 0.9;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
||||
import { ROUTER_DIRECTIVES } from '@angular/router-deprecated';
|
||||
import { ROUTER_DIRECTIVES } from '@angular/router';
|
||||
|
||||
import { Video, VideoService } from '../shared';
|
||||
import { User } from '../../shared';
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Component, ElementRef, OnInit } from '@angular/core';
|
||||
import { CanDeactivate, ComponentInstruction, RouteParams } from '@angular/router-deprecated';
|
||||
import { CanDeactivate, RouteSegment } from '@angular/router';
|
||||
|
||||
import { BytesPipe } from 'angular-pipes/src/math/bytes.pipe';
|
||||
|
||||
@ -30,7 +30,7 @@ export class VideoWatchComponent implements OnInit, CanDeactivate {
|
||||
|
||||
constructor(
|
||||
private elementRef: ElementRef,
|
||||
private routeParams: RouteParams,
|
||||
private routeSegment: RouteSegment,
|
||||
private videoService: VideoService,
|
||||
private webTorrentService: WebTorrentService
|
||||
) {}
|
||||
@ -74,7 +74,7 @@ export class VideoWatchComponent implements OnInit, CanDeactivate {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
let id = this.routeParams.get('id');
|
||||
let id = this.routeSegment.getParam('id');
|
||||
this.videoService.getVideo(id).subscribe(
|
||||
video => {
|
||||
this.video = video;
|
||||
@ -84,11 +84,11 @@ export class VideoWatchComponent implements OnInit, CanDeactivate {
|
||||
);
|
||||
}
|
||||
|
||||
routerCanDeactivate(next: ComponentInstruction, prev: ComponentInstruction) {
|
||||
routerCanDeactivate() {
|
||||
console.log('Removing video from webtorrent.');
|
||||
clearInterval(this.torrentInfosInterval);
|
||||
this.webTorrentService.remove(this.video.magnetUri);
|
||||
return true;
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
private loadTooLong() {
|
||||
|
@ -9,7 +9,7 @@ import '@angular/platform-browser-dynamic';
|
||||
import '@angular/core';
|
||||
import '@angular/common';
|
||||
import '@angular/http';
|
||||
import '@angular/router-deprecated';
|
||||
import '@angular/router';
|
||||
|
||||
// RxJS
|
||||
import 'rxjs/Observable';
|
||||
|
@ -37,6 +37,7 @@
|
||||
"src/app/shared/search/search-field.type.ts",
|
||||
"src/app/shared/search/search.component.ts",
|
||||
"src/app/shared/search/search.model.ts",
|
||||
"src/app/shared/search/search.service.ts",
|
||||
"src/app/shared/users/auth-status.model.ts",
|
||||
"src/app/shared/users/auth.service.ts",
|
||||
"src/app/shared/users/index.ts",
|
||||
|
Loading…
Reference in New Issue
Block a user