mirror of
https://github.com/bitwarden/browser
synced 2026-02-20 11:24:07 +00:00
[PM-26704] Vault List Item Ordering for Extension (#18853)
* shows all/filtered ciphers in allItems instead of the ones that haven't been bubbled up into autofill or favorites * removes remainingCiphers$ remnants * updates loading$ observable logic * updates loading$ test
This commit is contained in:
@@ -127,7 +127,7 @@
|
||||
<!--Change the title header when a filter is applied-->
|
||||
<app-vault-list-items-container
|
||||
[title]="((numberOfAppliedFilters$ | async) === 0 ? 'allItems' : 'items') | i18n"
|
||||
[ciphers]="(remainingCiphers$ | async) || []"
|
||||
[ciphers]="(filteredCiphers$ | async) || []"
|
||||
id="allItems"
|
||||
disableSectionMargin
|
||||
collapsibleKey="allItems"
|
||||
|
||||
@@ -164,7 +164,6 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
|
||||
protected filteredCiphers$ = this.vaultPopupItemsService.filteredCiphers$;
|
||||
protected favoriteCiphers$ = this.vaultPopupItemsService.favoriteCiphers$;
|
||||
protected remainingCiphers$ = this.vaultPopupItemsService.remainingCiphers$;
|
||||
protected allFilters$ = this.vaultPopupListFiltersService.allFilters$;
|
||||
protected cipherCount$ = this.vaultPopupItemsService.cipherCount$;
|
||||
protected hasPremium$ = this.activeUserId$.pipe(
|
||||
|
||||
@@ -370,37 +370,6 @@ describe("VaultPopupItemsService", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("remainingCiphers$", () => {
|
||||
beforeEach(() => {
|
||||
searchService.isSearchable.mockImplementation(async (text) => text.length > 2);
|
||||
});
|
||||
|
||||
it("should exclude autofill and favorite ciphers", (done) => {
|
||||
service.remainingCiphers$.subscribe((ciphers) => {
|
||||
// 2 autofill ciphers, 2 favorite ciphers = 6 remaining ciphers to show
|
||||
expect(ciphers.length).toBe(6);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should filter remainingCiphers$ down to search term", (done) => {
|
||||
const cipherList = Object.values(allCiphers);
|
||||
const searchText = "Login";
|
||||
|
||||
searchService.searchCiphers.mockImplementation(async () => {
|
||||
return cipherList.filter((cipher) => {
|
||||
return cipher.name.includes(searchText);
|
||||
});
|
||||
});
|
||||
|
||||
service.remainingCiphers$.subscribe((ciphers) => {
|
||||
// There are 6 remaining ciphers but only 2 with "Login" in the name
|
||||
expect(ciphers.length).toBe(2);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("emptyVault$", () => {
|
||||
it("should return true if there are no ciphers", (done) => {
|
||||
cipherServiceMock.cipherListViews$.mockReturnValue(of([]));
|
||||
@@ -493,8 +462,8 @@ describe("VaultPopupItemsService", () => {
|
||||
// Start tracking loading$ emissions
|
||||
tracked = new ObservableTracker(service.loading$);
|
||||
|
||||
// Track remainingCiphers$ to make cipher observables active
|
||||
trackedCiphers = new ObservableTracker(service.remainingCiphers$);
|
||||
// Track favoriteCiphers$ to make cipher observables active
|
||||
trackedCiphers = new ObservableTracker(service.favoriteCiphers$);
|
||||
});
|
||||
|
||||
it("should initialize with true first", async () => {
|
||||
|
||||
@@ -2,7 +2,6 @@ import { inject, Injectable, NgZone } from "@angular/core";
|
||||
import { toObservable } from "@angular/core/rxjs-interop";
|
||||
import {
|
||||
combineLatest,
|
||||
concatMap,
|
||||
distinctUntilChanged,
|
||||
distinctUntilKeyChanged,
|
||||
filter,
|
||||
@@ -242,31 +241,12 @@ export class VaultPopupItemsService {
|
||||
shareReplay({ refCount: false, bufferSize: 1 }),
|
||||
);
|
||||
|
||||
/**
|
||||
* List of all remaining ciphers that are not currently suggested for autofill or marked as favorite.
|
||||
* Ciphers are sorted by name.
|
||||
*/
|
||||
remainingCiphers$: Observable<PopupCipherViewLike[]> = this.favoriteCiphers$.pipe(
|
||||
concatMap(
|
||||
(
|
||||
favoriteCiphers, // concatMap->of is used to make withLatestFrom lazy to avoid race conditions with autoFillCiphers$
|
||||
) =>
|
||||
of(favoriteCiphers).pipe(withLatestFrom(this._filteredCipherList$, this.autoFillCiphers$)),
|
||||
),
|
||||
map(([favoriteCiphers, ciphers, autoFillCiphers]) =>
|
||||
ciphers.filter(
|
||||
(cipher) => !autoFillCiphers.includes(cipher) && !favoriteCiphers.includes(cipher),
|
||||
),
|
||||
),
|
||||
shareReplay({ refCount: false, bufferSize: 1 }),
|
||||
);
|
||||
|
||||
/**
|
||||
* Observable that indicates whether the service is currently loading ciphers.
|
||||
*/
|
||||
loading$: Observable<boolean> = merge(
|
||||
this._ciphersLoading$.pipe(map(() => true)),
|
||||
this.remainingCiphers$.pipe(map(() => false)),
|
||||
this.favoriteCiphers$.pipe(map(() => false)),
|
||||
).pipe(startWith(true), distinctUntilChanged(), shareReplay({ refCount: false, bufferSize: 1 }));
|
||||
|
||||
/** Observable that indicates whether there is search text present.
|
||||
|
||||
Reference in New Issue
Block a user