aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2018-07-25 10:28:43 +0200
committerChocobozzz <me@florianbigard.com>2018-07-25 10:28:43 +0200
commit772d5642ba617865519ca5e590061adf174866d4 (patch)
tree27e2f3c9a78a4de67128f005d2bd73a5eecca804 /client/src/app
parentd73c98884ec7f970ed95a01fb2d445d10c53c817 (diff)
downloadPeerTube-772d5642ba617865519ca5e590061adf174866d4.tar.gz
PeerTube-772d5642ba617865519ca5e590061adf174866d4.tar.zst
PeerTube-772d5642ba617865519ca5e590061adf174866d4.zip
Improve captions UX (at least I've tried)
Diffstat (limited to 'client/src/app')
-rw-r--r--client/src/app/shared/forms/form-reactive.ts3
-rw-r--r--client/src/app/shared/forms/reactive-file.component.html2
-rw-r--r--client/src/app/shared/forms/reactive-file.component.ts3
-rw-r--r--client/src/app/videos/+video-edit/shared/video-edit.component.html29
-rw-r--r--client/src/app/videos/+video-edit/shared/video-edit.component.scss23
-rw-r--r--client/src/app/videos/+video-edit/shared/video-edit.component.ts11
-rw-r--r--client/src/app/videos/+video-edit/video-update-routing.module.ts2
-rw-r--r--client/src/app/videos/+video-edit/video-update.component.ts15
-rw-r--r--client/src/app/videos/+video-edit/video-update.module.ts4
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 = {
10 10
11export abstract class FormReactive { 11export abstract class FormReactive {
12 protected abstract formValidatorService: FormValidatorService 12 protected abstract formValidatorService: FormValidatorService
13 protected formChanged = false
13 14
14 form: FormGroup 15 form: FormGroup
15 formErrors: FormReactiveErrors 16 formErrors: FormReactiveErrors
@@ -31,6 +32,8 @@ export abstract class FormReactive {
31 this.formErrors[ field ] = '' 32 this.formErrors[ field ] = ''
32 const control = this.form.get(field) 33 const control = this.form.get(field)
33 34
35 if (control.dirty) this.formChanged = true
36
34 // Don't care if dirty on force check 37 // Don't care if dirty on force check
35 const isDirty = control.dirty || forceCheck === true 38 const isDirty = control.dirty || forceCheck === true
36 if (control && isDirty && !control.valid) { 39 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 @@
4 <input 4 <input
5 type="file" 5 type="file"
6 [name]="inputName" [id]="inputName" [accept]="extensions" 6 [name]="inputName" [id]="inputName" [accept]="extensions"
7 (change)="fileChange($event)" 7 (change)="fileChange($event)" [(ngModel)]="fileInputValue"
8 /> 8 />
9 </div> 9 </div>
10 10
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 {
25 @Output() fileChanged = new EventEmitter<Blob>() 25 @Output() fileChanged = new EventEmitter<Blob>()
26 26
27 allowedExtensionsMessage = '' 27 allowedExtensionsMessage = ''
28 fileInputValue: any
28 29
29 private file: File 30 private file: File
30 31
@@ -63,6 +64,8 @@ export class ReactiveFileComponent implements OnInit, ControlValueAccessor {
63 64
64 writeValue (file: any) { 65 writeValue (file: any) {
65 this.file = file 66 this.file = file
67
68 if (!this.file) this.fileInputValue = null
66 } 69 }
67 70
68 registerOnChange (fn: (_: any) => void) { 71 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 @@
142 142
143 <div class="form-group" *ngFor="let videoCaption of videoCaptions"> 143 <div class="form-group" *ngFor="let videoCaption of videoCaptions">
144 144
145 <div *ngIf="videoCaption.action !== 'REMOVE'" class="caption-entry"> 145 <div class="caption-entry">
146 <div class="caption-entry-label">{{ videoCaption.language.label }}</div> 146 <ng-container *ngIf="!videoCaption.action">
147 <a
148 i18n-title title="See the subtitle file" class="caption-entry-label" target="_blank" rel="noopener noreferrer"
149 [href]="videoCaption.captionPath"
150 >{{ videoCaption.language.label }}</a>
147 151
148 <span i18n class="caption-entry-delete" (click)="deleteCaption(videoCaption)">Delete</span> 152 <div class="caption-entry-state">Already uploaded &#10004;</div>
153
154 <span i18n class="caption-entry-delete" (click)="deleteCaption(videoCaption)">Delete</span>
155 </ng-container>
156
157 <ng-container *ngIf="videoCaption.action === 'CREATE'">
158 <span class="caption-entry-label">{{ videoCaption.language.label }}</span>
159
160 <div class="caption-entry-state caption-entry-state-create">Will be created on update</div>
161
162 <span i18n class="caption-entry-delete" (click)="deleteCaption(videoCaption)">Cancel create</span>
163 </ng-container>
164
165 <ng-container *ngIf="videoCaption.action === 'REMOVE'">
166 <span class="caption-entry-label">{{ videoCaption.language.label }}</span>
167
168 <div class="caption-entry-state caption-entry-state-delete">Will be deleted on update</div>
169
170 <span i18n class="caption-entry-delete" (click)="deleteCaption(videoCaption)">Cancel deletion</span>
171 </ng-container>
149 </div> 172 </div>
150 </div> 173 </div>
151 174
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 @@
46 height: 40px; 46 height: 40px;
47 align-items: center; 47 align-items: center;
48 48
49 a.caption-entry-label {
50 @include disable-default-a-behaviour;
51
52 color: #000;
53
54 &:hover {
55 opacity: 0.8;
56 }
57 }
58
49 .caption-entry-label { 59 .caption-entry-label {
50 font-size: 15px; 60 font-size: 15px;
51 font-weight: bold; 61 font-weight: bold;
52 62
53 margin-right: 20px; 63 margin-right: 20px;
64 width: 150px;
65 }
66
67 .caption-entry-state {
68 width: 200px;
69
70 &.caption-entry-state-create {
71 color: #39CC0B;
72 }
73
74 &.caption-entry-state-delete {
75 color: #FF0000;
76 }
54 } 77 }
55 78
56 .caption-entry-delete { 79 .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 {
50 50
51 private schedulerInterval 51 private schedulerInterval
52 private firstPatchDone = false 52 private firstPatchDone = false
53 private initialVideoCaptions: string[] = []
53 54
54 constructor ( 55 constructor (
55 private formValidatorService: FormValidatorService, 56 private formValidatorService: FormValidatorService,
@@ -127,6 +128,8 @@ export class VideoEditComponent implements OnInit, OnDestroy {
127 this.videoLanguages = this.serverService.getVideoLanguages() 128 this.videoLanguages = this.serverService.getVideoLanguages()
128 129
129 this.schedulerInterval = setInterval(() => this.minScheduledDate = new Date(), 1000 * 60) // Update every minute 130 this.schedulerInterval = setInterval(() => this.minScheduledDate = new Date(), 1000 * 60) // Update every minute
131
132 this.initialVideoCaptions = this.videoCaptions.map(c => c.language.id)
130 } 133 }
131 134
132 ngOnDestroy () { 135 ngOnDestroy () {
@@ -147,7 +150,13 @@ export class VideoEditComponent implements OnInit, OnDestroy {
147 ) 150 )
148 } 151 }
149 152
150 deleteCaption (caption: VideoCaptionEdit) { 153 async deleteCaption (caption: VideoCaptionEdit) {
154 // Caption recovers his former state
155 if (caption.action && this.initialVideoCaptions.indexOf(caption.language.id) !== -1) {
156 caption.action = undefined
157 return
158 }
159
151 // This caption is not on the server, just remove it from our array 160 // This caption is not on the server, just remove it from our array
152 if (caption.action === 'CREATE') { 161 if (caption.action === 'CREATE') {
153 removeElementFromArray(this.videoCaptions, caption) 162 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'
6import { LoginGuard } from '../../core' 6import { LoginGuard } from '../../core'
7import { VideoUpdateComponent } from './video-update.component' 7import { VideoUpdateComponent } from './video-update.component'
8import { VideoUpdateResolver } from '@app/videos/+video-edit/video-update.resolver' 8import { VideoUpdateResolver } from '@app/videos/+video-edit/video-update.resolver'
9import { CanDeactivateGuard } from '@app/shared/guards/can-deactivate-guard.service'
9 10
10const videoUpdateRoutes: Routes = [ 11const videoUpdateRoutes: Routes = [
11 { 12 {
12 path: '', 13 path: '',
13 component: VideoUpdateComponent, 14 component: VideoUpdateComponent,
14 canActivate: [ MetaGuard, LoginGuard ], 15 canActivate: [ MetaGuard, LoginGuard ],
16 canDeactivate: [ CanDeactivateGuard ],
15 resolve: { 17 resolve: {
16 videoData: VideoUpdateResolver 18 videoData: VideoUpdateResolver
17 } 19 }
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 {
29 schedulePublicationPossible = false 29 schedulePublicationPossible = false
30 videoCaptions: VideoCaptionEdit[] = [] 30 videoCaptions: VideoCaptionEdit[] = []
31 31
32 private updateDone = false
33
32 constructor ( 34 constructor (
33 protected formValidatorService: FormValidatorService, 35 protected formValidatorService: FormValidatorService,
34 private route: ActivatedRoute, 36 private route: ActivatedRoute,
@@ -65,7 +67,7 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
65 this.schedulePublicationPossible = this.video.privacy === VideoPrivacy.PRIVATE 67 this.schedulePublicationPossible = this.video.privacy === VideoPrivacy.PRIVATE
66 } 68 }
67 69
68 // FIXME: Angular does not detec 70 // FIXME: Angular does not detect the change inside this subscription, so use the patched setTimeout
69 setTimeout(() => this.hydrateFormFromVideo()) 71 setTimeout(() => this.hydrateFormFromVideo())
70 }, 72 },
71 73
@@ -76,6 +78,16 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
76 ) 78 )
77 } 79 }
78 80
81 canDeactivate () {
82 if (this.updateDone === true) return { canDeactivate: true }
83
84 for (const caption of this.videoCaptions) {
85 if (caption.action) return { canDeactivate: false }
86 }
87
88 return { canDeactivate: this.formChanged === false }
89 }
90
79 checkForm () { 91 checkForm () {
80 this.forceCheck() 92 this.forceCheck()
81 93
@@ -100,6 +112,7 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
100 ) 112 )
101 .subscribe( 113 .subscribe(
102 () => { 114 () => {
115 this.updateDone = true
103 this.isUpdatingVideo = false 116 this.isUpdatingVideo = false
104 this.loadingBar.complete() 117 this.loadingBar.complete()
105 this.notificationsService.success(this.i18n('Success'), this.i18n('Video updated.')) 118 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'
4import { VideoUpdateRoutingModule } from './video-update-routing.module' 4import { VideoUpdateRoutingModule } from './video-update-routing.module'
5import { VideoUpdateComponent } from './video-update.component' 5import { VideoUpdateComponent } from './video-update.component'
6import { VideoUpdateResolver } from '@app/videos/+video-edit/video-update.resolver' 6import { VideoUpdateResolver } from '@app/videos/+video-edit/video-update.resolver'
7import { CanDeactivateGuard } from '@app/shared/guards/can-deactivate-guard.service'
7 8
8@NgModule({ 9@NgModule({
9 imports: [ 10 imports: [
@@ -21,7 +22,8 @@ import { VideoUpdateResolver } from '@app/videos/+video-edit/video-update.resolv
21 ], 22 ],
22 23
23 providers: [ 24 providers: [
24 VideoUpdateResolver 25 VideoUpdateResolver,
26 CanDeactivateGuard
25 ] 27 ]
26}) 28})
27export class VideoUpdateModule { } 29export class VideoUpdateModule { }