From d8e689b864749648d03cf4391fd4a475126f01cd Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Mon, 10 Apr 2017 21:15:28 +0200 Subject: Client: add basic support for updating a video --- client/src/app/videos/video-edit/index.ts | 2 + .../app/videos/video-edit/video-add.component.html | 128 ++++++++++++ .../app/videos/video-edit/video-add.component.ts | 230 +++++++++++++++++++++ .../videos/video-edit/video-edit.component.scss | 57 +++++ .../videos/video-edit/video-update.component.html | 101 +++++++++ .../videos/video-edit/video-update.component.ts | 170 +++++++++++++++ 6 files changed, 688 insertions(+) create mode 100644 client/src/app/videos/video-edit/index.ts create mode 100644 client/src/app/videos/video-edit/video-add.component.html create mode 100644 client/src/app/videos/video-edit/video-add.component.ts create mode 100644 client/src/app/videos/video-edit/video-edit.component.scss create mode 100644 client/src/app/videos/video-edit/video-update.component.html create mode 100644 client/src/app/videos/video-edit/video-update.component.ts (limited to 'client/src/app/videos/video-edit') diff --git a/client/src/app/videos/video-edit/index.ts b/client/src/app/videos/video-edit/index.ts new file mode 100644 index 000000000..5ce4fb9b1 --- /dev/null +++ b/client/src/app/videos/video-edit/index.ts @@ -0,0 +1,2 @@ +export * from './video-add.component'; +export * from './video-update.component'; diff --git a/client/src/app/videos/video-edit/video-add.component.html b/client/src/app/videos/video-edit/video-add.component.html new file mode 100644 index 000000000..104747a8c --- /dev/null +++ b/client/src/app/videos/video-edit/video-add.component.html @@ -0,0 +1,128 @@ +

Upload a video

