Improve captions UX (at least I've tried)

pull/850/head
Chocobozzz 2018-07-25 10:28:43 +02:00
parent d73c98884e
commit 772d5642ba
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
9 changed files with 85 additions and 7 deletions

View File

@ -10,6 +10,7 @@ export type FormReactiveValidationMessages = {
export abstract class FormReactive { export abstract class FormReactive {
protected abstract formValidatorService: FormValidatorService protected abstract formValidatorService: FormValidatorService
protected formChanged = false
form: FormGroup form: FormGroup
formErrors: FormReactiveErrors formErrors: FormReactiveErrors
@ -31,6 +32,8 @@ export abstract class FormReactive {
this.formErrors[ field ] = '' this.formErrors[ field ] = ''
const control = this.form.get(field) const control = this.form.get(field)
if (control.dirty) this.formChanged = true
// Don't care if dirty on force check // Don't care if dirty on force check
const isDirty = control.dirty || forceCheck === true const isDirty = control.dirty || forceCheck === true
if (control && isDirty && !control.valid) { if (control && isDirty && !control.valid) {

View File

@ -4,7 +4,7 @@
<input <input
type="file" type="file"
[name]="inputName" [id]="inputName" [accept]="extensions" [name]="inputName" [id]="inputName" [accept]="extensions"
(change)="fileChange($event)" (change)="fileChange($event)" [(ngModel)]="fileInputValue"
/> />
</div> </div>

View File

@ -25,6 +25,7 @@ export class ReactiveFileComponent implements OnInit, ControlValueAccessor {
@Output() fileChanged = new EventEmitter<Blob>() @Output() fileChanged = new EventEmitter<Blob>()
allowedExtensionsMessage = '' allowedExtensionsMessage = ''
fileInputValue: any
private file: File private file: File
@ -63,6 +64,8 @@ export class ReactiveFileComponent implements OnInit, ControlValueAccessor {
writeValue (file: any) { writeValue (file: any) {
this.file = file this.file = file
if (!this.file) this.fileInputValue = null
} }
registerOnChange (fn: (_: any) => void) { registerOnChange (fn: (_: any) => void) {

View File

@ -142,10 +142,33 @@
<div class="form-group" *ngFor="let videoCaption of videoCaptions"> <div class="form-group" *ngFor="let videoCaption of videoCaptions">
<div *ngIf="videoCaption.action !== 'REMOVE'" class="caption-entry"> <div class="caption-entry">
<div class="caption-entry-label">{{ videoCaption.language.label }}</div> <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>
</div> </div>

View File

@ -46,11 +46,34 @@
height: 40px; height: 40px;
align-items: center; align-items: center;
a.caption-entry-label {
@include disable-default-a-behaviour;
color: #000;
&:hover {
opacity: 0.8;
}
}
.caption-entry-label { .caption-entry-label {
font-size: 15px; font-size: 15px;
font-weight: bold; font-weight: bold;
margin-right: 20px; 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 { .caption-entry-delete {

View File

@ -50,6 +50,7 @@ export class VideoEditComponent implements OnInit, OnDestroy {
private schedulerInterval private schedulerInterval
private firstPatchDone = false private firstPatchDone = false
private initialVideoCaptions: string[] = []
constructor ( constructor (
private formValidatorService: FormValidatorService, private formValidatorService: FormValidatorService,
@ -127,6 +128,8 @@ export class VideoEditComponent implements OnInit, OnDestroy {
this.videoLanguages = this.serverService.getVideoLanguages() this.videoLanguages = this.serverService.getVideoLanguages()
this.schedulerInterval = setInterval(() => this.minScheduledDate = new Date(), 1000 * 60) // Update every minute this.schedulerInterval = setInterval(() => this.minScheduledDate = new Date(), 1000 * 60) // Update every minute
this.initialVideoCaptions = this.videoCaptions.map(c => c.language.id)
} }
ngOnDestroy () { 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 // This caption is not on the server, just remove it from our array
if (caption.action === 'CREATE') { if (caption.action === 'CREATE') {
removeElementFromArray(this.videoCaptions, caption) removeElementFromArray(this.videoCaptions, caption)

View File

@ -6,12 +6,14 @@ import { MetaGuard } from '@ngx-meta/core'
import { LoginGuard } from '../../core' import { LoginGuard } from '../../core'
import { VideoUpdateComponent } from './video-update.component' import { VideoUpdateComponent } from './video-update.component'
import { VideoUpdateResolver } from '@app/videos/+video-edit/video-update.resolver' import { VideoUpdateResolver } from '@app/videos/+video-edit/video-update.resolver'
import { CanDeactivateGuard } from '@app/shared/guards/can-deactivate-guard.service'
const videoUpdateRoutes: Routes = [ const videoUpdateRoutes: Routes = [
{ {
path: '', path: '',
component: VideoUpdateComponent, component: VideoUpdateComponent,
canActivate: [ MetaGuard, LoginGuard ], canActivate: [ MetaGuard, LoginGuard ],
canDeactivate: [ CanDeactivateGuard ],
resolve: { resolve: {
videoData: VideoUpdateResolver videoData: VideoUpdateResolver
} }

View File

@ -29,6 +29,8 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
schedulePublicationPossible = false schedulePublicationPossible = false
videoCaptions: VideoCaptionEdit[] = [] videoCaptions: VideoCaptionEdit[] = []
private updateDone = false
constructor ( constructor (
protected formValidatorService: FormValidatorService, protected formValidatorService: FormValidatorService,
private route: ActivatedRoute, private route: ActivatedRoute,
@ -65,7 +67,7 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
this.schedulePublicationPossible = this.video.privacy === VideoPrivacy.PRIVATE 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()) 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 () { checkForm () {
this.forceCheck() this.forceCheck()
@ -100,6 +112,7 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
) )
.subscribe( .subscribe(
() => { () => {
this.updateDone = true
this.isUpdatingVideo = false this.isUpdatingVideo = false
this.loadingBar.complete() this.loadingBar.complete()
this.notificationsService.success(this.i18n('Success'), this.i18n('Video updated.')) this.notificationsService.success(this.i18n('Success'), this.i18n('Video updated.'))

View File

@ -4,6 +4,7 @@ import { VideoEditModule } from './shared/video-edit.module'
import { VideoUpdateRoutingModule } from './video-update-routing.module' import { VideoUpdateRoutingModule } from './video-update-routing.module'
import { VideoUpdateComponent } from './video-update.component' import { VideoUpdateComponent } from './video-update.component'
import { VideoUpdateResolver } from '@app/videos/+video-edit/video-update.resolver' import { VideoUpdateResolver } from '@app/videos/+video-edit/video-update.resolver'
import { CanDeactivateGuard } from '@app/shared/guards/can-deactivate-guard.service'
@NgModule({ @NgModule({
imports: [ imports: [
@ -21,7 +22,8 @@ import { VideoUpdateResolver } from '@app/videos/+video-edit/video-update.resolv
], ],
providers: [ providers: [
VideoUpdateResolver VideoUpdateResolver,
CanDeactivateGuard
] ]
}) })
export class VideoUpdateModule { } export class VideoUpdateModule { }