Skip to content

Commit

Permalink
feat(chrome-extension): improve performance on localization panel
Browse files Browse the repository at this point in the history
Limit the number of key translation displayed and only create inputs for displayed items

Encourage the user to use the search bar to find their translation
  • Loading branch information
cpaulve-1A committed Jul 18, 2024
1 parent 3130346 commit 3218047
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 51 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
import { AsyncPipe } from '@angular/common';
import { ChangeDetectionStrategy, Component, computed, effect, inject, type OnDestroy, type Signal, untracked, viewChild, ViewEncapsulation } from '@angular/core';
import {
ChangeDetectionStrategy,
Component,
computed,
effect,
inject,
type OnDestroy,
type Signal,
untracked,
viewChild,
ViewEncapsulation
} from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { DfTooltipModule } from '@design-factory/design-factory';
Expand All @@ -10,8 +21,7 @@ import type {
} from '@o3r/localization';
import { Subscription } from 'rxjs';
import { map, throttleTime } from 'rxjs/operators';
import { ChromeExtensionConnectionService } from '../../services/connection.service';
import { LocalizationService, StateService } from '../../services';
import { ChromeExtensionConnectionService, LocalizationService, StateService } from '../../services';

const THROTTLE_TIME = 100;

Expand All @@ -36,6 +46,8 @@ export class LocalizationPanelPresComponent implements OnDestroy {
private readonly connectionService = inject(ChromeExtensionConnectionService);
private readonly localizationService = inject(LocalizationService);
private readonly stateService = inject(StateService);
private readonly subscription = new Subscription();
private readonly maxItemDisplayed = 20;

public readonly isTranslationDeactivationEnabled = this.localizationService.isTranslationDeactivationEnabled;
public readonly localizations: Signal<LocalizationMetadata>;
Expand All @@ -44,6 +56,7 @@ export class LocalizationPanelPresComponent implements OnDestroy {
public readonly filteredLocalizations: Signal<LocalizationMetadata>;
public readonly languages = this.localizationService.languages;
public readonly hasSeveralLanguages: Signal<boolean>;
public readonly isTruncated: Signal<boolean>;
public readonly localizationActiveStateOverridesForCurrentLang = computed(() => {
const lang = this.currentLanguage();
if (!lang) {
Expand All @@ -66,22 +79,9 @@ export class LocalizationPanelPresComponent implements OnDestroy {
translations: new FormGroup<Record<string, LangTranslationsControl>>({})
});

private readonly subscription = new Subscription();

public accordion = viewChild<NgbAccordionDirective>('acc');

constructor() {
this.connectionService.sendMessage(
'requestMessages',
{
only: [
'localizations',
'languages',
'switchLanguage',
'isTranslationDeactivationEnabled'
]
}
);
this.hasSeveralLanguages = computed(() => this.languages().length >= 2);
this.localizations = computed(() => this.localizationService.localizationsMetadata().filter((localization) => !localization.dictionary && !localization.ref));
this.hasLocalizations = computed(() => !!this.localizations().length);
Expand All @@ -93,14 +93,20 @@ export class LocalizationPanelPresComponent implements OnDestroy {
),
{ initialValue: '' }
);
const searchMatch = computed(() => {
const searchText = search();
return searchText ?
this.localizations().filter(({ key, description, tags, ref }) =>
[key, description, ...(tags || []), ref].some((value) => value?.toLowerCase().includes(searchText))
) : this.localizations();
});

this.filteredLocalizations = computed(() => {
const searchText = search();
return searchText
? this.localizations().filter(({ key, description, tags, ref }) => [key, description, ...(tags || []), ref].some((value) => value?.toLowerCase().includes(searchText)))
: this.localizations();
return searchMatch().slice(0, this.maxItemDisplayed);
});

this.isTruncated = computed(() => this.filteredLocalizations().length < searchMatch().length);

effect(() => {
const lang = this.currentLanguage();
if (lang) {
Expand All @@ -120,34 +126,13 @@ export class LocalizationPanelPresComponent implements OnDestroy {
})
);
effect(() => {
const translations = this.localizationService.translationsForCurrentLanguage();
const translations = this.filteredLocalizations();
const lang = untracked(this.currentLanguage);
if (!lang) {
return;
}
let langControl = this.form.controls.translations.controls[lang];
if (!langControl) {
langControl = new FormGroup<Record<string, TranslationControl>>({});
this.form.controls.translations.addControl(lang, langControl);
}
Object.entries(translations).forEach(([key, value]) => {
const control = langControl.controls[key];
const initialValue =
untracked(this.stateService.localState).localizations?.[this.form.value.lang || '']?.[key]
|| value
|| untracked(this.localizationService.localizationsMetadata).find((loc) => loc.key === key)?.value
|| '';
if (!control) {
const newControl = new FormControl<string>(initialValue);
langControl.addControl(key, newControl);
this.subscription.add(
newControl.valueChanges.pipe(
throttleTime(THROTTLE_TIME, undefined, { trailing: true })
).subscribe((newValue) => this.onLocalizationChange(key, newValue ?? ''))
);
} else {
control.setValue(initialValue);
}
translations.forEach(({key}) => {
this.upsertKeyForm(key, lang);
});
});
effect(() => {
Expand All @@ -166,14 +151,32 @@ export class LocalizationPanelPresComponent implements OnDestroy {
control.disable();
}
});
const locOverride = computed(() => this.stateService.localState().localizations || {});
effect(() => {
Object.entries(locOverride()).forEach(([lang, overridePerLang]) =>
Object.entries(overridePerLang).forEach(([key, value]) =>
this.form.controls.translations.controls[lang]?.controls[key]?.setValue(value)
)
}

private upsertKeyForm(key: string, lang: string) {
let langControl = this.form.controls.translations.controls[lang];
if (!langControl) {
langControl = new FormGroup<Record<string, TranslationControl>>({});
this.form.controls.translations.addControl(lang, langControl);
}
const control = langControl.controls[key];
const controlValue =
this.stateService.localState().localizations?.[this.form.value.lang || '']?.[key]
|| untracked(this.localizationService.localizationsMetadata).find((loc) => loc.key === key)?.value
|| '';
if (!control) {
const newControl = new FormControl<string>(controlValue);
langControl.addControl(key, newControl);
this.subscription.add(
newControl.valueChanges.pipe(
throttleTime(THROTTLE_TIME, undefined, { trailing: true })
).subscribe((newValue) => {
this.onLocalizationChange(key, newValue ?? '');
})
);
});
} else if (control.value !== controlValue) {
control.setValue(controlValue);
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ <h3 ngbAccordionHeader>
} @empty {
<h3>No localization found for your search.</h3>
}
@if (isTruncated()) {
<span>Too many matches for this filter, please be more specific in your search.</span>
}
</div>
</ng-container>
} @else {
Expand Down

0 comments on commit 3218047

Please sign in to comment.