mirror of https://github.com/Chocobozzz/PeerTube
Client: add basic support for updating a video
parent
a184c71b52
commit
d8e689b864
|
@ -1,5 +1,5 @@
|
|||
export * from './shared';
|
||||
export * from './video-add';
|
||||
export * from './video-edit';
|
||||
export * from './video-list';
|
||||
export * from './video-watch';
|
||||
export * from './videos-routing.module';
|
||||
|
|
|
@ -5,8 +5,11 @@ export class Video {
|
|||
by: string;
|
||||
createdAt: Date;
|
||||
categoryLabel: string;
|
||||
category: string;
|
||||
licenceLabel: string;
|
||||
licence: string;
|
||||
languageLabel: string;
|
||||
language: string;
|
||||
description: string;
|
||||
duration: string;
|
||||
id: string;
|
||||
|
@ -38,8 +41,11 @@ export class Video {
|
|||
author: string,
|
||||
createdAt: string,
|
||||
categoryLabel: string,
|
||||
category: string,
|
||||
licenceLabel: string,
|
||||
licence: string,
|
||||
languageLabel: string;
|
||||
language: string;
|
||||
description: string,
|
||||
duration: number;
|
||||
id: string,
|
||||
|
@ -57,8 +63,11 @@ export class Video {
|
|||
this.author = hash.author;
|
||||
this.createdAt = new Date(hash.createdAt);
|
||||
this.categoryLabel = hash.categoryLabel;
|
||||
this.category = hash.category;
|
||||
this.licenceLabel = hash.licenceLabel;
|
||||
this.licence = hash.licence;
|
||||
this.languageLabel = hash.languageLabel;
|
||||
this.language = hash.language;
|
||||
this.description = hash.description;
|
||||
this.duration = Video.createDurationString(hash.duration);
|
||||
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...
|
||||
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 { Http } from '@angular/http';
|
||||
import { Http, Headers, RequestOptions } from '@angular/http';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import 'rxjs/add/operator/catch';
|
||||
import 'rxjs/add/operator/map';
|
||||
|
@ -80,6 +80,23 @@ export class VideoService {
|
|||
.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) {
|
||||
const params = this.restService.buildRestGetParams(pagination, sort);
|
||||
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
export * from './video-add.component';
|
|
@ -0,0 +1,2 @@
|
|||
export * from './video-add.component';
|
||||
export * from './video-update.component';
|
|
@ -19,7 +19,7 @@ import { VideoService } from '../shared';
|
|||
|
||||
@Component({
|
||||
selector: 'my-videos-add',
|
||||
styleUrls: [ './video-add.component.scss' ],
|
||||
styleUrls: [ './video-edit.component.scss' ],
|
||||
templateUrl: './video-add.component.html'
|
||||
})
|
||||
|
|
@ -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>
|
|
@ -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>
|
||||
|
||||
<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">
|
||||
<a class="dropdown-item" title="Get magnet URI" href="#" (click)="showMagnetUriModal($event)">
|
||||
<span class="glyphicon glyphicon-magnet"></span> Magnet
|
||||
|
|
|
@ -187,6 +187,11 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
|||
return this.authService.isLoggedIn();
|
||||
}
|
||||
|
||||
canUserUpdateVideo() {
|
||||
return this.authService.getUser() !== null &&
|
||||
this.authService.getUser().username === this.video.author;
|
||||
}
|
||||
|
||||
private checkUserRating() {
|
||||
// Unlogged users do not have ratings
|
||||
if (this.isUserLoggedIn() === false) return;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
import { VideoAddComponent } from './video-add';
|
||||
import { VideoAddComponent, VideoUpdateComponent } from './video-edit';
|
||||
import { VideoListComponent } from './video-list';
|
||||
import { VideosComponent } from './videos.component';
|
||||
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',
|
||||
redirectTo: 'watch/:id'
|
||||
|
|
|
@ -2,7 +2,7 @@ import { NgModule } from '@angular/core';
|
|||
|
||||
import { VideosRoutingModule } from './videos-routing.module';
|
||||
import { VideosComponent } from './videos.component';
|
||||
import { VideoAddComponent } from './video-add';
|
||||
import { VideoAddComponent, VideoUpdateComponent } from './video-edit';
|
||||
import { VideoListComponent, VideoMiniatureComponent, VideoSortComponent } from './video-list';
|
||||
import {
|
||||
VideoWatchComponent,
|
||||
|
@ -24,6 +24,7 @@ import { SharedModule } from '../shared';
|
|||
VideosComponent,
|
||||
|
||||
VideoAddComponent,
|
||||
VideoUpdateComponent,
|
||||
|
||||
VideoListComponent,
|
||||
VideoMiniatureComponent,
|
||||
|
|
Loading…
Reference in New Issue