mirror of
https://github.com/bitwarden/browser
synced 2026-01-29 15:53:45 +00:00
[PM-30289] Vault Popup Item Service optimizations
- Add distinctUntilChanged to _otherAutofillTypes$ to prevent duplicate emissions - debounceTime(0) to _allDecryptedCiphers to avoid back-to-back emissions from ciphers$() and localData$() - shareReplay() to _allDecryptedCiphers to avoid multiple unique subscriptions - Add _effectiveSearchText$ to avoid flashing vault list when search query is nonSearchable - Add distinctUntilChanged to filters$ observable to avoid duplicate emissions during page load
This commit is contained in:
@@ -3,6 +3,7 @@ import { toObservable } from "@angular/core/rxjs-interop";
|
||||
import {
|
||||
combineLatest,
|
||||
concatMap,
|
||||
debounceTime,
|
||||
distinctUntilChanged,
|
||||
distinctUntilKeyChanged,
|
||||
filter,
|
||||
@@ -99,6 +100,7 @@ export class VaultPopupItemsService {
|
||||
...(showIdentities ? [CipherType.Identity] : []),
|
||||
];
|
||||
}),
|
||||
distinctUntilChanged((a, b) => a.length === b.length && a.every((v, i) => v === b[i])),
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -111,6 +113,7 @@ export class VaultPopupItemsService {
|
||||
filter((userId): userId is UserId => userId != null),
|
||||
switchMap((userId) =>
|
||||
merge(this.cipherService.ciphers$(userId), this.cipherService.localData$(userId)).pipe(
|
||||
debounceTime(0),
|
||||
runInsideAngular(this.ngZone),
|
||||
tap(() => this._ciphersLoading$.next()),
|
||||
waitUntilSync(this.syncService),
|
||||
@@ -164,24 +167,35 @@ export class VaultPopupItemsService {
|
||||
}),
|
||||
),
|
||||
),
|
||||
shareReplay({ refCount: true, bufferSize: 1 }),
|
||||
);
|
||||
|
||||
/**
|
||||
* Observable that emits the search text when it's searchable, or an empty string when it's not.
|
||||
* This prevents unnecessary re-renders when typing non-searchable text (e.g., single characters).
|
||||
* @private
|
||||
*/
|
||||
private _effectiveSearchText$ = combineLatest([
|
||||
this.searchText$,
|
||||
getUserId(this.accountService.activeAccount$),
|
||||
]).pipe(
|
||||
switchMap(async ([searchText, userId]) => {
|
||||
const isSearchable = await this.searchService.isSearchable(userId, searchText);
|
||||
return isSearchable ? searchText : "";
|
||||
}),
|
||||
distinctUntilChanged(),
|
||||
shareReplay({ refCount: true, bufferSize: 1 }),
|
||||
);
|
||||
|
||||
/**
|
||||
* Observable that indicates whether there is search text present that is searchable.
|
||||
* @private
|
||||
*/
|
||||
private _hasSearchText = combineLatest([
|
||||
this.searchText$,
|
||||
getUserId(this.accountService.activeAccount$),
|
||||
]).pipe(
|
||||
switchMap(([searchText, userId]) => {
|
||||
return this.searchService.isSearchable(userId, searchText);
|
||||
}),
|
||||
);
|
||||
private _hasSearchText = this._effectiveSearchText$.pipe(map((text) => text !== ""));
|
||||
|
||||
private _filteredCipherList$: Observable<PopupCipherViewLike[]> = combineLatest([
|
||||
this._activeCipherList$,
|
||||
this.searchText$,
|
||||
this._effectiveSearchText$,
|
||||
this.vaultPopupListFiltersService.filterFunction$,
|
||||
getUserId(this.accountService.activeAccount$),
|
||||
]).pipe(
|
||||
|
||||
@@ -100,6 +100,9 @@ export class VaultPopupListFiltersService {
|
||||
*/
|
||||
filters$ = this.filterForm.valueChanges.pipe(
|
||||
startWith(this.filterForm.value),
|
||||
distinctUntilChanged(
|
||||
(previous, current) => JSON.stringify(previous) === JSON.stringify(current),
|
||||
),
|
||||
shareReplay({ bufferSize: 1, refCount: true }),
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user