From 772d5642ba617865519ca5e590061adf174866d4 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Wed, 25 Jul 2018 10:28:43 +0200 Subject: Improve captions UX (at least I've tried) --- client/src/app/shared/forms/form-reactive.ts | 3 +++ .../app/shared/forms/reactive-file.component.html | 2 +- .../app/shared/forms/reactive-file.component.ts | 3 +++ .../+video-edit/shared/video-edit.component.html | 29 +++++++++++++++++++--- .../+video-edit/shared/video-edit.component.scss | 23 +++++++++++++++++ .../+video-edit/shared/video-edit.component.ts | 11 +++++++- .../+video-edit/video-update-routing.module.ts | 2 ++ .../videos/+video-edit/video-update.component.ts | 15 ++++++++++- .../app/videos/+video-edit/video-update.module.ts | 4 ++- 9 files changed, 85 insertions(+), 7 deletions(-) diff --git a/client/src/app/shared/forms/form-reactive.ts b/client/src/app/shared/forms/form-reactive.ts index 441ec8203..e4f7481b5 100644 --- a/client/src/app/shared/forms/form-reactive.ts +++ b/client/src/app/shared/forms/form-reactive.ts @@ -10,6 +10,7 @@ export type FormReactiveValidationMessages = { export abstract class FormReactive { protected abstract formValidatorService: FormValidatorService + protected formChanged = false form: FormGroup formErrors: FormReactiveErrors @@ -31,6 +32,8 @@ export abstract class FormReactive { this.formErrors[ field ] = '' const control = this.form.get(field) + if (control.dirty) this.formChanged = true + // Don't care if dirty on force check const isDirty = control.dirty || forceCheck === true if (control && isDirty && !control.valid) { diff --git a/client/src/app/shared/forms/reactive-file.component.html b/client/src/app/shared/forms/reactive-file.component.html index 9fb1c9e3e..7d691059d 100644 --- a/client/src/app/shared/forms/reactive-file.component.html +++ b/client/src/app/shared/forms/reactive-file.component.html @@ -4,7 +4,7 @@ diff --git a/client/src/app/shared/forms/reactive-file.component.ts b/client/src/app/shared/forms/reactive-file.component.ts index f5758b643..8d22aa56c 100644 --- a/client/src/app/shared/forms/reactive-file.component.ts +++ b/client/src/app/shared/forms/reactive-file.component.ts @@ -25,6 +25,7 @@ export class ReactiveFileComponent implements OnInit, ControlValueAccessor { @Output() fileChanged = new EventEmitter() allowedExtensionsMessage = '' + fileInputValue: any private file: File @@ -63,6 +64,8 @@ export class ReactiveFileComponent implements OnInit, ControlValueAccessor { writeValue (file: any) { this.file = file + + if (!this.file) this.fileInputValue = null } registerOnChange (fn: (_: any) => void) { diff --git a/client/src/app/videos/+video-edit/shared/video-edit.component.html b/client/src/app/videos/+video-edit/shared/video-edit.component.html index 16dbf5cfc..2c40747ba 100644 --- a/client/src/app/videos/+video-edit/shared/video-edit.component.html +++ b/client/src/app/videos/+video-edit/shared/video-edit.component.html @@ -142,10 +142,33 @@
-
- +
+ + - Delete +
Already uploaded ✔
+ + Delete +
+ + + + +
Will be created on update
+ + Cancel create +
+ + + + +
Will be deleted on update
+ + Cancel deletion +
diff --git a/client/src/app/videos/+video-edit/shared/video-edit.component.scss b/client/src/app/videos/+video-edit/shared/video-edit.component.scss index 058ccba36..f3d9ee44a 100644 --- a/client/src/app/videos/+video-edit/shared/video-edit.component.scss +++ b/client/src/app/videos/+video-edit/shared/video-edit.component.scss @@ -46,11 +46,34 @@ height: 40px; align-items: center; + a.caption-entry-label { + @include disable-default-a-behaviour; + + color: #000; + + &:hover { + opacity: 0.8; + } + } + .caption-entry-label { font-size: 15px; font-weight: bold; margin-right: 20px; + width: 150px; + } + + .caption-entry-state { + width: 200px; + + &.caption-entry-state-create { + color: #39CC0B; + } + + &.caption-entry-state-delete { + color: #FF0000; + } } .caption-entry-delete { diff --git a/client/src/app/videos/+video-edit/shared/video-edit.component.ts b/client/src/app/videos/+video-edit/shared/video-edit.component.ts index 00c7bc41d..b8aef99dd 100644 --- a/client/src/app/videos/+video-edit/shared/video-edit.component.ts +++ b/client/src/app/videos/+video-edit/shared/video-edit.component.ts @@ -50,6 +50,7 @@ export class VideoEditComponent implements OnInit, OnDestroy { private schedulerInterval private firstPatchDone = false + private initialVideoCaptions: string[] = [] constructor ( private formValidatorService: FormValidatorService, @@ -127,6 +128,8 @@ export class VideoEditComponent implements OnInit, OnDestroy { this.videoLanguages = this.serverService.getVideoLanguages() this.schedulerInterval = setInterval(() => this.minScheduledDate = new Date(), 1000 * 60) // Update every minute + + this.initialVideoCaptions = this.videoCaptions.map(c => c.language.id) } ngOnDestroy () { @@ -147,7 +150,13 @@ export class VideoEditComponent implements OnInit, OnDestroy { ) } - deleteCaption (caption: VideoCaptionEdit) { + async deleteCaption (caption: VideoCaptionEdit) { + // Caption recovers his former state + if (caption.action && this.initialVideoCaptions.indexOf(caption.language.id) !== -1) { + caption.action = undefined + return + } + // This caption is not on the server, just remove it from our array if (caption.action === 'CREATE') { removeElementFromArray(this.videoCaptions, caption) diff --git a/client/src/app/videos/+video-edit/video-update-routing.module.ts b/client/src/app/videos/+video-edit/video-update-routing.module.ts index dfeeb8a90..564b8fb45 100644 --- a/client/src/app/videos/+video-edit/video-update-routing.module.ts +++ b/client/src/app/videos/+video-edit/video-update-routing.module.ts @@ -6,12 +6,14 @@ import { MetaGuard } from '@ngx-meta/core' import { LoginGuard } from '../../core' import { VideoUpdateComponent } from './video-update.component' import { VideoUpdateResolver } from '@app/videos/+video-edit/video-update.resolver' +import { CanDeactivateGuard } from '@app/shared/guards/can-deactivate-guard.service' const videoUpdateRoutes: Routes = [ { path: '', component: VideoUpdateComponent, canActivate: [ MetaGuard, LoginGuard ], + canDeactivate: [ CanDeactivateGuard ], resolve: { videoData: VideoUpdateResolver } diff --git a/client/src/app/videos/+video-edit/video-update.component.ts b/client/src/app/videos/+video-edit/video-update.component.ts index 952fe0293..798c48f3c 100644 --- a/client/src/app/videos/+video-edit/video-update.component.ts +++ b/client/src/app/videos/+video-edit/video-update.component.ts @@ -29,6 +29,8 @@ export class VideoUpdateComponent extends FormReactive implements OnInit { schedulePublicationPossible = false videoCaptions: VideoCaptionEdit[] = [] + private updateDone = false + constructor ( protected formValidatorService: FormValidatorService, private route: ActivatedRoute, @@ -65,7 +67,7 @@ export class VideoUpdateComponent extends FormReactive implements OnInit { this.schedulePublicationPossible = this.video.privacy === VideoPrivacy.PRIVATE } - // FIXME: Angular does not detec + // FIXME: Angular does not detect the change inside this subscription, so use the patched setTimeout setTimeout(() => this.hydrateFormFromVideo()) }, @@ -76,6 +78,16 @@ export class VideoUpdateComponent extends FormReactive implements OnInit { ) } + canDeactivate () { + if (this.updateDone === true) return { canDeactivate: true } + + for (const caption of this.videoCaptions) { + if (caption.action) return { canDeactivate: false } + } + + return { canDeactivate: this.formChanged === false } + } + checkForm () { this.forceCheck() @@ -100,6 +112,7 @@ export class VideoUpdateComponent extends FormReactive implements OnInit { ) .subscribe( () => { + this.updateDone = true this.isUpdatingVideo = false this.loadingBar.complete() this.notificationsService.success(this.i18n('Success'), this.i18n('Video updated.')) diff --git a/client/src/app/videos/+video-edit/video-update.module.ts b/client/src/app/videos/+video-edit/video-update.module.ts index 4f5d72cec..d60aa699f 100644 --- a/client/src/app/videos/+video-edit/video-update.module.ts +++ b/client/src/app/videos/+video-edit/video-update.module.ts @@ -4,6 +4,7 @@ import { VideoEditModule } from './shared/video-edit.module' import { VideoUpdateRoutingModule } from './video-update-routing.module' import { VideoUpdateComponent } from './video-update.component' import { VideoUpdateResolver } from '@app/videos/+video-edit/video-update.resolver' +import { CanDeactivateGuard } from '@app/shared/guards/can-deactivate-guard.service' @NgModule({ imports: [ @@ -21,7 +22,8 @@ import { VideoUpdateResolver } from '@app/videos/+video-edit/video-update.resolv ], providers: [ - VideoUpdateResolver + VideoUpdateResolver, + CanDeactivateGuard ] }) export class VideoUpdateModule { } -- cgit v1.2.3