From: Chocobozzz <me@florianbigard.com>
Date: Wed, 25 Jul 2018 08:28:43 +0000 (+0200)
Subject: Improve captions UX (at least I've tried)
X-Git-Tag: v1.0.0-beta.10.pre.1~11
X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=772d5642ba617865519ca5e590061adf174866d4;p=oweals%2Fpeertube.git

Improve captions UX (at least I've tried)
---

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 @@
     <input
       type="file"
       [name]="inputName" [id]="inputName" [accept]="extensions"
-      (change)="fileChange($event)"
+      (change)="fileChange($event)" [(ngModel)]="fileInputValue"
     />
   </div>
 
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<Blob>()
 
   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 @@
 
         <div class="form-group" *ngFor="let videoCaption of videoCaptions">
 
-          <div *ngIf="videoCaption.action !== 'REMOVE'" class="caption-entry">
-            <div class="caption-entry-label">{{ videoCaption.language.label }}</div>
+          <div class="caption-entry">
+            <ng-container *ngIf="!videoCaption.action">
+              <a
+                i18n-title title="See the subtitle file" class="caption-entry-label" target="_blank" rel="noopener noreferrer"
+                [href]="videoCaption.captionPath"
+              >{{ videoCaption.language.label }}</a>
 
-            <span i18n class="caption-entry-delete" (click)="deleteCaption(videoCaption)">Delete</span>
+              <div class="caption-entry-state">Already uploaded	&#10004;</div>
+
+              <span i18n class="caption-entry-delete" (click)="deleteCaption(videoCaption)">Delete</span>
+            </ng-container>
+
+            <ng-container *ngIf="videoCaption.action === 'CREATE'">
+              <span class="caption-entry-label">{{ videoCaption.language.label }}</span>
+
+              <div class="caption-entry-state caption-entry-state-create">Will be created on update</div>
+
+              <span i18n class="caption-entry-delete" (click)="deleteCaption(videoCaption)">Cancel create</span>
+            </ng-container>
+
+            <ng-container *ngIf="videoCaption.action === 'REMOVE'">
+              <span class="caption-entry-label">{{ videoCaption.language.label }}</span>
+
+              <div class="caption-entry-state caption-entry-state-delete">Will be deleted on update</div>
+
+              <span i18n class="caption-entry-delete" (click)="deleteCaption(videoCaption)">Cancel deletion</span>
+            </ng-container>
           </div>
         </div>
 
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 { }