mirror of
https://github.com/bitwarden/browser
synced 2025-12-15 07:43:35 +00:00
[PM-6991] Improve reactivity of cipherViews$ observable in cipher service (#11141)
* [PM-6991] Remove self reference to cipherViews$ * [PM-6991] Update cipherViews$ observable to be reactive to encrypted ciphers and localData * [PM-6991] Use the cipherViews$ observable in the Web vault * [PM-6991] Update the vault filter service to use cipherViews$ observable * [PM-6991] Add deprecation notice to getAllDecrypted * [PM-6991] Ensure cipherViews$ emits an empty array whenever the decrypted cipher cache is cleared * [PM-6991] Fix cipher service test * [PM-6991] Use shareReplay instead of share * [PM-6991] Remove ciphersExpectingUpdate and replace with a null filter instead for cipherViews$ * [PM-6991] Skip refreshing on null cipherViews$ to avoid flashing an empty vault after modifying ciphers
This commit is contained in:
@@ -35,6 +35,7 @@ describe("vault filter service", () => {
|
||||
let organizations: ReplaySubject<Organization[]>;
|
||||
let folderViews: ReplaySubject<FolderView[]>;
|
||||
let collectionViews: ReplaySubject<CollectionView[]>;
|
||||
let cipherViews: ReplaySubject<CipherView[]>;
|
||||
let personalOwnershipPolicy: ReplaySubject<boolean>;
|
||||
let singleOrgPolicy: ReplaySubject<boolean>;
|
||||
let stateProvider: FakeStateProvider;
|
||||
@@ -57,6 +58,7 @@ describe("vault filter service", () => {
|
||||
organizations = new ReplaySubject<Organization[]>(1);
|
||||
folderViews = new ReplaySubject<FolderView[]>(1);
|
||||
collectionViews = new ReplaySubject<CollectionView[]>(1);
|
||||
cipherViews = new ReplaySubject<CipherView[]>(1);
|
||||
personalOwnershipPolicy = new ReplaySubject<boolean>(1);
|
||||
singleOrgPolicy = new ReplaySubject<boolean>(1);
|
||||
|
||||
@@ -69,6 +71,7 @@ describe("vault filter service", () => {
|
||||
policyService.policyAppliesToActiveUser$
|
||||
.calledWith(PolicyType.SingleOrg)
|
||||
.mockReturnValue(singleOrgPolicy);
|
||||
cipherService.cipherViews$ = cipherViews;
|
||||
|
||||
vaultFilterService = new VaultFilterService(
|
||||
organizationService,
|
||||
@@ -162,7 +165,7 @@ describe("vault filter service", () => {
|
||||
createCipherView("1", "org test id", "folder test id"),
|
||||
createCipherView("2", "non matching org id", "non matching folder id"),
|
||||
];
|
||||
cipherService.getAllDecrypted.mockResolvedValue(storedCiphers);
|
||||
cipherViews.next(storedCiphers);
|
||||
|
||||
const storedFolders = [
|
||||
createFolderView("folder test id", "test"),
|
||||
@@ -191,6 +194,7 @@ describe("vault filter service", () => {
|
||||
createFolderView("Folder 3 Id", "Folder 1/Folder 3"),
|
||||
];
|
||||
folderViews.next(storedFolders);
|
||||
cipherViews.next([]);
|
||||
|
||||
const result = await firstValueFrom(vaultFilterService.folderTree$);
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.servi
|
||||
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||
import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
|
||||
import { ServiceUtils } from "@bitwarden/common/vault/service-utils";
|
||||
import { COLLAPSED_GROUPINGS } from "@bitwarden/common/vault/services/key-state/collapsed-groupings.state";
|
||||
@@ -55,9 +56,9 @@ export class VaultFilterService implements VaultFilterServiceAbstraction {
|
||||
protected _organizationFilter = new BehaviorSubject<Organization>(null);
|
||||
|
||||
filteredFolders$: Observable<FolderView[]> = this.folderService.folderViews$.pipe(
|
||||
combineLatestWith(this._organizationFilter),
|
||||
switchMap(([folders, org]) => {
|
||||
return this.filterFolders(folders, org);
|
||||
combineLatestWith(this.cipherService.cipherViews$, this._organizationFilter),
|
||||
switchMap(([folders, ciphers, org]) => {
|
||||
return this.filterFolders(folders, ciphers, org);
|
||||
}),
|
||||
);
|
||||
folderTree$: Observable<TreeNode<FolderFilter>> = this.filteredFolders$.pipe(
|
||||
@@ -231,6 +232,7 @@ export class VaultFilterService implements VaultFilterServiceAbstraction {
|
||||
|
||||
protected async filterFolders(
|
||||
storedFolders: FolderView[],
|
||||
ciphers: CipherView[],
|
||||
org?: Organization,
|
||||
): Promise<FolderView[]> {
|
||||
// If no org or "My Vault" is selected, show all folders
|
||||
@@ -239,7 +241,6 @@ export class VaultFilterService implements VaultFilterServiceAbstraction {
|
||||
}
|
||||
|
||||
// Otherwise, show only folders that have ciphers from the selected org and the "no folder" folder
|
||||
const ciphers = await this.cipherService.getAllDecrypted();
|
||||
const orgCiphers = ciphers.filter((c) => c.organizationId == org?.id);
|
||||
return storedFolders.filter(
|
||||
(f) => orgCiphers.some((oc) => oc.folderId == f.id) || f.id == null,
|
||||
|
||||
@@ -281,7 +281,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
this.currentSearchText$ = this.route.queryParams.pipe(map((queryParams) => queryParams.search));
|
||||
|
||||
const ciphers$ = combineLatest([
|
||||
Utils.asyncToObservable(() => this.cipherService.getAllDecrypted()),
|
||||
this.cipherService.cipherViews$.pipe(filter((c) => c !== null)),
|
||||
filter$,
|
||||
this.currentSearchText$,
|
||||
]).pipe(
|
||||
|
||||
Reference in New Issue
Block a user