+ +
{{ error }}
+ +
+
+ + +
+ {{ formErrors.name }} +
+
+ +
+ + +
+ +
+ + + +
+ {{ formErrors.category }} +
+
+ +
+ + + +
+ {{ formErrors.licence }} +
+
+ +
+ + + +
+ {{ formErrors.language }} +
+
+ +
+ (press enter to add the tag) + +
+ {{ formErrors.currentTag }} +
+
+ +
+
+ {{ tag }} + x +
+
+ +
+ {{ tagsError }} +
+ +
+ +
+ Select the video... + +
+
+ +
+
+ {{ filename }} + +
+
+ +
+ {{ fileError }} +
+ +
+ + +
+ {{ formErrors.description }} +
+
+ +
+ +
+ +
+ +
+
diff --git a/client/src/app/videos/video-edit/video-add.component.ts b/client/src/app/videos/video-edit/video-add.component.ts new file mode 100644 index 000000000..e3cf0e9d8 --- /dev/null +++ b/client/src/app/videos/video-edit/video-add.component.ts @@ -0,0 +1,230 @@ +import { Component, ElementRef, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup } from '@angular/forms'; +import { 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 { VideoService } from '../shared'; + +@Component({ + selector: 'my-videos-add', + styleUrls: [ './video-edit.component.scss' ], + templateUrl: './video-add.component.html' +}) + +export class VideoAddComponent extends FormReactive implements OnInit { + tags: string[] = []; + uploader: FileUploader; + videoCategories = []; + videoLicences = []; + videoLanguages = []; + + 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 router: Router, + private notificationsService: NotificationsService, + private videoService: VideoService + ) { + super(); + } + + get filename() { + if (this.uploader.queue.length === 0) { + return null; + } + + return this.uploader.queue[0].file.name; + } + + 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.videoCategories = this.videoService.videoCategories; + this.videoLicences = this.videoService.videoLicences; + this.videoLanguages = this.videoService.videoLanguages; + + this.uploader = new FileUploader({ + authToken: this.authService.getRequestHeaderValue(), + queueLimit: 1, + url: '/api/v1/videos', + removeAfterUpload: true + }); + + this.uploader.onBuildItemForm = (item, form) => { + const name = this.form.value['name']; + const nsfw = this.form.value['nsfw']; + const category = this.form.value['category']; + const licence = this.form.value['licence']; + const language = this.form.value['language']; + const description = this.form.value['description']; + + form.append('name', name); + form.append('category', category); + form.append('nsfw', nsfw); + form.append('licence', licence); + + // Language is optional + if (language) { + form.append('language', language); + } + + form.append('description', description); + + for (let i = 0; i < this.tags.length; i++) { + form.append(`tags[${i}]`, this.tags[i]); + } + }; + + this.buildForm(); + } + + checkForm() { + this.forceCheck(); + + if (this.filename === null) { + this.fileError = 'You did not add a file.'; + } + + return this.form.valid === true && this.tagsError === '' && this.fileError === ''; + } + + fileChanged() { + this.fileError = ''; + } + + onTagKeyPress(event: KeyboardEvent) { + // Enter press + if (event.keyCode === 13) { + this.addTagIfPossible(); + } + } + + removeFile() { + this.uploader.clearQueue(); + } + + removeTag(tag: string) { + this.tags.splice(this.tags.indexOf(tag), 1); + this.form.get('currentTag').enable(); + } + + upload() { + // Maybe the user forgot to press "enter" when he filled the field + this.addTagIfPossible(); + + if (this.checkForm() === false) { + return; + } + + const item = this.uploader.queue[0]; + // TODO: wait for https://github.com/valor-software/ng2-file-upload/pull/242 + item.alias = 'videofile'; + + // FIXME: remove + // Run detection change for progress bar + const interval = setInterval(() => { ; }, 250); + + item.onSuccess = () => { + clearInterval(interval); + + console.log('Video uploaded.'); + this.notificationsService.success('Success', 'Video uploaded.'); + + + // Print all the videos once it's finished + this.router.navigate(['/videos/list']); + }; + + item.onError = (response: string, status: number) => { + clearInterval(interval); + + // We need to handle manually these cases beceause we use the FileUpload component + if (status === 400) { + this.error = response; + } else if (status === 401) { + this.error = 'Access token was expired, refreshing token...'; + this.authService.refreshAccessToken().subscribe( + () => { + // Update the uploader request header + this.uploader.authToken = this.authService.getRequestHeaderValue(); + this.error += ' access token refreshed. Please retry your request.'; + } + ); + } else { + this.error = 'Unknow error'; + console.error(this.error); + } + }; + + this.uploader.uploadAll(); + } + + 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 = ''; + } + } +} diff --git a/client/src/app/videos/video-edit/video-edit.component.scss b/client/src/app/videos/video-edit/video-edit.component.scss new file mode 100644 index 000000000..11ee3297e --- /dev/null +++ b/client/src/app/videos/video-edit/video-edit.component.scss @@ -0,0 +1,57 @@ +.btn-file { + position: relative; + overflow: hidden; + display: block; +} + +.btn-file input[type=file] { + position: absolute; + top: 0; + right: 0; + min-width: 100%; + min-height: 100%; + font-size: 100px; + text-align: right; + filter: alpha(opacity=0); + opacity: 0; + outline: none; + background: white; + cursor: inherit; + display: block; +} + +.name_file { + display: inline-block; + margin-left: 10px; +} + +.form-group { + margin-bottom: 10px; +} + +div.tags { + height: 40px; + font-size: 20px; + margin-top: 20px; + + .tag { + margin-right: 10px; + + .remove { + cursor: pointer; + } + } +} + +div.file-to-upload { + height: 40px; + + .glyphicon-remove { + cursor: pointer; + } +} + +.little-information { + font-size: 0.8em; + font-style: italic; +} diff --git a/client/src/app/videos/video-edit/video-update.component.html b/client/src/app/videos/video-edit/video-update.component.html new file mode 100644 index 000000000..665a952d0 --- /dev/null +++ b/client/src/app/videos/video-edit/video-update.component.html @@ -0,0 +1,101 @@ +

Update {{ video.name }}

+ +
{{ error }}
+ +
+
+ + +
+ {{ formErrors.name }} +
+
+ +
+ + +
+ +
+ + + +
+ {{ formErrors.category }} +
+
+ +
+ + + +
+ {{ formErrors.licence }} +
+
+ +
+ + + +
+ {{ formErrors.language }} +
+
+ +
+ (press enter to add the tag) + +
+ {{ formErrors.currentTag }} +
+
+ +
+
+ {{ tag }} + x +
+
+ +
+ {{ tagsError }} +
+ +
+ + +
+ {{ formErrors.description }} +
+
+ +
+ +
+
diff --git a/client/src/app/videos/video-edit/video-update.component.ts b/client/src/app/videos/video-edit/video-update.component.ts new file mode 100644 index 000000000..b45780a41 --- /dev/null +++ b/client/src/app/videos/video-edit/video-update.component.ts @@ -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()); + } +} -- cgit v1.2.3