Client: add basic support for updating a video
This commit is contained in:
parent
a184c71b52
commit
d8e689b864
|
@ -1,5 +1,5 @@
|
||||||
export * from './shared';
|
export * from './shared';
|
||||||
export * from './video-add';
|
export * from './video-edit';
|
||||||
export * from './video-list';
|
export * from './video-list';
|
||||||
export * from './video-watch';
|
export * from './video-watch';
|
||||||
export * from './videos-routing.module';
|
export * from './videos-routing.module';
|
||||||
|
|
|
@ -5,8 +5,11 @@ export class Video {
|
||||||
by: string;
|
by: string;
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
categoryLabel: string;
|
categoryLabel: string;
|
||||||
|
category: string;
|
||||||
licenceLabel: string;
|
licenceLabel: string;
|
||||||
|
licence: string;
|
||||||
languageLabel: string;
|
languageLabel: string;
|
||||||
|
language: string;
|
||||||
description: string;
|
description: string;
|
||||||
duration: string;
|
duration: string;
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -38,8 +41,11 @@ export class Video {
|
||||||
author: string,
|
author: string,
|
||||||
createdAt: string,
|
createdAt: string,
|
||||||
categoryLabel: string,
|
categoryLabel: string,
|
||||||
|
category: string,
|
||||||
licenceLabel: string,
|
licenceLabel: string,
|
||||||
|
licence: string,
|
||||||
languageLabel: string;
|
languageLabel: string;
|
||||||
|
language: string;
|
||||||
description: string,
|
description: string,
|
||||||
duration: number;
|
duration: number;
|
||||||
id: string,
|
id: string,
|
||||||
|
@ -57,8 +63,11 @@ export class Video {
|
||||||
this.author = hash.author;
|
this.author = hash.author;
|
||||||
this.createdAt = new Date(hash.createdAt);
|
this.createdAt = new Date(hash.createdAt);
|
||||||
this.categoryLabel = hash.categoryLabel;
|
this.categoryLabel = hash.categoryLabel;
|
||||||
|
this.category = hash.category;
|
||||||
this.licenceLabel = hash.licenceLabel;
|
this.licenceLabel = hash.licenceLabel;
|
||||||
|
this.licence = hash.licence;
|
||||||
this.languageLabel = hash.languageLabel;
|
this.languageLabel = hash.languageLabel;
|
||||||
|
this.language = hash.language;
|
||||||
this.description = hash.description;
|
this.description = hash.description;
|
||||||
this.duration = Video.createDurationString(hash.duration);
|
this.duration = Video.createDurationString(hash.duration);
|
||||||
this.id = hash.id;
|
this.id = hash.id;
|
||||||
|
@ -84,4 +93,33 @@ export class Video {
|
||||||
// If the video is NSFW and the user is not logged in, or the user does not want to display NSFW videos...
|
// If the video is NSFW and the user is not logged in, or the user does not want to display NSFW videos...
|
||||||
return (this.nsfw && (!user || user.displayNSFW === false));
|
return (this.nsfw && (!user || user.displayNSFW === false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
patch(values: Object) {
|
||||||
|
Object.keys(values).forEach((key) => {
|
||||||
|
this[key] = values[key];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON() {
|
||||||
|
return {
|
||||||
|
author: this.author,
|
||||||
|
createdAt: this.createdAt,
|
||||||
|
category: this.category,
|
||||||
|
licence: this.licence,
|
||||||
|
language: this.language,
|
||||||
|
description: this.description,
|
||||||
|
duration: this.duration,
|
||||||
|
id: this.id,
|
||||||
|
isLocal: this.isLocal,
|
||||||
|
magnetUri: this.magnetUri,
|
||||||
|
name: this.name,
|
||||||
|
podHost: this.podHost,
|
||||||
|
tags: this.tags,
|
||||||
|
thumbnailPath: this.thumbnailPath,
|
||||||
|
views: this.views,
|
||||||
|
likes: this.likes,
|
||||||
|
dislikes: this.dislikes,
|
||||||
|
nsfw: this.nsfw
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Http } from '@angular/http';
|
import { Http, Headers, RequestOptions } from '@angular/http';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
import 'rxjs/add/operator/catch';
|
import 'rxjs/add/operator/catch';
|
||||||
import 'rxjs/add/operator/map';
|
import 'rxjs/add/operator/map';
|
||||||
|
@ -80,6 +80,23 @@ export class VideoService {
|
||||||
.catch((res) => this.restExtractor.handleError(res));
|
.catch((res) => this.restExtractor.handleError(res));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateVideo(video: Video) {
|
||||||
|
const body = {
|
||||||
|
name: video.name,
|
||||||
|
category: video.category,
|
||||||
|
licence: video.licence,
|
||||||
|
language: video.language,
|
||||||
|
description: video.description,
|
||||||
|
tags: video.tags
|
||||||
|
};
|
||||||
|
const headers = new Headers({ 'Content-Type': 'application/json' });
|
||||||
|
const options = new RequestOptions({ headers: headers });
|
||||||
|
|
||||||
|
return this.authHttp.put(`${VideoService.BASE_VIDEO_URL}/${video.id}`, body, options)
|
||||||
|
.map(this.restExtractor.extractDataBool)
|
||||||
|
.catch(this.restExtractor.handleError);
|
||||||
|
}
|
||||||
|
|
||||||
getVideos(pagination: RestPagination, sort: SortField) {
|
getVideos(pagination: RestPagination, sort: SortField) {
|
||||||
const params = this.restService.buildRestGetParams(pagination, sort);
|
const params = this.restService.buildRestGetParams(pagination, sort);
|
||||||
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
export * from './video-add.component';
|
|
2
client/src/app/videos/video-edit/index.ts
Normal file
2
client/src/app/videos/video-edit/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export * from './video-add.component';
|
||||||
|
export * from './video-update.component';
|
|
@ -19,7 +19,7 @@ import { VideoService } from '../shared';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-videos-add',
|
selector: 'my-videos-add',
|
||||||
styleUrls: [ './video-add.component.scss' ],
|
styleUrls: [ './video-edit.component.scss' ],
|
||||||
templateUrl: './video-add.component.html'
|
templateUrl: './video-add.component.html'
|
||||||
})
|
})
|
||||||
|
|
101
client/src/app/videos/video-edit/video-update.component.html
Normal file
101
client/src/app/videos/video-edit/video-update.component.html
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
<h3>Update {{ video.name }}</h3>
|
||||||
|
|
||||||
|
<div *ngIf="error" class="alert alert-danger">{{ error }}</div>
|
||||||
|
|
||||||
|
<form novalidate [formGroup]="form">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="name">Name</label>
|
||||||
|
<input
|
||||||
|
type="text" class="form-control" id="name"
|
||||||
|
formControlName="name"
|
||||||
|
>
|
||||||
|
<div *ngIf="formErrors.name" class="alert alert-danger">
|
||||||
|
{{ formErrors.name }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="nsfw">NSFW</label>
|
||||||
|
<input
|
||||||
|
type="checkbox" id="nsfw"
|
||||||
|
formControlName="nsfw"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="category">Category</label>
|
||||||
|
<select class="form-control" id="category" formControlName="category">
|
||||||
|
<option></option>
|
||||||
|
<option *ngFor="let category of videoCategories" [value]="category.id">{{ category.label }}</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<div *ngIf="formErrors.category" class="alert alert-danger">
|
||||||
|
{{ formErrors.category }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="licence">Licence</label>
|
||||||
|
<select class="form-control" id="licence" formControlName="licence">
|
||||||
|
<option></option>
|
||||||
|
<option *ngFor="let licence of videoLicences" [value]="licence.id">{{ licence.label }}</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<div *ngIf="formErrors.licence" class="alert alert-danger">
|
||||||
|
{{ formErrors.licence }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="language">Language</label>
|
||||||
|
<select class="form-control" id="language" formControlName="language">
|
||||||
|
<option></option>
|
||||||
|
<option *ngFor="let language of videoLanguages" [value]="language.id">{{ language.label }}</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<div *ngIf="formErrors.language" class="alert alert-danger">
|
||||||
|
{{ formErrors.language }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="tags">Tags</label> <span class="little-information">(press enter to add the tag)</span>
|
||||||
|
<input
|
||||||
|
type="text" class="form-control" id="currentTag"
|
||||||
|
formControlName="currentTag" (keyup)="onTagKeyPress($event)"
|
||||||
|
>
|
||||||
|
<div *ngIf="formErrors.currentTag" class="alert alert-danger">
|
||||||
|
{{ formErrors.currentTag }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tags">
|
||||||
|
<div class="label label-primary tag" *ngFor="let tag of tags">
|
||||||
|
{{ tag }}
|
||||||
|
<span class="remove" (click)="removeTag(tag)">x</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="tagsError" class="alert alert-danger">
|
||||||
|
{{ tagsError }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="description">Description</label>
|
||||||
|
<textarea
|
||||||
|
id="description" class="form-control" placeholder="Description..."
|
||||||
|
formControlName="description"
|
||||||
|
>
|
||||||
|
</textarea>
|
||||||
|
<div *ngIf="formErrors.description" class="alert alert-danger">
|
||||||
|
{{ formErrors.description }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<input
|
||||||
|
type="button" value="Update" class="btn btn-default form-control"
|
||||||
|
(click)="update()"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</form>
|
170
client/src/app/videos/video-edit/video-update.component.ts
Normal file
170
client/src/app/videos/video-edit/video-update.component.ts
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
import { Component, ElementRef, OnInit } from '@angular/core';
|
||||||
|
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
|
||||||
|
import { FileUploader } from 'ng2-file-upload/ng2-file-upload';
|
||||||
|
import { NotificationsService } from 'angular2-notifications';
|
||||||
|
|
||||||
|
import { AuthService } from '../../core';
|
||||||
|
import {
|
||||||
|
FormReactive,
|
||||||
|
VIDEO_NAME,
|
||||||
|
VIDEO_CATEGORY,
|
||||||
|
VIDEO_LICENCE,
|
||||||
|
VIDEO_LANGUAGE,
|
||||||
|
VIDEO_DESCRIPTION,
|
||||||
|
VIDEO_TAGS
|
||||||
|
} from '../../shared';
|
||||||
|
import { Video, VideoService } from '../shared';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-videos-update',
|
||||||
|
styleUrls: [ './video-edit.component.scss' ],
|
||||||
|
templateUrl: './video-update.component.html'
|
||||||
|
})
|
||||||
|
|
||||||
|
export class VideoUpdateComponent extends FormReactive implements OnInit {
|
||||||
|
tags: string[] = [];
|
||||||
|
videoCategories = [];
|
||||||
|
videoLicences = [];
|
||||||
|
videoLanguages = [];
|
||||||
|
video: Video;
|
||||||
|
|
||||||
|
error: string = null;
|
||||||
|
form: FormGroup;
|
||||||
|
formErrors = {
|
||||||
|
name: '',
|
||||||
|
category: '',
|
||||||
|
licence: '',
|
||||||
|
language: '',
|
||||||
|
description: '',
|
||||||
|
currentTag: ''
|
||||||
|
};
|
||||||
|
validationMessages = {
|
||||||
|
name: VIDEO_NAME.MESSAGES,
|
||||||
|
category: VIDEO_CATEGORY.MESSAGES,
|
||||||
|
licence: VIDEO_LICENCE.MESSAGES,
|
||||||
|
language: VIDEO_LANGUAGE.MESSAGES,
|
||||||
|
description: VIDEO_DESCRIPTION.MESSAGES,
|
||||||
|
currentTag: VIDEO_TAGS.MESSAGES
|
||||||
|
};
|
||||||
|
|
||||||
|
// Special error messages
|
||||||
|
tagsError = '';
|
||||||
|
fileError = '';
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private authService: AuthService,
|
||||||
|
private elementRef: ElementRef,
|
||||||
|
private formBuilder: FormBuilder,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private router: Router,
|
||||||
|
private notificationsService: NotificationsService,
|
||||||
|
private videoService: VideoService
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
buildForm() {
|
||||||
|
this.form = this.formBuilder.group({
|
||||||
|
name: [ '', VIDEO_NAME.VALIDATORS ],
|
||||||
|
nsfw: [ false ],
|
||||||
|
category: [ '', VIDEO_CATEGORY.VALIDATORS ],
|
||||||
|
licence: [ '', VIDEO_LICENCE.VALIDATORS ],
|
||||||
|
language: [ '', VIDEO_LANGUAGE.VALIDATORS ],
|
||||||
|
description: [ '', VIDEO_DESCRIPTION.VALIDATORS ],
|
||||||
|
currentTag: [ '', VIDEO_TAGS.VALIDATORS ]
|
||||||
|
});
|
||||||
|
|
||||||
|
this.form.valueChanges.subscribe(data => this.onValueChanged(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.buildForm();
|
||||||
|
|
||||||
|
this.videoCategories = this.videoService.videoCategories;
|
||||||
|
this.videoLicences = this.videoService.videoLicences;
|
||||||
|
this.videoLanguages = this.videoService.videoLanguages;
|
||||||
|
|
||||||
|
const id = this.route.snapshot.params['id'];
|
||||||
|
this.videoService.getVideo(id)
|
||||||
|
.subscribe(
|
||||||
|
video => {
|
||||||
|
this.video = video;
|
||||||
|
|
||||||
|
this.hydrateFormFromVideo();
|
||||||
|
},
|
||||||
|
|
||||||
|
err => this.error = 'Cannot fetch video.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
checkForm() {
|
||||||
|
this.forceCheck();
|
||||||
|
|
||||||
|
return this.form.valid === true && this.tagsError === '' && this.fileError === '';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
onTagKeyPress(event: KeyboardEvent) {
|
||||||
|
// Enter press
|
||||||
|
if (event.keyCode === 13) {
|
||||||
|
this.addTagIfPossible();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeTag(tag: string) {
|
||||||
|
this.tags.splice(this.tags.indexOf(tag), 1);
|
||||||
|
this.form.get('currentTag').enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
// Maybe the user forgot to press "enter" when he filled the field
|
||||||
|
this.addTagIfPossible();
|
||||||
|
|
||||||
|
if (this.checkForm() === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.video.patch(this.form.value);
|
||||||
|
|
||||||
|
this.videoService.updateVideo(this.video)
|
||||||
|
.subscribe(
|
||||||
|
() => {
|
||||||
|
this.notificationsService.success('Success', 'Video updated.');
|
||||||
|
this.router.navigate([ '/videos/watch', this.video.id ]);
|
||||||
|
},
|
||||||
|
|
||||||
|
err => {
|
||||||
|
this.error = 'Cannot update the video.';
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private addTagIfPossible() {
|
||||||
|
const currentTag = this.form.value['currentTag'];
|
||||||
|
if (currentTag === undefined) return;
|
||||||
|
|
||||||
|
// Check if the tag is valid and does not already exist
|
||||||
|
if (
|
||||||
|
currentTag.length >= 2 &&
|
||||||
|
this.form.controls['currentTag'].valid &&
|
||||||
|
this.tags.indexOf(currentTag) === -1
|
||||||
|
) {
|
||||||
|
this.tags.push(currentTag);
|
||||||
|
this.form.patchValue({ currentTag: '' });
|
||||||
|
|
||||||
|
if (this.tags.length >= 3) {
|
||||||
|
this.form.get('currentTag').disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.tagsError = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private hydrateFormFromVideo() {
|
||||||
|
this.form.patchValue(this.video.toJSON());
|
||||||
|
}
|
||||||
|
}
|
|
@ -79,6 +79,12 @@
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<ul dropdownMenu id="more-menu" role="menu" aria-labelledby="single-button">
|
<ul dropdownMenu id="more-menu" role="menu" aria-labelledby="single-button">
|
||||||
|
<li *ngIf="canUserUpdateVideo()" role="menuitem">
|
||||||
|
<a class="dropdown-item" title="Update this video" href="#" [routerLink]="[ '/videos/edit', video.id ]">
|
||||||
|
<span class="glyphicon glyphicon-pencil"></span> Update
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li role="menuitem">
|
<li role="menuitem">
|
||||||
<a class="dropdown-item" title="Get magnet URI" href="#" (click)="showMagnetUriModal($event)">
|
<a class="dropdown-item" title="Get magnet URI" href="#" (click)="showMagnetUriModal($event)">
|
||||||
<span class="glyphicon glyphicon-magnet"></span> Magnet
|
<span class="glyphicon glyphicon-magnet"></span> Magnet
|
||||||
|
|
|
@ -187,6 +187,11 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
return this.authService.isLoggedIn();
|
return this.authService.isLoggedIn();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
canUserUpdateVideo() {
|
||||||
|
return this.authService.getUser() !== null &&
|
||||||
|
this.authService.getUser().username === this.video.author;
|
||||||
|
}
|
||||||
|
|
||||||
private checkUserRating() {
|
private checkUserRating() {
|
||||||
// Unlogged users do not have ratings
|
// Unlogged users do not have ratings
|
||||||
if (this.isUserLoggedIn() === false) return;
|
if (this.isUserLoggedIn() === false) return;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
|
||||||
import { VideoAddComponent } from './video-add';
|
import { VideoAddComponent, VideoUpdateComponent } from './video-edit';
|
||||||
import { VideoListComponent } from './video-list';
|
import { VideoListComponent } from './video-list';
|
||||||
import { VideosComponent } from './videos.component';
|
import { VideosComponent } from './videos.component';
|
||||||
import { VideoWatchComponent } from './video-watch';
|
import { VideoWatchComponent } from './video-watch';
|
||||||
|
@ -29,6 +29,15 @@ const videosRoutes: Routes = [
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'edit/:id',
|
||||||
|
component: VideoUpdateComponent,
|
||||||
|
data: {
|
||||||
|
meta: {
|
||||||
|
title: 'Edit a video'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: ':id',
|
path: ':id',
|
||||||
redirectTo: 'watch/:id'
|
redirectTo: 'watch/:id'
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
import { VideosRoutingModule } from './videos-routing.module';
|
import { VideosRoutingModule } from './videos-routing.module';
|
||||||
import { VideosComponent } from './videos.component';
|
import { VideosComponent } from './videos.component';
|
||||||
import { VideoAddComponent } from './video-add';
|
import { VideoAddComponent, VideoUpdateComponent } from './video-edit';
|
||||||
import { VideoListComponent, VideoMiniatureComponent, VideoSortComponent } from './video-list';
|
import { VideoListComponent, VideoMiniatureComponent, VideoSortComponent } from './video-list';
|
||||||
import {
|
import {
|
||||||
VideoWatchComponent,
|
VideoWatchComponent,
|
||||||
|
@ -24,6 +24,7 @@ import { SharedModule } from '../shared';
|
||||||
VideosComponent,
|
VideosComponent,
|
||||||
|
|
||||||
VideoAddComponent,
|
VideoAddComponent,
|
||||||
|
VideoUpdateComponent,
|
||||||
|
|
||||||
VideoListComponent,
|
VideoListComponent,
|
||||||
VideoMiniatureComponent,
|
VideoMiniatureComponent,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user