1
0
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:
Shane Melton
2024-10-10 14:54:23 -07:00
committed by GitHub
parent c221efd09a
commit 9d163550fd
6 changed files with 65 additions and 41 deletions

View File

@@ -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$);

View File

@@ -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,

View File

@@ -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(