1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-12 14:23:32 +00:00

[PS-1078] Refactor FolderService to use Observables (#3022)

This commit is contained in:
Oscar Hinton
2022-07-12 20:25:18 +02:00
committed by GitHub
parent a43aa9612c
commit 23253b3882
32 changed files with 421 additions and 180 deletions

View File

@@ -1,4 +1,5 @@
import { Directive, EventEmitter, Input, OnInit, Output } from "@angular/core";
import { Observable } from "rxjs";
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
import { CipherService } from "@bitwarden/common/abstractions/cipher.service";
@@ -51,7 +52,7 @@ export class AddEditComponent implements OnInit {
editMode = false;
cipher: CipherView;
folders: FolderView[];
folders$: Observable<FolderView[]>;
collections: CollectionView[] = [];
title: string;
formPromise: Promise<any>;
@@ -243,7 +244,7 @@ export class AddEditComponent implements OnInit {
}
}
this.folders = await this.folderService.getAllDecrypted();
this.folders$ = this.folderService.folderViews$;
if (this.editMode && this.previousCipherId !== this.cipherId) {
this.eventService.collect(EventType.Cipher_ClientViewed, this.cipherId);

View File

@@ -1,4 +1,5 @@
import { Directive, EventEmitter, Input, OnInit, Output } from "@angular/core";
import { firstValueFrom, Observable } from "rxjs";
import { Organization } from "@bitwarden/common/models/domain/organization";
import { ITreeNodeObject } from "@bitwarden/common/models/domain/treeNode";
@@ -28,7 +29,7 @@ export class VaultFilterComponent implements OnInit {
activePersonalOwnershipPolicy: boolean;
activeSingleOrganizationPolicy: boolean;
collections: DynamicTreeNode<CollectionView>;
folders: DynamicTreeNode<FolderView>;
folders$: Observable<DynamicTreeNode<FolderView>>;
constructor(protected vaultFilterService: VaultFilterService) {}
@@ -45,7 +46,7 @@ export class VaultFilterComponent implements OnInit {
this.activeSingleOrganizationPolicy =
await this.vaultFilterService.checkForSingleOrganizationPolicy();
}
this.folders = await this.vaultFilterService.buildFolders();
this.folders$ = await this.vaultFilterService.buildNestedFolders();
this.collections = await this.initCollections();
this.isLoaded = true;
}
@@ -67,13 +68,13 @@ export class VaultFilterComponent implements OnInit {
async applyFilter(filter: VaultFilter) {
if (filter.refreshCollectionsAndFolders) {
await this.reloadCollectionsAndFolders(filter);
filter = this.pruneInvalidatedFilterSelections(filter);
filter = await this.pruneInvalidatedFilterSelections(filter);
}
this.onFilterChange.emit(filter);
}
async reloadCollectionsAndFolders(filter: VaultFilter) {
this.folders = await this.vaultFilterService.buildFolders(filter.selectedOrganizationId);
this.folders$ = await this.vaultFilterService.buildNestedFolders(filter.selectedOrganizationId);
this.collections = filter.myVaultOnly
? null
: await this.vaultFilterService.buildCollections(filter.selectedOrganizationId);
@@ -95,14 +96,17 @@ export class VaultFilterComponent implements OnInit {
this.onEditFolder.emit(folder);
}
protected pruneInvalidatedFilterSelections(filter: VaultFilter): VaultFilter {
filter = this.pruneInvalidFolderSelection(filter);
protected async pruneInvalidatedFilterSelections(filter: VaultFilter): Promise<VaultFilter> {
filter = await this.pruneInvalidFolderSelection(filter);
filter = this.pruneInvalidCollectionSelection(filter);
return filter;
}
protected pruneInvalidFolderSelection(filter: VaultFilter): VaultFilter {
if (filter.selectedFolder && !this.folders?.hasId(filter.selectedFolderId)) {
protected async pruneInvalidFolderSelection(filter: VaultFilter): Promise<VaultFilter> {
if (
filter.selectedFolder &&
!(await firstValueFrom(this.folders$))?.hasId(filter.selectedFolderId)
) {
filter.selectedFolder = false;
filter.selectedFolderId = null;
}

View File

@@ -1,4 +1,5 @@
import { Injectable } from "@angular/core";
import { from, mergeMap, Observable } from "rxjs";
import { CipherService } from "@bitwarden/common/abstractions/cipher.service";
import { CollectionService } from "@bitwarden/common/abstractions/collection.service";
@@ -7,12 +8,16 @@ import { OrganizationService } from "@bitwarden/common/abstractions/organization
import { PolicyService } from "@bitwarden/common/abstractions/policy.service";
import { StateService } from "@bitwarden/common/abstractions/state.service";
import { PolicyType } from "@bitwarden/common/enums/policyType";
import { ServiceUtils } from "@bitwarden/common/misc/serviceUtils";
import { Organization } from "@bitwarden/common/models/domain/organization";
import { TreeNode } from "@bitwarden/common/models/domain/treeNode";
import { CollectionView } from "@bitwarden/common/models/view/collectionView";
import { FolderView } from "@bitwarden/common/models/view/folderView";
import { DynamicTreeNode } from "./models/dynamic-tree-node.model";
const NestingDelimiter = "/";
@Injectable()
export class VaultFilterService {
constructor(
@@ -36,25 +41,30 @@ export class VaultFilterService {
return await this.organizationService.getAll();
}
async buildFolders(organizationId?: string): Promise<DynamicTreeNode<FolderView>> {
const storedFolders = await this.folderService.getAllDecrypted();
let folders: FolderView[];
if (organizationId != null) {
const ciphers = await this.cipherService.getAllDecrypted();
const orgCiphers = ciphers.filter((c) => c.organizationId == organizationId);
folders = storedFolders.filter(
(f) =>
orgCiphers.filter((oc) => oc.folderId == f.id).length > 0 ||
ciphers.filter((c) => c.folderId == f.id).length < 1
);
} else {
folders = storedFolders;
}
const nestedFolders = await this.folderService.getAllNested(folders);
return new DynamicTreeNode<FolderView>({
fullList: folders,
nestedList: nestedFolders,
});
buildNestedFolders(organizationId?: string): Observable<DynamicTreeNode<FolderView>> {
const transformation = async (storedFolders: FolderView[]) => {
let folders: FolderView[];
if (organizationId != null) {
const ciphers = await this.cipherService.getAllDecrypted();
const orgCiphers = ciphers.filter((c) => c.organizationId == organizationId);
folders = storedFolders.filter(
(f) =>
orgCiphers.filter((oc) => oc.folderId == f.id).length > 0 ||
ciphers.filter((c) => c.folderId == f.id).length < 1
);
} else {
folders = storedFolders;
}
const nestedFolders = await this.getAllFoldersNested(folders);
return new DynamicTreeNode<FolderView>({
fullList: folders,
nestedList: nestedFolders,
});
};
return this.folderService.folderViews$.pipe(
mergeMap((folders) => from(transformation(folders)))
);
}
async buildCollections(organizationId?: string): Promise<DynamicTreeNode<CollectionView>> {
@@ -79,4 +89,21 @@ export class VaultFilterService {
async checkForPersonalOwnershipPolicy(): Promise<boolean> {
return await this.policyService.policyAppliesToUser(PolicyType.PersonalOwnership);
}
protected async getAllFoldersNested(folders?: FolderView[]): Promise<TreeNode<FolderView>[]> {
const nodes: TreeNode<FolderView>[] = [];
folders.forEach((f) => {
const folderCopy = new FolderView();
folderCopy.id = f.id;
folderCopy.revisionDate = f.revisionDate;
const parts = f.name != null ? f.name.replace(/^\/+|\/+$/g, "").split(NestingDelimiter) : [];
ServiceUtils.nestedTraverse(nodes, 0, parts, folderCopy, null, NestingDelimiter);
});
return nodes;
}
async getFolderNested(id: string): Promise<TreeNode<FolderView>> {
const folders = await this.getAllFoldersNested();
return ServiceUtils.getTreeNodeObject(folders, id) as TreeNode<FolderView>;
}
}

View File

@@ -223,6 +223,7 @@ export const LOG_MAC_FAILURES = new InjectionToken<string>("LOG_MAC_FAILURES");
I18nServiceAbstraction,
CipherServiceAbstraction,
StateServiceAbstraction,
BroadcasterServiceAbstraction,
],
},
{