## Description
This set the position of the scrollbar at the top of the textarea when opening the __subtitle editor__.
Previously the textarea scroll position was at the bottom of the textarea which doesn't make much sense when you want to edit a subtitle : you most likely want to edit the beginning of the subtitle first.
This also set the caret position on the first character.
## Design decision
I had to use a *component approach* instead of an `<ng-template>` for the edition modal because the `@viewChild` directive doesn't work for elements __inside__ an `<ng-template>`.
I needed the `viewChild` directive to get an `ElementRef` of the `textarea`.
> See the following issue and its workaround :
> - https://github.com/valor-software/ngx-bootstrap/issues/3825
> - https://stackblitz.com/edit/angular-t5dfp7
> - https://medium.com/@izzatnadiri/how-to-pass-data-to-and-receive-from-ng-bootstrap-modals-
916f2ad5d66e
## Related issues
Closes [peertube-plugin-transcription/#39](https://gitlab.com/apps_education/peertube/plugin-transcription/-/issues/39)
--- /dev/null
+<ng-container [formGroup]="form">
+ <div class="modal-header">
+ <h4 i18n class="modal-title">Edit caption</h4>
+ <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon>
+ </div>
+
+ <div class="modal-body">
+ <label i18n for="captionFileContent">Caption</label>
+ <textarea
+ id="captionFileContent"
+ formControlName="captionFileContent"
+ class="form-control caption-textarea"
+ [ngClass]="{ 'input-error': formErrors['captionFileContent'] }"
+ #textarea
+ >
+ </textarea>
+
+ <div *ngIf="formErrors.captionFileContent" class="form-error">
+ {{ formErrors.captionFileContent }}
+ </div>
+ </div>
+
+ <div class="modal-footer inputs">
+ <input
+ type="button" role="button" i18n-value value="Cancel" class="peertube-button grey-button"
+ (click)="cancel()" (key.enter)="cancel()"
+ >
+
+ <input
+ type="submit" i18n-value value="Edit this caption" class="peertube-button orange-button"
+ [disabled]="!form.valid" (click)="updateCaption()"
+ >
+ </div>
+</ng-container>
import { VIDEO_CAPTION_FILE_CONTENT_VALIDATOR } from '@app/shared/form-validators/video-captions-validators'
import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
import { VideoCaptionEdit, VideoCaptionService, VideoCaptionWithPathEdit } from '@app/shared/shared-main'
-import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'
+import { NgbModal, NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { HTMLServerConfig, VideoConstant } from '@shared/models'
import { ServerService } from '../../../../core'
+/**
+ * https://github.com/valor-software/ngx-bootstrap/issues/3825
+ * https://stackblitz.com/edit/angular-t5dfp7
+ * https://medium.com/@izzatnadiri/how-to-pass-data-to-and-receive-from-ng-bootstrap-modals-916f2ad5d66e
+ */
@Component({
- selector: 'my-video-caption-edit-modal',
- styleUrls: [ './video-caption-edit-modal.component.scss' ],
- templateUrl: './video-caption-edit-modal.component.html'
+ selector: 'my-video-caption-edit-modal-content',
+ styleUrls: [ './video-caption-edit-modal-content.component.scss' ],
+ templateUrl: './video-caption-edit-modal-content.component.html'
})
-export class VideoCaptionEditModalComponent extends FormReactive implements OnInit {
+export class VideoCaptionEditModalContentComponent extends FormReactive implements OnInit {
@Input() videoCaption: VideoCaptionWithPathEdit
@Input() serverConfig: HTMLServerConfig
@Output() captionEdited = new EventEmitter<VideoCaptionEdit>()
- @ViewChild('modal', { static: true }) modal: ElementRef
+ @ViewChild('textarea', { static: true }) textarea!: ElementRef
videoCaptionLanguages: VideoConstant<string>[] = []
- private openedModal: NgbModalRef
constructor (
+ protected openedModal: NgbActiveModal,
protected formValidatorService: FormValidatorService,
private modalService: NgbModal,
private videoCaptionService: VideoCaptionService,
this.form.patchValue({
captionFileContent: res
})
+ this.resetTextarea()
})
}
- show () {
- this.openedModal = this.modalService.open(this.modal, { centered: true, keyboard: false })
+ resetTextarea () {
+ this.textarea.nativeElement.scrollTop = 0
+ this.textarea.nativeElement.selectionStart = 0
+ this.textarea.nativeElement.selectionEnd = 0
}
hide () {
+++ /dev/null
-<ng-template #modal>
- <ng-container [formGroup]="form">
-
- <div class="modal-header">
- <h4 i18n class="modal-title">Edit caption</h4>
- <my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon>
- </div>
-
- <div class="modal-body">
- <label i18n for="captionFileContent">Caption</label>
- <textarea
- id="captionFileContent"
- formControlName="captionFileContent"
- class="form-control caption-textarea"
- [ngClass]="{ 'input-error': formErrors['captionFileContent'] }"
- >
- </textarea>
-
- <div *ngIf="formErrors.captionFileContent" class="form-error">
- {{ formErrors.captionFileContent }}
- </div>
- </div>
-
- <div class="modal-footer inputs">
- <input
- type="button" role="button" i18n-value value="Cancel" class="peertube-button grey-button"
- (click)="cancel()" (key.enter)="cancel()"
- >
-
- <input
- type="submit" i18n-value value="Edit this caption" class="peertube-button orange-button"
- [disabled]="!form.valid" (click)="updateCaption()"
- >
- </div>
- </ng-container>
-</ng-template>
<div i18n class="caption-entry-state">Already uploaded on {{ videoCaption.updatedAt | date }} ✔</div>
- <span i18n class="caption-entry-edit" (click)="videoCaptionEditModal.show()">Edit</span>
+ <span i18n class="caption-entry-edit" (click)="openEditCaptionModal(videoCaption)">Edit</span>
<span i18n class="caption-entry-delete" (click)="deleteCaption(videoCaption)">Delete</span>
</ng-container>
<span i18n class="caption-entry-delete" (click)="deleteCaption(videoCaption)">Cancel deletion</span>
</ng-container>
-
- <my-video-caption-edit-modal
- #videoCaptionEditModal
- [videoCaption]="videoCaption"
- [serverConfig]="serverConfig"
- (captionEdited)="onCaptionEdited($event)"
- ></my-video-caption-edit-modal>
</div>
</div>
} from '@shared/models'
import { I18nPrimengCalendarService } from './i18n-primeng-calendar.service'
import { VideoCaptionAddModalComponent } from './video-caption-add-modal.component'
-import { VideoCaptionEditModalComponent } from './video-caption-edit-modal/video-caption-edit-modal.component'
+import { VideoCaptionEditModalContentComponent } from './video-caption-edit-modal-content/video-caption-edit-modal-content.component'
import { VideoEditType } from './video-edit.type'
import { VideoSource } from '@shared/models/videos/video-source'
import { logger } from '@root-helpers/logger'
+import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
type VideoLanguages = VideoConstant<string> & { group?: string }
type PluginField = {
@Input() liveVideo: LiveVideo
@ViewChild('videoCaptionAddModal', { static: true }) videoCaptionAddModal: VideoCaptionAddModalComponent
- @ViewChild('videoCaptionEditModal', { static: true }) editCaptionModal: VideoCaptionEditModalComponent
@Output() formBuilt = new EventEmitter<void>()
@Output() pluginFieldsAdded = new EventEmitter<void>()
private i18nPrimengCalendarService: I18nPrimengCalendarService,
private ngZone: NgZone,
private hooks: HooksService,
- private cd: ChangeDetectorRef
+ private cd: ChangeDetectorRef,
+ private modalService: NgbModal
) {
this.calendarTimezone = this.i18nPrimengCalendarService.getTimezone()
this.calendarDateFormat = this.i18nPrimengCalendarService.getDateFormat()
this.videoCaptionAddModal.show()
}
+ openEditCaptionModal (videoCaption: VideoCaptionWithPathEdit) {
+ const modalRef = this.modalService.open(VideoCaptionEditModalContentComponent, { centered: true, keyboard: false })
+ modalRef.componentInstance.videoCaption = videoCaption
+ modalRef.componentInstance.serverConfig = this.serverConfig
+ modalRef.componentInstance.captionEdited.subscribe(this.onCaptionEdited.bind(this))
+ }
+
isSaveReplayEnabled () {
return this.serverConfig.live.allowReplay
}
import { SharedVideoLiveModule } from '@app/shared/shared-video-live'
import { I18nPrimengCalendarService } from './i18n-primeng-calendar.service'
import { VideoCaptionAddModalComponent } from './video-caption-add-modal.component'
-import { VideoCaptionEditModalComponent } from './video-caption-edit-modal/video-caption-edit-modal.component'
+import { VideoCaptionEditModalContentComponent } from './video-caption-edit-modal-content/video-caption-edit-modal-content.component'
import { VideoEditComponent } from './video-edit.component'
@NgModule({
declarations: [
VideoEditComponent,
VideoCaptionAddModalComponent,
- VideoCaptionEditModalComponent
+ VideoCaptionEditModalContentComponent
],
exports: [