From 321804727cc45a7bafa752abbb2c0f229f30d0b9 Mon Sep 17 00:00:00 2001 From: Corinne PAULVE Date: Tue, 16 Jul 2024 15:57:50 +0200 Subject: [PATCH] feat(chrome-extension): improve performance on localization panel 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 --- .../localization-panel-pres.component.ts | 105 +++++++++--------- .../localization-panel-pres.template.html | 3 + 2 files changed, 57 insertions(+), 51 deletions(-) diff --git a/apps/chrome-devtools/src/app-devtools/localization-panel/localization-panel-pres.component.ts b/apps/chrome-devtools/src/app-devtools/localization-panel/localization-panel-pres.component.ts index 9a2f07029f..0f8ab3abe0 100644 --- a/apps/chrome-devtools/src/app-devtools/localization-panel/localization-panel-pres.component.ts +++ b/apps/chrome-devtools/src/app-devtools/localization-panel/localization-panel-pres.component.ts @@ -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'; @@ -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; @@ -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; @@ -44,6 +56,7 @@ export class LocalizationPanelPresComponent implements OnDestroy { public readonly filteredLocalizations: Signal; public readonly languages = this.localizationService.languages; public readonly hasSeveralLanguages: Signal; + public readonly isTruncated: Signal; public readonly localizationActiveStateOverridesForCurrentLang = computed(() => { const lang = this.currentLanguage(); if (!lang) { @@ -66,22 +79,9 @@ export class LocalizationPanelPresComponent implements OnDestroy { translations: new FormGroup>({}) }); - private readonly subscription = new Subscription(); - public accordion = viewChild('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); @@ -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) { @@ -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>({}); - 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(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(() => { @@ -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>({}); + 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(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); + } } /** diff --git a/apps/chrome-devtools/src/app-devtools/localization-panel/localization-panel-pres.template.html b/apps/chrome-devtools/src/app-devtools/localization-panel/localization-panel-pres.template.html index 443befde03..9d8c5930a4 100644 --- a/apps/chrome-devtools/src/app-devtools/localization-panel/localization-panel-pres.template.html +++ b/apps/chrome-devtools/src/app-devtools/localization-panel/localization-panel-pres.template.html @@ -78,6 +78,9 @@

} @empty {

No localization found for your search.

} + @if (isTruncated()) { + Too many matches for this filter, please be more specific in your search. + } } @else {