RED-6786, fix editor sluggishness, refactor save method.

This commit is contained in:
George 2023-05-25 19:38:45 +03:00
parent a649a3123f
commit 6744f7fed6
7 changed files with 76 additions and 60 deletions

View File

@ -47,16 +47,14 @@ export class DictionaryScreenComponent implements OnInit {
this._loadingService.start();
try {
await lastValueFrom(
this._dictionaryService.saveEntries(
entries,
this.initialEntries$.value,
this.#dossierTemplateId,
this.entityType,
null,
true,
DICTIONARY_TO_ENTRY_TYPE_MAP[this.type],
),
await this._dictionaryService.saveEntries(
entries,
this.initialEntries$.value,
this.#dossierTemplateId,
this.entityType,
null,
true,
DICTIONARY_TO_ENTRY_TYPE_MAP[this.type],
);
await this._loadEntries();
} catch (e) {

View File

@ -50,15 +50,13 @@ export class EditDossierDictionaryComponent implements EditDossierSectionInterfa
async save(): EditDossierSaveResult {
try {
await firstValueFrom(
this._dictionaryService.saveEntries(
this._dictionaryManager.editor.currentEntries,
this._dictionaryManager.initialEntries,
this.dossier.dossierTemplateId,
this.dossierDictionary.type,
this.dossier.id,
false,
),
await this._dictionaryService.saveEntries(
this._dictionaryManager.editor.currentEntries,
this._dictionaryManager.initialEntries,
this.dossier.dossierTemplateId,
this.dossierDictionary.type,
this.dossier.id,
false,
);
await this.#updateDossierDictionary();

View File

@ -97,6 +97,7 @@
[label]="'dictionary-overview.save-changes' | translate"
[type]="iconButtonTypes.primary"
icon="iqser:check"
[disabled]="!!(_loadingService.isLoading$ | async)"
></iqser-icon-button>
<div (click)="revert()" class="all-caps-label cancel" translate="dictionary-overview.revert-changes"></div>
</div>

View File

@ -57,7 +57,7 @@ export class DictionaryManagerComponent implements OnChanges {
constructor(
private readonly _dictionaryService: DictionaryService,
private readonly _dictionariesMapService: DictionariesMapService,
private readonly _loadingService: LoadingService,
protected readonly _loadingService: LoadingService,
private readonly _changeRef: ChangeDetectorRef,
readonly activeDossiersService: ActiveDossiersService,
readonly dossierTemplatesService: DossierTemplatesService,

View File

@ -1,6 +1,6 @@
<ngx-monaco-editor
(init)="onCodeEditorInit($event)"
(ngModelChange)="codeEditorTextChanged()"
(ngModelChange)="codeEditorTextChanged($event)"
*ngIf="!showDiffEditor"
[(ngModel)]="value"
[options]="editorOptions"
@ -14,3 +14,5 @@
[options]="editorOptions"
[original]="diffEditorText"
></ngx-monaco-diff-editor>
<ng-container *ngIf="_codeEditorTextChangedHandler$ | async"></ng-container>

View File

@ -1,5 +1,5 @@
import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
import { Debounce, List, LoadingService, OnChange } from '@iqser/common-ui';
import { List, LoadingService, OnChange } from '@iqser/common-ui';
import { EditorThemeService } from '@services/editor-theme.service';
import IStandaloneEditorConstructionOptions = monaco.editor.IStandaloneEditorConstructionOptions;
import ICodeEditor = monaco.editor.ICodeEditor;
@ -7,6 +7,8 @@ import IDiffEditor = monaco.editor.IDiffEditor;
import IModelDeltaDecoration = monaco.editor.IModelDeltaDecoration;
import ILineChange = monaco.editor.ILineChange;
import IEditorMouseEvent = monaco.editor.IEditorMouseEvent;
import { BehaviorSubject } from 'rxjs';
import { debounceTime, filter, tap } from 'rxjs/operators';
const MIN_WORD_LENGTH = 2;
const lineChangeToDecoration = ({ originalEndLineNumber, originalStartLineNumber }: ILineChange) =>
@ -41,6 +43,17 @@ export class EditorComponent implements OnInit, OnChanges {
value: string;
private _diffEditor: IDiffEditor;
private _decorations: string[] = [];
private readonly _codeEditorTextChanged$ = new BehaviorSubject<string>('');
protected readonly _codeEditorTextChangedHandler$ = this._codeEditorTextChanged$.asObservable().pipe(
filter(Boolean),
debounceTime(0), // prevent race condition with onPaste event
tap(newText => {
const newDecorations = this.#getDecorations(newText);
this._decorations = this.codeEditor.deltaDecorations(this._decorations, newDecorations);
this.diffValue = this.value;
this._loadingService.stop();
}),
);
constructor(private readonly _loadingService: LoadingService, private readonly _editorThemeService: EditorThemeService) {}
@ -56,12 +69,12 @@ export class EditorComponent implements OnInit, OnChanges {
return this.currentEntries.length;
}
@Debounce()
codeEditorTextChanged() {
const newDecorations = this.#getDecorations();
this._decorations = this.codeEditor.deltaDecorations(this._decorations, newDecorations);
this.diffValue = this.value;
this._loadingService.stop();
onPaste() {
this._loadingService.start();
}
codeEditorTextChanged(event) {
this._codeEditorTextChanged$.next(event);
}
ngOnChanges(changes: SimpleChanges) {
@ -105,12 +118,8 @@ export class EditorComponent implements OnInit, OnChanges {
this._diffEditor?.getModifiedEditor().setValue(this.diffValue);
}
onPaste() {
this._loadingService.start();
}
#getDecorations() {
const currentEntries = this.currentEntries;
#getDecorations(newText: string) {
const currentEntries = newText.split('/n');
const newDecorations: IModelDeltaDecoration[] = [];
for (let index = 0; index < currentEntries.length; ++index) {

View File

@ -86,7 +86,7 @@ export class DictionaryService extends EntitiesService<IDictionary, Dictionary>
);
}
saveEntries(
async saveEntries(
entries: List,
initialEntries: List,
dossierTemplateId: string,
@ -94,42 +94,50 @@ export class DictionaryService extends EntitiesService<IDictionary, Dictionary>
dossierId: string,
showToast = true,
dictionaryEntryType = DictionaryEntryTypes.ENTRY,
): Observable<unknown> {
const entriesToAdd = entries.map(e => e.trim()).filter(e => !!e);
const deletedEntries = initialEntries.filter(e => !entries.includes(e));
console.log({ entriesToAdd, deletedEntries });
): Promise<unknown> {
const entriesToAdd: Array<string> = [];
const deletedEntries: Array<string> = [];
for (let i = 0; i < entries.length; i++) {
if (!entries.at(i).trim()) {
continue;
}
entriesToAdd.push(entries.at(i));
}
for (let i = 0; i < initialEntries.length; i++) {
if (entries.includes(initialEntries.at(i))) {
continue;
}
deletedEntries.push(initialEntries.at(i));
}
// remove empty lines
const invalidRowsExist = entriesToAdd.filter(e => e.length < MIN_WORD_LENGTH);
if (invalidRowsExist.length === 0) {
// can add at least 1 - block UI
const obs: Observable<IDictionary>[] = [];
const promises: Promise<IDictionary>[] = [];
if (deletedEntries.length) {
obs.push(this._deleteEntries(deletedEntries, dossierTemplateId, type, dictionaryEntryType, dossierId));
promises.push(firstValueFrom(this._deleteEntries(deletedEntries, dossierTemplateId, type, dictionaryEntryType, dossierId)));
}
if (entriesToAdd.filter(e => !initialEntries.includes(e)).length) {
obs.push(this._addEntries(entriesToAdd, dossierTemplateId, type, dictionaryEntryType, dossierId));
promises.push(firstValueFrom(this._addEntries(entriesToAdd, dossierTemplateId, type, dictionaryEntryType, dossierId)));
}
try {
await Promise.all(promises);
if (showToast) {
this._toaster.success(_('dictionary-overview.success.generic'));
}
return;
} catch (error) {
if ((error as HttpErrorResponse).status === 400) {
this._toaster.error(_('dictionary-overview.error.400'));
} else {
this._toaster.error(_('dictionary-overview.error.generic'));
}
return;
}
return zip(obs).pipe(
switchMap(dictionary => this._dossierTemplateStatsService.getFor([dossierTemplateId]).pipe(map(() => dictionary))),
tap({
next: () => {
if (showToast) {
this._toaster.success(_('dictionary-overview.success.generic'));
}
},
error: (error: unknown) => {
if ((error as HttpErrorResponse).status === 400) {
this._toaster.error(_('dictionary-overview.error.400'));
} else {
this._toaster.error(_('dictionary-overview.error.generic'));
}
},
}),
);
}
this._toaster.error(_('dictionary-overview.error.entries-too-short'));
return throwError(() => 'Entries too short');
throw new Error('Entries too short');
}
hasManualType(dossierTemplateId: string): boolean {