From e8e8f7b806cf7e293a9e60273b7b110e0f4d7539 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Fri, 3 Feb 2023 15:18:10 +0100 Subject: [PATCH 01/30] [EC-775] feat: add compatibility layer from #4154 --- .../app/core/vault-filter/filter-ciphers.ts | 5 ++ .../vault-filter/routed-vault-filter.model.ts | 5 ++ .../router-vault-filter.service.ts | 47 ++++++++++ .../core/vault-filter/vault-filter.module.ts | 10 +++ .../routed-vault-filter-bridge.service.ts | 76 ++++++++++++++++ .../routed-vault-filter-bridge.model.ts | 89 +++++++++++++++++++ .../vault/app/vault/vault-items.component.ts | 2 +- .../src/vault/app/vault/vault.component.ts | 17 +++- 8 files changed, 249 insertions(+), 2 deletions(-) create mode 100644 apps/web/src/vault/app/core/vault-filter/filter-ciphers.ts create mode 100644 apps/web/src/vault/app/core/vault-filter/routed-vault-filter.model.ts create mode 100644 apps/web/src/vault/app/core/vault-filter/router-vault-filter.service.ts create mode 100644 apps/web/src/vault/app/core/vault-filter/vault-filter.module.ts create mode 100644 apps/web/src/vault/app/vault/vault-filter/services/routed-vault-filter-bridge.service.ts create mode 100644 apps/web/src/vault/app/vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts diff --git a/apps/web/src/vault/app/core/vault-filter/filter-ciphers.ts b/apps/web/src/vault/app/core/vault-filter/filter-ciphers.ts new file mode 100644 index 000000000000..058fff92ef50 --- /dev/null +++ b/apps/web/src/vault/app/core/vault-filter/filter-ciphers.ts @@ -0,0 +1,5 @@ +import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; + +export function filterCiphers(ciphers: CipherView[]): boolean { + return true; +} diff --git a/apps/web/src/vault/app/core/vault-filter/routed-vault-filter.model.ts b/apps/web/src/vault/app/core/vault-filter/routed-vault-filter.model.ts new file mode 100644 index 000000000000..41531f1125b2 --- /dev/null +++ b/apps/web/src/vault/app/core/vault-filter/routed-vault-filter.model.ts @@ -0,0 +1,5 @@ +export interface RoutedVaultFilterModel { + collectionId?: string; + folderId?: string; + organizationId?: string; +} diff --git a/apps/web/src/vault/app/core/vault-filter/router-vault-filter.service.ts b/apps/web/src/vault/app/core/vault-filter/router-vault-filter.service.ts new file mode 100644 index 000000000000..a5293f41a031 --- /dev/null +++ b/apps/web/src/vault/app/core/vault-filter/router-vault-filter.service.ts @@ -0,0 +1,47 @@ +import { Injectable, OnDestroy } from "@angular/core"; +import { ActivatedRoute, NavigationExtras } from "@angular/router"; +import { combineLatest, map, Observable, Subject, takeUntil, tap } from "rxjs"; + +import { RoutedVaultFilterModel } from "./routed-vault-filter.model"; + +@Injectable() +export class RoutedVaultFilterService implements OnDestroy { + private onDestroy = new Subject(); + + filter$: Observable; + + constructor(activatedRoute: ActivatedRoute) { + this.filter$ = combineLatest([activatedRoute.paramMap, activatedRoute.queryParamMap]).pipe( + map(([params, queryParams]) => { + return { + collectionId: queryParams.get("collectionId") ?? undefined, + folderId: queryParams.get("folderId") ?? undefined, + organizationId: + queryParams.get("organizationId") ?? params.get("organizationId") ?? undefined, + }; + }), + // eslint-disable-next-line no-console + tap((filter) => console.log("RoutedVaultFilterService.filter", filter)), + takeUntil(this.onDestroy) + ); + } + + createRoute(filter: RoutedVaultFilterModel): { commands: any[]; extras?: NavigationExtras } { + return { + commands: [], + extras: { + queryParams: { + collectionId: filter.collectionId ?? null, + folderId: filter.folderId ?? null, + organizationId: filter.organizationId ?? null, + }, + queryParamsHandling: "merge", + }, + }; + } + + ngOnDestroy(): void { + this.onDestroy.next(); + this.onDestroy.complete(); + } +} diff --git a/apps/web/src/vault/app/core/vault-filter/vault-filter.module.ts b/apps/web/src/vault/app/core/vault-filter/vault-filter.module.ts new file mode 100644 index 000000000000..0dbe9654f0c2 --- /dev/null +++ b/apps/web/src/vault/app/core/vault-filter/vault-filter.module.ts @@ -0,0 +1,10 @@ +// import { CommonModule } from "@angular/common"; +// import { NgModule } from "@angular/core"; +// import { RouterModule } from "@angular/router"; + +// @NgModule({ +// declarations: [], +// imports: [CommonModule, RouterModule], +// exports: [], +// }) +// export class VaultFilterModule {} diff --git a/apps/web/src/vault/app/vault/vault-filter/services/routed-vault-filter-bridge.service.ts b/apps/web/src/vault/app/vault/vault-filter/services/routed-vault-filter-bridge.service.ts new file mode 100644 index 000000000000..de07fa7ba598 --- /dev/null +++ b/apps/web/src/vault/app/vault/vault-filter/services/routed-vault-filter-bridge.service.ts @@ -0,0 +1,76 @@ +import { Injectable } from "@angular/core"; +import { Router } from "@angular/router"; +import { combineLatest, map, Observable } from "rxjs"; + +import { ITreeNodeObject, TreeNode } from "@bitwarden/common/models/domain/tree-node"; + +import { RoutedVaultFilterModel } from "../../../core/vault-filter/routed-vault-filter.model"; +import { RoutedVaultFilterService } from "../../../core/vault-filter/router-vault-filter.service"; +import { RoutedVaultFilterBridge } from "../shared/models/routed-vault-filter-bridge.model"; +import { VaultFilter } from "../shared/models/vault-filter.model"; + +import { VaultFilterService } from "./abstractions/vault-filter.service"; + +@Injectable() +export class RoutedVaultFilterBridgeService { + readonly activeFilter$: Observable; + + constructor( + private router: Router, + private routedVaultFilterService: RoutedVaultFilterService, + legacyVaultFilterService: VaultFilterService + ) { + this.activeFilter$ = combineLatest([ + routedVaultFilterService.filter$, + legacyVaultFilterService.collectionTree$, + legacyVaultFilterService.folderTree$, + legacyVaultFilterService.organizationTree$, + ]).pipe( + map(([filter, collectionTree, folderTree, organizationTree]) => { + const legacyFilter = new VaultFilter(); + + if (filter.collectionId !== undefined) { + legacyFilter.selectedCollectionNode = this.findNode(collectionTree, filter.collectionId); + } + + if (filter.folderId !== undefined) { + legacyFilter.selectedFolderNode = this.findNode(folderTree, filter.folderId); + } + + if (filter.organizationId !== undefined) { + legacyFilter.selectedOrganizationNode = this.findNode( + organizationTree, + filter.organizationId + ); + } + + const bridgeModel = new RoutedVaultFilterBridge(legacyFilter, this); + + return bridgeModel; + }) + ); + } + + navigate(filter: RoutedVaultFilterModel) { + const route = this.routedVaultFilterService.createRoute(filter); + this.router.navigate(route.commands, route.extras); + } + + private findNode( + node: TreeNode, + id: string + ): TreeNode | undefined { + if (node.node.id === id) { + return node; + } + + for (const child of node.children) { + const result = this.findNode(child, id); + if (result !== undefined) { + return result; + } + } + + return undefined; + } +} diff --git a/apps/web/src/vault/app/vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts b/apps/web/src/vault/app/vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts new file mode 100644 index 000000000000..37e3aa0f2d74 --- /dev/null +++ b/apps/web/src/vault/app/vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts @@ -0,0 +1,89 @@ +import { TreeNode } from "@bitwarden/common/models/domain/tree-node"; +import { CipherType } from "@bitwarden/common/vault/enums/cipher-type"; + +import { RoutedVaultFilterBridgeService } from "../../services/routed-vault-filter-bridge.service"; + +import { VaultFilter, VaultFilterFunction } from "./vault-filter.model"; +import { + OrganizationFilter, + CipherTypeFilter, + FolderFilter, + CollectionFilter, + CipherStatus, +} from "./vault-filter.type"; + +export class RoutedVaultFilterBridge implements VaultFilter { + constructor( + private legacyFilter: VaultFilter, + private bridgeService: RoutedVaultFilterBridgeService + ) {} + get collectionBreadcrumbs(): TreeNode[] { + return this.legacyFilter.collectionBreadcrumbs; + } + get isCollectionSelected(): boolean { + return this.legacyFilter.isCollectionSelected; + } + get isUnassignedCollectionSelected(): boolean { + return this.legacyFilter.isUnassignedCollectionSelected; + } + get isMyVaultSelected(): boolean { + return this.legacyFilter.isMyVaultSelected; + } + get selectedOrganizationNode(): TreeNode { + return this.legacyFilter.selectedOrganizationNode; + } + set selectedOrganizationNode(value: TreeNode) { + this.bridgeService.navigate({ organizationId: value.node.id }); + } + get selectedCipherTypeNode(): TreeNode { + return this.legacyFilter.selectedCipherTypeNode; + } + set selectedCipherTypeNode(value: TreeNode) { + // this.bridgeService.navigate({ }); + } + get selectedFolderNode(): TreeNode { + return this.legacyFilter.selectedFolderNode; + } + set selectedFolderNode(value: TreeNode) { + this.bridgeService.navigate({ folderId: value.node.id }); + } + get selectedCollectionNode(): TreeNode { + return this.legacyFilter.selectedCollectionNode; + } + set selectedCollectionNode(value: TreeNode) { + this.bridgeService.navigate({ collectionId: value.node.id }); + } + get isFavorites(): boolean { + return this.legacyFilter.isFavorites; + } + get isDeleted(): boolean { + return this.legacyFilter.isDeleted; + } + get organizationId(): string { + return this.legacyFilter.organizationId; + } + get cipherType(): CipherType { + return this.legacyFilter.cipherType; + } + get cipherStatus(): CipherStatus { + return this.legacyFilter.cipherStatus; + } + get cipherTypeId(): string { + return this.legacyFilter.cipherTypeId; + } + get folderId(): string { + return this.legacyFilter.folderId; + } + get collectionId(): string { + return this.legacyFilter.collectionId; + } + resetFilter(): void { + return this.legacyFilter.resetFilter(); + } + resetOrganization(): void { + return this.legacyFilter.resetOrganization(); + } + buildFilter(): VaultFilterFunction { + return this.legacyFilter.buildFilter(); + } +} diff --git a/apps/web/src/vault/app/vault/vault-items.component.ts b/apps/web/src/vault/app/vault/vault-items.component.ts index 7f662f09f3fb..02445cc18835 100644 --- a/apps/web/src/vault/app/vault/vault-items.component.ts +++ b/apps/web/src/vault/app/vault/vault-items.component.ts @@ -456,7 +456,7 @@ export class VaultItemsComponent extends BaseVaultItemsComponent implements OnDe navigateCollection(node: TreeNode) { const filter = this.activeFilter; filter.selectedCollectionNode = node; - this.activeFilterChanged.emit(filter); + // this.activeFilterChanged.emit(filter); } checkAll(select: boolean) { diff --git a/apps/web/src/vault/app/vault/vault.component.ts b/apps/web/src/vault/app/vault/vault.component.ts index 37204662120f..73607bf98df7 100644 --- a/apps/web/src/vault/app/vault/vault.component.ts +++ b/apps/web/src/vault/app/vault/vault.component.ts @@ -29,6 +29,7 @@ import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.serv import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { UpdateKeyComponent } from "../../../app/settings/update-key.component"; +import { RoutedVaultFilterService } from "../core/vault-filter/router-vault-filter.service"; import { AddEditComponent } from "./add-edit.component"; import { AttachmentsComponent } from "./attachments.component"; @@ -37,6 +38,7 @@ import { FolderAddEditComponent } from "./folder-add-edit.component"; import { ShareComponent } from "./share.component"; import { VaultFilterComponent } from "./vault-filter/components/vault-filter.component"; import { VaultFilterService } from "./vault-filter/services/abstractions/vault-filter.service"; +import { RoutedVaultFilterBridgeService } from "./vault-filter/services/routed-vault-filter-bridge.service"; import { VaultFilter } from "./vault-filter/shared/models/vault-filter.model"; import { FolderFilter, OrganizationFilter } from "./vault-filter/shared/models/vault-filter.type"; import { VaultItemsComponent } from "./vault-items.component"; @@ -46,6 +48,7 @@ const BroadcasterSubscriptionId = "VaultComponent"; @Component({ selector: "app-vault", templateUrl: "vault.component.html", + providers: [RoutedVaultFilterService, RoutedVaultFilterBridgeService], }) export class VaultComponent implements OnInit, OnDestroy { @ViewChild("vaultFilter", { static: true }) filterComponent: VaultFilterComponent; @@ -88,6 +91,7 @@ export class VaultComponent implements OnInit, OnDestroy { private stateService: StateService, private organizationService: OrganizationService, private vaultFilterService: VaultFilterService, + private routedVaultFilterBridgeService: RoutedVaultFilterBridgeService, private cipherService: CipherService, private passwordRepromptService: PasswordRepromptService ) {} @@ -165,6 +169,17 @@ export class VaultComponent implements OnInit, OnDestroy { } }); }); + + this.routedVaultFilterBridgeService.activeFilter$ + .pipe(takeUntil(this.destroy$)) + .subscribe((activeFilter) => { + this.activeFilter = activeFilter; + // eslint-disable-next-line no-console + this.vaultItemsComponent.reload( + this.activeFilter.buildFilter(), + this.activeFilter.isDeleted + ); + }); } get isShowingCards() { @@ -194,7 +209,7 @@ export class VaultComponent implements OnInit, OnDestroy { this.activeFilter.buildFilter(), this.activeFilter.isDeleted ); - this.go(); + // this.go(); } async applyOrganizationFilter(orgId: string) { From f9888b6241cb0c8ae6f2a2cf9db4d7851b093d01 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Fri, 3 Feb 2023 15:29:29 +0100 Subject: [PATCH 02/30] [EC-775] fix: ciphers not reloading on filter change --- .../web/src/vault/app/vault/vault-items.component.ts | 12 ++++++++++-- apps/web/src/vault/app/vault/vault.component.ts | 5 ----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/apps/web/src/vault/app/vault/vault-items.component.ts b/apps/web/src/vault/app/vault/vault-items.component.ts index 02445cc18835..7caa49ab4d39 100644 --- a/apps/web/src/vault/app/vault/vault-items.component.ts +++ b/apps/web/src/vault/app/vault/vault-items.component.ts @@ -55,7 +55,6 @@ export type VaultItemRow = (CipherView | TreeNode) & { checked }) export class VaultItemsComponent extends BaseVaultItemsComponent implements OnDestroy { @Input() showAddNew = true; - @Input() activeFilter: VaultFilter; @Output() activeFilterChanged = new EventEmitter(); @Output() onAttachmentsClicked = new EventEmitter(); @Output() onShareClicked = new EventEmitter(); @@ -63,6 +62,15 @@ export class VaultItemsComponent extends BaseVaultItemsComponent implements OnDe @Output() onCloneClicked = new EventEmitter(); @Output() onOrganzationBadgeClicked = new EventEmitter(); + private _activeFilter: VaultFilter; + @Input() get activeFilter(): VaultFilter { + return this._activeFilter; + } + set activeFilter(value: VaultFilter) { + this._activeFilter = value; + this.reload(this.activeFilter.buildFilter(), this.activeFilter.isDeleted); + } + cipherType = CipherType; actionPromise: Promise; userHasPremiumAccess = false; @@ -456,7 +464,7 @@ export class VaultItemsComponent extends BaseVaultItemsComponent implements OnDe navigateCollection(node: TreeNode) { const filter = this.activeFilter; filter.selectedCollectionNode = node; - // this.activeFilterChanged.emit(filter); + this.activeFilterChanged.emit(filter); } checkAll(select: boolean) { diff --git a/apps/web/src/vault/app/vault/vault.component.ts b/apps/web/src/vault/app/vault/vault.component.ts index 73607bf98df7..75dbb3d1325d 100644 --- a/apps/web/src/vault/app/vault/vault.component.ts +++ b/apps/web/src/vault/app/vault/vault.component.ts @@ -174,11 +174,6 @@ export class VaultComponent implements OnInit, OnDestroy { .pipe(takeUntil(this.destroy$)) .subscribe((activeFilter) => { this.activeFilter = activeFilter; - // eslint-disable-next-line no-console - this.vaultItemsComponent.reload( - this.activeFilter.buildFilter(), - this.activeFilter.isDeleted - ); }); } From 9f0dcfeadf46d8684d202b0d25b85c5d50575951 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Mon, 6 Feb 2023 13:50:34 +0100 Subject: [PATCH 03/30] [EC-775] feat: add support for cipher types --- .../vault-filter/routed-vault-filter.model.ts | 1 + .../router-vault-filter.service.ts | 6 +-- .../abstractions/vault-filter.service.ts | 1 + .../routed-vault-filter-bridge.service.ts | 19 +++++--- .../services/vault-filter.service.ts | 43 +++++++++++++++++++ .../routed-vault-filter-bridge.model.ts | 18 ++++++-- 6 files changed, 76 insertions(+), 12 deletions(-) diff --git a/apps/web/src/vault/app/core/vault-filter/routed-vault-filter.model.ts b/apps/web/src/vault/app/core/vault-filter/routed-vault-filter.model.ts index 41531f1125b2..258fa1a908de 100644 --- a/apps/web/src/vault/app/core/vault-filter/routed-vault-filter.model.ts +++ b/apps/web/src/vault/app/core/vault-filter/routed-vault-filter.model.ts @@ -2,4 +2,5 @@ export interface RoutedVaultFilterModel { collectionId?: string; folderId?: string; organizationId?: string; + type?: string; } diff --git a/apps/web/src/vault/app/core/vault-filter/router-vault-filter.service.ts b/apps/web/src/vault/app/core/vault-filter/router-vault-filter.service.ts index a5293f41a031..85e90a534ee5 100644 --- a/apps/web/src/vault/app/core/vault-filter/router-vault-filter.service.ts +++ b/apps/web/src/vault/app/core/vault-filter/router-vault-filter.service.ts @@ -1,6 +1,6 @@ import { Injectable, OnDestroy } from "@angular/core"; import { ActivatedRoute, NavigationExtras } from "@angular/router"; -import { combineLatest, map, Observable, Subject, takeUntil, tap } from "rxjs"; +import { combineLatest, map, Observable, Subject, takeUntil } from "rxjs"; import { RoutedVaultFilterModel } from "./routed-vault-filter.model"; @@ -18,10 +18,9 @@ export class RoutedVaultFilterService implements OnDestroy { folderId: queryParams.get("folderId") ?? undefined, organizationId: queryParams.get("organizationId") ?? params.get("organizationId") ?? undefined, + type: queryParams.get("type") ?? undefined, }; }), - // eslint-disable-next-line no-console - tap((filter) => console.log("RoutedVaultFilterService.filter", filter)), takeUntil(this.onDestroy) ); } @@ -34,6 +33,7 @@ export class RoutedVaultFilterService implements OnDestroy { collectionId: filter.collectionId ?? null, folderId: filter.folderId ?? null, organizationId: filter.organizationId ?? null, + type: filter.type ?? null, }, queryParamsHandling: "merge", }, diff --git a/apps/web/src/vault/app/vault/vault-filter/services/abstractions/vault-filter.service.ts b/apps/web/src/vault/app/vault/vault-filter/services/abstractions/vault-filter.service.ts index 7ae84244a82f..3a454d08d5b6 100644 --- a/apps/web/src/vault/app/vault/vault-filter/services/abstractions/vault-filter.service.ts +++ b/apps/web/src/vault/app/vault/vault-filter/services/abstractions/vault-filter.service.ts @@ -19,6 +19,7 @@ export abstract class VaultFilterService { organizationTree$: Observable>; folderTree$: Observable>; collectionTree$: Observable>; + cipherTypeTree$: Observable>; reloadCollections: () => Promise; getCollectionNodeFromTree: (id: string) => Promise>; setCollapsedFilterNodes: (collapsedFilterNodes: Set) => Promise; diff --git a/apps/web/src/vault/app/vault/vault-filter/services/routed-vault-filter-bridge.service.ts b/apps/web/src/vault/app/vault/vault-filter/services/routed-vault-filter-bridge.service.ts index de07fa7ba598..af4e8d21247b 100644 --- a/apps/web/src/vault/app/vault/vault-filter/services/routed-vault-filter-bridge.service.ts +++ b/apps/web/src/vault/app/vault/vault-filter/services/routed-vault-filter-bridge.service.ts @@ -25,8 +25,9 @@ export class RoutedVaultFilterBridgeService { legacyVaultFilterService.collectionTree$, legacyVaultFilterService.folderTree$, legacyVaultFilterService.organizationTree$, + legacyVaultFilterService.cipherTypeTree$, ]).pipe( - map(([filter, collectionTree, folderTree, organizationTree]) => { + map(([filter, collectionTree, folderTree, organizationTree, cipherTypeTree]) => { const legacyFilter = new VaultFilter(); if (filter.collectionId !== undefined) { @@ -44,7 +45,11 @@ export class RoutedVaultFilterBridgeService { ); } - const bridgeModel = new RoutedVaultFilterBridge(legacyFilter, this); + if (filter.type !== undefined) { + legacyFilter.selectedCipherTypeNode = this.findNode(cipherTypeTree, filter.type); + } + + const bridgeModel = new RoutedVaultFilterBridge(filter, legacyFilter, this); return bridgeModel; }) @@ -58,14 +63,18 @@ export class RoutedVaultFilterBridgeService { private findNode( node: TreeNode, - id: string + idOrPredicate: string | ((node: T) => boolean) ): TreeNode | undefined { - if (node.node.id === id) { + if (typeof idOrPredicate === "string" && node.node.id === idOrPredicate) { + return node; + } + + if (typeof idOrPredicate === "function" && idOrPredicate(node.node)) { return node; } for (const child of node.children) { - const result = this.findNode(child, id); + const result = this.findNode(child, idOrPredicate); if (result !== undefined) { return result; } diff --git a/apps/web/src/vault/app/vault/vault-filter/services/vault-filter.service.ts b/apps/web/src/vault/app/vault/vault-filter/services/vault-filter.service.ts index bae89751b8ef..dbac1974e58e 100644 --- a/apps/web/src/vault/app/vault/vault-filter/services/vault-filter.service.ts +++ b/apps/web/src/vault/app/vault/vault-filter/services/vault-filter.service.ts @@ -22,6 +22,7 @@ import { TreeNode } from "@bitwarden/common/models/domain/tree-node"; import { CollectionView } from "@bitwarden/common/models/view/collection.view"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; +import { CipherType } from "@bitwarden/common/vault/enums/cipher-type"; import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; import { CollectionAdminView } from "../../../../../app/organizations/core"; @@ -73,6 +74,8 @@ export class VaultFilterService implements VaultFilterServiceAbstraction { map((collections) => this.buildCollectionTree(collections)) ); + cipherTypeTree$: Observable> = this.buildCipherTypeTree(); + constructor( protected stateService: StateService, protected organizationService: OrganizationService, @@ -253,4 +256,44 @@ export class VaultFilterService implements VaultFilterServiceAbstraction { const head = new FolderView() as FolderFilter; return new TreeNode(head, null, "folders", "AllFolders"); } + + protected buildCipherTypeTree(): Observable> { + const allTypeFilters: CipherTypeFilter[] = [ + { + id: "favorites", + name: this.i18nService.t("favorites"), + type: "favorites", + icon: "bwi-star", + }, + { + id: "login", + name: this.i18nService.t("typeLogin"), + type: CipherType.Login, + icon: "bwi-globe", + }, + { + id: "card", + name: this.i18nService.t("typeCard"), + type: CipherType.Card, + icon: "bwi-credit-card", + }, + { + id: "identity", + name: this.i18nService.t("typeIdentity"), + type: CipherType.Identity, + icon: "bwi-id-card", + }, + { + id: "note", + name: this.i18nService.t("typeSecureNote"), + type: CipherType.SecureNote, + icon: "bwi-sticky-note", + }, + ]; + + return this.buildTypeTree( + { id: "AllItems", name: "allItems", type: "all", icon: "" }, + allTypeFilters + ); + } } diff --git a/apps/web/src/vault/app/vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts b/apps/web/src/vault/app/vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts index 37e3aa0f2d74..8045e4d7ecf4 100644 --- a/apps/web/src/vault/app/vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts +++ b/apps/web/src/vault/app/vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts @@ -1,6 +1,7 @@ import { TreeNode } from "@bitwarden/common/models/domain/tree-node"; import { CipherType } from "@bitwarden/common/vault/enums/cipher-type"; +import { RoutedVaultFilterModel } from "../../../../core/vault-filter/routed-vault-filter.model"; import { RoutedVaultFilterBridgeService } from "../../services/routed-vault-filter-bridge.service"; import { VaultFilter, VaultFilterFunction } from "./vault-filter.model"; @@ -14,6 +15,7 @@ import { export class RoutedVaultFilterBridge implements VaultFilter { constructor( + private routedFilter: RoutedVaultFilterModel, private legacyFilter: VaultFilter, private bridgeService: RoutedVaultFilterBridgeService ) {} @@ -33,25 +35,33 @@ export class RoutedVaultFilterBridge implements VaultFilter { return this.legacyFilter.selectedOrganizationNode; } set selectedOrganizationNode(value: TreeNode) { - this.bridgeService.navigate({ organizationId: value.node.id }); + this.bridgeService.navigate({ ...this.routedFilter, organizationId: value.node.id }); } get selectedCipherTypeNode(): TreeNode { return this.legacyFilter.selectedCipherTypeNode; } set selectedCipherTypeNode(value: TreeNode) { - // this.bridgeService.navigate({ }); + this.bridgeService.navigate({ ...this.routedFilter, type: value.node.id }); } get selectedFolderNode(): TreeNode { return this.legacyFilter.selectedFolderNode; } set selectedFolderNode(value: TreeNode) { - this.bridgeService.navigate({ folderId: value.node.id }); + this.bridgeService.navigate({ + ...this.routedFilter, + folderId: value.node.id, + collectionId: null, + }); } get selectedCollectionNode(): TreeNode { return this.legacyFilter.selectedCollectionNode; } set selectedCollectionNode(value: TreeNode) { - this.bridgeService.navigate({ collectionId: value.node.id }); + this.bridgeService.navigate({ + ...this.routedFilter, + folderId: null, + collectionId: value.node.id, + }); } get isFavorites(): boolean { return this.legacyFilter.isFavorites; From 096e7fdc1a80ed7ffb4b0d973573dd48c4dc3a2a Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Tue, 7 Feb 2023 09:49:10 +0100 Subject: [PATCH 04/30] [EC-775] feat: implement organization switching --- .../vault/vault-filter/components/vault-filter.component.ts | 5 +++-- .../shared/models/routed-vault-filter-bridge.model.ts | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/web/src/vault/app/vault/vault-filter/components/vault-filter.component.ts b/apps/web/src/vault/app/vault/vault-filter/components/vault-filter.component.ts index 1b9a6821d5b3..13653c08ea3d 100644 --- a/apps/web/src/vault/app/vault/vault-filter/components/vault-filter.component.ts +++ b/apps/web/src/vault/app/vault/vault-filter/components/vault-filter.component.ts @@ -143,8 +143,9 @@ export class VaultFilterComponent implements OnInit, OnDestroy { return; } const filter = this.activeFilter; - filter.resetOrganization(); - if (orgNode?.node.id !== "AllVaults") { + if (orgNode?.node.id === "AllVaults") { + filter.resetOrganization(); + } else { filter.selectedOrganizationNode = orgNode; } this.vaultFilterService.setOrganizationFilter(orgNode.node); diff --git a/apps/web/src/vault/app/vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts b/apps/web/src/vault/app/vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts index 8045e4d7ecf4..7f8419cb9b24 100644 --- a/apps/web/src/vault/app/vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts +++ b/apps/web/src/vault/app/vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts @@ -88,10 +88,10 @@ export class RoutedVaultFilterBridge implements VaultFilter { return this.legacyFilter.collectionId; } resetFilter(): void { - return this.legacyFilter.resetFilter(); + this.bridgeService.navigate({}); } resetOrganization(): void { - return this.legacyFilter.resetOrganization(); + this.bridgeService.navigate({ ...this.routedFilter, organizationId: null }); } buildFilter(): VaultFilterFunction { return this.legacyFilter.buildFilter(); From b2c341fa35899ba357409617ec07f6ffbae40073 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Tue, 14 Feb 2023 10:53:07 +0100 Subject: [PATCH 05/30] [EC-775] feat: remove invalid folder and collection checks Had to remove these becuase they were causing double navigations on each click. --- .../components/vault-filter.component.ts | 52 +------------------ .../routed-vault-filter-bridge.model.ts | 6 ++- .../vault-filter/vault-filter.component.ts | 25 +-------- 3 files changed, 8 insertions(+), 75 deletions(-) diff --git a/apps/web/src/vault/individual-vault/vault-filter/components/vault-filter.component.ts b/apps/web/src/vault/individual-vault/vault-filter/components/vault-filter.component.ts index 13653c08ea3d..89fb827725e6 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/components/vault-filter.component.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/components/vault-filter.component.ts @@ -1,14 +1,12 @@ import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from "@angular/core"; -import { firstValueFrom, Subject, switchMap, takeUntil } from "rxjs"; +import { firstValueFrom, Subject } from "rxjs"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { PolicyService } from "@bitwarden/common/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/enums/policyType"; import { TreeNode } from "@bitwarden/common/models/domain/tree-node"; -import { CollectionView } from "@bitwarden/common/models/view/collection.view"; import { CipherType } from "@bitwarden/common/vault/enums/cipher-type"; -import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; import { VaultFilterService } from "../services/abstractions/vault-filter.service"; import { @@ -88,9 +86,7 @@ export class VaultFilterComponent implements OnInit, OnDestroy { protected policyService: PolicyService, protected i18nService: I18nService, protected platformUtilsService: PlatformUtilsService - ) { - this.loadSubscriptions(); - } + ) {} async ngOnInit(): Promise { this.filters = await this.buildAllFilters(); @@ -104,26 +100,6 @@ export class VaultFilterComponent implements OnInit, OnDestroy { this.destroy$.complete(); } - protected loadSubscriptions() { - this.vaultFilterService.filteredFolders$ - .pipe( - switchMap(async (folders) => { - this.removeInvalidFolderSelection(folders); - }), - takeUntil(this.destroy$) - ) - .subscribe(); - - this.vaultFilterService.filteredCollections$ - .pipe( - switchMap(async (collections) => { - this.removeInvalidCollectionSelection(collections); - }), - takeUntil(this.destroy$) - ) - .subscribe(); - } - searchTextChanged(t: string) { this.searchText = t; this.onSearchTextChanged.emit(t); @@ -186,30 +162,6 @@ export class VaultFilterComponent implements OnInit, OnDestroy { return await firstValueFrom(this.filters?.typeFilter.data$); } - protected async removeInvalidFolderSelection(folders: FolderView[]) { - if (this.activeFilter.selectedFolderNode) { - if (!folders.some((f) => f.id === this.activeFilter.folderId)) { - const filter = this.activeFilter; - filter.resetFilter(); - filter.selectedCipherTypeNode = - (await this.getDefaultFilter()) as TreeNode; - this.applyVaultFilter(filter); - } - } - } - - protected async removeInvalidCollectionSelection(collections: CollectionView[]) { - if (this.activeFilter.selectedCollectionNode) { - if (!collections.some((f) => f.id === this.activeFilter.collectionId)) { - const filter = this.activeFilter; - filter.resetFilter(); - filter.selectedCipherTypeNode = - (await this.getDefaultFilter()) as TreeNode; - this.applyVaultFilter(filter); - } - } - } - async buildAllFilters(): Promise { const builderFilter = {} as VaultFilterList; builderFilter.organizationFilter = await this.addOrganizationFilter(); diff --git a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts index c78e63828147..1d912519d11b 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts @@ -35,7 +35,11 @@ export class RoutedVaultFilterBridge implements VaultFilter { return this.legacyFilter.selectedOrganizationNode; } set selectedOrganizationNode(value: TreeNode) { - this.bridgeService.navigate({ ...this.routedFilter, organizationId: value.node.id }); + this.bridgeService.navigate({ + ...this.routedFilter, + organizationId: value.node.id, + collectionId: null, + }); } get selectedCipherTypeNode(): TreeNode { return this.legacyFilter.selectedCipherTypeNode; diff --git a/apps/web/src/vault/org-vault/vault-filter/vault-filter.component.ts b/apps/web/src/vault/org-vault/vault-filter/vault-filter.component.ts index 2dca1c967a01..fa8b6cff5ceb 100644 --- a/apps/web/src/vault/org-vault/vault-filter/vault-filter.component.ts +++ b/apps/web/src/vault/org-vault/vault-filter/vault-filter.component.ts @@ -1,9 +1,8 @@ import { Component, Input, OnDestroy, OnInit } from "@angular/core"; -import { firstValueFrom, Subject, switchMap, takeUntil } from "rxjs"; +import { firstValueFrom, Subject } from "rxjs"; import { Organization } from "@bitwarden/common/models/domain/organization"; import { TreeNode } from "@bitwarden/common/models/domain/tree-node"; -import { CollectionView } from "@bitwarden/common/models/view/collection.view"; import { VaultFilterComponent as BaseVaultFilterComponent } from "../../individual-vault/vault-filter/components/vault-filter.component"; //../../vault/vault-filter/components/vault-filter.component"; import { @@ -41,28 +40,6 @@ export class VaultFilterComponent extends BaseVaultFilterComponent implements On this.destroy$.complete(); } - protected loadSubscriptions() { - this.vaultFilterService.filteredCollections$ - .pipe( - switchMap(async (collections) => { - this.removeInvalidCollectionSelection(collections); - }), - takeUntil(this.destroy$) - ) - .subscribe(); - } - - protected async removeInvalidCollectionSelection(collections: CollectionView[]) { - if (this.activeFilter.selectedCollectionNode) { - if (!collections.some((f) => f.id === this.activeFilter.collectionId)) { - this.activeFilter.resetFilter(); - this.activeFilter.selectedCollectionNode = - (await this.getDefaultFilter()) as TreeNode; - this.applyVaultFilter(this.activeFilter); - } - } - } - async buildAllFilters(): Promise { const builderFilter = {} as VaultFilterList; builderFilter.typeFilter = await this.addTypeFilter(["favorites"]); From 7594233256980a635a1aaf14f866e93f50e7d669 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Tue, 14 Feb 2023 13:57:43 +0100 Subject: [PATCH 06/30] [EC-775] fix: fix reverse data flow race condition vault-filter.component was pushing up old filter models which would sometimes overwrite new filter models that came from the routed filter service. --- .../vault-filter/components/vault-filter.component.ts | 9 --------- .../src/vault/individual-vault/vault.component.html | 3 --- apps/web/src/vault/individual-vault/vault.component.ts | 10 ---------- apps/web/src/vault/org-vault/vault.component.html | 3 --- 4 files changed, 25 deletions(-) diff --git a/apps/web/src/vault/individual-vault/vault-filter/components/vault-filter.component.ts b/apps/web/src/vault/individual-vault/vault-filter/components/vault-filter.component.ts index 89fb827725e6..a3e2cc8cdf62 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/components/vault-filter.component.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/components/vault-filter.component.ts @@ -32,7 +32,6 @@ import { OrganizationOptionsComponent } from "./organization-options.component"; export class VaultFilterComponent implements OnInit, OnDestroy { filters?: VaultFilterList; @Input() activeFilter: VaultFilter = new VaultFilter(); - @Output() activeFilterChanged = new EventEmitter(); @Output() onSearchTextChanged = new EventEmitter(); @Output() onAddFolder = new EventEmitter(); @Output() onEditFolder = new EventEmitter(); @@ -105,10 +104,6 @@ export class VaultFilterComponent implements OnInit, OnDestroy { this.onSearchTextChanged.emit(t); } - protected applyVaultFilter(filter: VaultFilter) { - this.activeFilterChanged.emit(filter); - } - applyOrganizationFilter = async (orgNode: TreeNode): Promise => { if (!orgNode?.node.enabled) { this.platformUtilsService.showToast( @@ -126,28 +121,24 @@ export class VaultFilterComponent implements OnInit, OnDestroy { } this.vaultFilterService.setOrganizationFilter(orgNode.node); await this.vaultFilterService.expandOrgFilter(); - this.applyVaultFilter(filter); }; applyTypeFilter = async (filterNode: TreeNode): Promise => { const filter = this.activeFilter; filter.resetFilter(); filter.selectedCipherTypeNode = filterNode; - this.applyVaultFilter(filter); }; applyFolderFilter = async (folderNode: TreeNode): Promise => { const filter = this.activeFilter; filter.resetFilter(); filter.selectedFolderNode = folderNode; - this.applyVaultFilter(filter); }; applyCollectionFilter = async (collectionNode: TreeNode): Promise => { const filter = this.activeFilter; filter.resetFilter(); filter.selectedCollectionNode = collectionNode; - this.applyVaultFilter(filter); }; addFolder = async (): Promise => { diff --git a/apps/web/src/vault/individual-vault/vault.component.html b/apps/web/src/vault/individual-vault/vault.component.html index ceb82cfa38c0..25f441187550 100644 --- a/apps/web/src/vault/individual-vault/vault.component.html +++ b/apps/web/src/vault/individual-vault/vault.component.html @@ -7,7 +7,6 @@ @@ -28,7 +26,6 @@ @@ -20,7 +19,6 @@
Date: Tue, 14 Feb 2023 14:36:28 +0100 Subject: [PATCH 07/30] [EC-775] fix: No folder use-case not working --- .../services/routed-vault-filter-bridge.service.ts | 13 ++++++++++--- .../models/routed-vault-filter-bridge.model.ts | 11 ++++++++++- .../shared/models/routed-vault-filter.model.ts | 2 ++ 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts index 749097c46756..1ce0c1faa275 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts @@ -5,7 +5,7 @@ import { combineLatest, map, Observable } from "rxjs"; import { ITreeNodeObject, TreeNode } from "@bitwarden/common/models/domain/tree-node"; import { RoutedVaultFilterBridge } from "../shared/models/routed-vault-filter-bridge.model"; -import { RoutedVaultFilterModel } from "../shared/models/routed-vault-filter.model"; +import { RoutedVaultFilterModel, Unassigned } from "../shared/models/routed-vault-filter.model"; import { VaultFilter } from "../shared/models/vault-filter.model"; import { VaultFilterService } from "./abstractions/vault-filter.service"; @@ -34,7 +34,11 @@ export class RoutedVaultFilterBridgeService { legacyFilter.selectedCollectionNode = this.findNode(collectionTree, filter.collectionId); } - if (filter.folderId !== undefined) { + if (filter.folderId !== undefined && filter.folderId === Unassigned) { + legacyFilter.selectedFolderNode = this.findNode(folderTree, null); + } + + if (filter.folderId !== undefined && filter.folderId !== Unassigned) { legacyFilter.selectedFolderNode = this.findNode(folderTree, filter.folderId); } @@ -65,7 +69,10 @@ export class RoutedVaultFilterBridgeService { node: TreeNode, idOrPredicate: string | ((node: T) => boolean) ): TreeNode | undefined { - if (typeof idOrPredicate === "string" && node.node.id === idOrPredicate) { + if ( + (typeof idOrPredicate === "string" || idOrPredicate == null) && + node.node.id === idOrPredicate + ) { return node; } diff --git a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts index 1d912519d11b..4b4928ccb58b 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts @@ -3,7 +3,7 @@ import { CipherType } from "@bitwarden/common/vault/enums/cipher-type"; import { RoutedVaultFilterBridgeService } from "../../services/routed-vault-filter-bridge.service"; -import { RoutedVaultFilterModel } from "./routed-vault-filter.model"; +import { RoutedVaultFilterModel, Unassigned } from "./routed-vault-filter.model"; import { VaultFilter, VaultFilterFunction } from "./vault-filter.model"; import { OrganizationFilter, @@ -51,6 +51,15 @@ export class RoutedVaultFilterBridge implements VaultFilter { return this.legacyFilter.selectedFolderNode; } set selectedFolderNode(value: TreeNode) { + if (value != null && value.node.id === null) { + this.bridgeService.navigate({ + ...this.routedFilter, + folderId: Unassigned, + collectionId: null, + }); + return; + } + this.bridgeService.navigate({ ...this.routedFilter, folderId: value.node.id, diff --git a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model.ts b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model.ts index 258fa1a908de..a5a247896a13 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model.ts @@ -1,3 +1,5 @@ +export const Unassigned = "unassigned"; + export interface RoutedVaultFilterModel { collectionId?: string; folderId?: string; From 4eaeb9b7ae876fe0b8c8429d00da22e006d4a98e Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Tue, 14 Feb 2023 15:14:10 +0100 Subject: [PATCH 08/30] [EC-775] feat: make navigation behave like master --- .../shared/models/routed-vault-filter-bridge.model.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts index 4b4928ccb58b..0cf104873c7f 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts @@ -45,7 +45,12 @@ export class RoutedVaultFilterBridge implements VaultFilter { return this.legacyFilter.selectedCipherTypeNode; } set selectedCipherTypeNode(value: TreeNode) { - this.bridgeService.navigate({ ...this.routedFilter, type: value.node.id }); + this.bridgeService.navigate({ + ...this.routedFilter, + type: value.node.id, + folderId: null, + collectionId: null, + }); } get selectedFolderNode(): TreeNode { return this.legacyFilter.selectedFolderNode; @@ -63,6 +68,7 @@ export class RoutedVaultFilterBridge implements VaultFilter { this.bridgeService.navigate({ ...this.routedFilter, folderId: value.node.id, + type: null, collectionId: null, }); } @@ -72,6 +78,7 @@ export class RoutedVaultFilterBridge implements VaultFilter { set selectedCollectionNode(value: TreeNode) { this.bridgeService.navigate({ ...this.routedFilter, + type: null, folderId: null, collectionId: value.node.id, }); From 4452ddd9bccfd658d2800db26328d2dd75944fe0 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Tue, 14 Feb 2023 15:43:06 +0100 Subject: [PATCH 09/30] [EC-775] feat: add support for trash --- .../services/routed-vault-filter-bridge.service.ts | 9 ++++++++- .../shared/models/routed-vault-filter-bridge.model.ts | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts index 1ce0c1faa275..f9cd60ee270a 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts @@ -7,6 +7,7 @@ import { ITreeNodeObject, TreeNode } from "@bitwarden/common/models/domain/tree- import { RoutedVaultFilterBridge } from "../shared/models/routed-vault-filter-bridge.model"; import { RoutedVaultFilterModel, Unassigned } from "../shared/models/routed-vault-filter.model"; import { VaultFilter } from "../shared/models/vault-filter.model"; +import { CipherTypeFilter } from "../shared/models/vault-filter.type"; import { VaultFilterService } from "./abstractions/vault-filter.service"; import { RoutedVaultFilterService } from "./routed-vault-filter.service"; @@ -49,7 +50,13 @@ export class RoutedVaultFilterBridgeService { ); } - if (filter.type !== undefined) { + if (filter.type !== undefined && filter.type === "trash") { + legacyFilter.selectedCipherTypeNode = { + node: { id: "trash", name: "", icon: "", type: "trash" }, + } as unknown as TreeNode; + } + + if (filter.type !== undefined && filter.type !== "trash") { legacyFilter.selectedCipherTypeNode = this.findNode(cipherTypeTree, filter.type); } diff --git a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts index 0cf104873c7f..71c0256ad35a 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts @@ -60,6 +60,7 @@ export class RoutedVaultFilterBridge implements VaultFilter { this.bridgeService.navigate({ ...this.routedFilter, folderId: Unassigned, + type: null, collectionId: null, }); return; From 1e138591d3af57c68e36184ce01051a166951070 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Wed, 15 Feb 2023 11:34:42 +0100 Subject: [PATCH 10/30] [EC-775] chore: simplify findNode --- .../routed-vault-filter-bridge.service.ts | 13 +++---------- .../routed-vault-filter-bridge.service.ts | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 10 deletions(-) create mode 100644 apps/web/src/vault/org-vault/vault-filter/routed-vault-filter-bridge.service.ts diff --git a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts index f9cd60ee270a..79e47a60ca20 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts @@ -74,21 +74,14 @@ export class RoutedVaultFilterBridgeService { private findNode( node: TreeNode, - idOrPredicate: string | ((node: T) => boolean) + id: string ): TreeNode | undefined { - if ( - (typeof idOrPredicate === "string" || idOrPredicate == null) && - node.node.id === idOrPredicate - ) { - return node; - } - - if (typeof idOrPredicate === "function" && idOrPredicate(node.node)) { + if (node.node.id === id) { return node; } for (const child of node.children) { - const result = this.findNode(child, idOrPredicate); + const result = this.findNode(child, id); if (result !== undefined) { return result; } diff --git a/apps/web/src/vault/org-vault/vault-filter/routed-vault-filter-bridge.service.ts b/apps/web/src/vault/org-vault/vault-filter/routed-vault-filter-bridge.service.ts new file mode 100644 index 000000000000..be8bc8628602 --- /dev/null +++ b/apps/web/src/vault/org-vault/vault-filter/routed-vault-filter-bridge.service.ts @@ -0,0 +1,18 @@ +import { Injectable } from "@angular/core"; +import { Router } from "@angular/router"; + +import { RoutedVaultFilterBridgeService as BaseRoutedVaultFilterBridgeService } from "../../individual-vault/vault-filter/services/routed-vault-filter-bridge.service"; +import { RoutedVaultFilterService } from "../../individual-vault/vault-filter/services/routed-vault-filter.service"; + +import { VaultFilterService } from "./vault-filter.service"; + +@Injectable() +export class RoutedVaultFilterBridgeService extends BaseRoutedVaultFilterBridgeService { + constructor( + private router: Router, + private routedVaultFilterService: RoutedVaultFilterService, + legacyVaultFilterService: VaultFilterService + ) { + super(router, routedVaultFilterService, legacyVaultFilterService); + } +} From 0b6b07589d845cd733072d7cbf548524542da49d Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Wed, 15 Feb 2023 14:15:35 +0100 Subject: [PATCH 11/30] [EC-775] feat: add support for org vault --- .../routed-vault-filter-bridge.service.ts | 6 +++++- .../routed-vault-filter-bridge.model.ts | 10 ++++++++++ .../routed-vault-filter-bridge.service.ts | 18 ----------------- .../src/vault/org-vault/vault.component.ts | 20 +++++++++---------- 4 files changed, 25 insertions(+), 29 deletions(-) delete mode 100644 apps/web/src/vault/org-vault/vault-filter/routed-vault-filter-bridge.service.ts diff --git a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts index 79e47a60ca20..f13423fa54e4 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts @@ -31,7 +31,11 @@ export class RoutedVaultFilterBridgeService { map(([filter, collectionTree, folderTree, organizationTree, cipherTypeTree]) => { const legacyFilter = new VaultFilter(); - if (filter.collectionId !== undefined) { + if (filter.collectionId !== undefined && filter.collectionId === Unassigned) { + legacyFilter.selectedCollectionNode = this.findNode(collectionTree, null); + } + + if (filter.collectionId !== undefined && filter.collectionId !== Unassigned) { legacyFilter.selectedCollectionNode = this.findNode(collectionTree, filter.collectionId); } diff --git a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts index 71c0256ad35a..cce0331f3020 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts @@ -77,6 +77,16 @@ export class RoutedVaultFilterBridge implements VaultFilter { return this.legacyFilter.selectedCollectionNode; } set selectedCollectionNode(value: TreeNode) { + if (value != null && value.node.id === null) { + this.bridgeService.navigate({ + ...this.routedFilter, + collectionId: Unassigned, + type: null, + folderId: null, + }); + return; + } + this.bridgeService.navigate({ ...this.routedFilter, type: null, diff --git a/apps/web/src/vault/org-vault/vault-filter/routed-vault-filter-bridge.service.ts b/apps/web/src/vault/org-vault/vault-filter/routed-vault-filter-bridge.service.ts deleted file mode 100644 index be8bc8628602..000000000000 --- a/apps/web/src/vault/org-vault/vault-filter/routed-vault-filter-bridge.service.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Injectable } from "@angular/core"; -import { Router } from "@angular/router"; - -import { RoutedVaultFilterBridgeService as BaseRoutedVaultFilterBridgeService } from "../../individual-vault/vault-filter/services/routed-vault-filter-bridge.service"; -import { RoutedVaultFilterService } from "../../individual-vault/vault-filter/services/routed-vault-filter.service"; - -import { VaultFilterService } from "./vault-filter.service"; - -@Injectable() -export class RoutedVaultFilterBridgeService extends BaseRoutedVaultFilterBridgeService { - constructor( - private router: Router, - private routedVaultFilterService: RoutedVaultFilterService, - legacyVaultFilterService: VaultFilterService - ) { - super(router, routedVaultFilterService, legacyVaultFilterService); - } -} diff --git a/apps/web/src/vault/org-vault/vault.component.ts b/apps/web/src/vault/org-vault/vault.component.ts index ab65aea8ae34..d447559566ca 100644 --- a/apps/web/src/vault/org-vault/vault.component.ts +++ b/apps/web/src/vault/org-vault/vault.component.ts @@ -28,6 +28,8 @@ import { EntityEventsComponent } from "../../app/organizations/manage/entity-eve import { CollectionsComponent } from "../../app/organizations/vault/collections.component"; import { VaultFilterService } from "../../vault/individual-vault/vault-filter/services/abstractions/vault-filter.service"; import { VaultFilter } from "../../vault/individual-vault/vault-filter/shared/models/vault-filter.model"; +import { RoutedVaultFilterBridgeService } from "../individual-vault/vault-filter/services/routed-vault-filter-bridge.service"; +import { RoutedVaultFilterService } from "../individual-vault/vault-filter/services/routed-vault-filter.service"; import { AddEditComponent } from "./add-edit.component"; import { AttachmentsComponent } from "./attachments.component"; @@ -39,6 +41,7 @@ const BroadcasterSubscriptionId = "OrgVaultComponent"; @Component({ selector: "app-org-vault", templateUrl: "vault.component.html", + providers: [RoutedVaultFilterService, RoutedVaultFilterBridgeService], }) export class VaultComponent implements OnInit, OnDestroy { @ViewChild("vaultFilter", { static: true }) @@ -62,6 +65,7 @@ export class VaultComponent implements OnInit, OnDestroy { private route: ActivatedRoute, private organizationService: OrganizationService, protected vaultFilterService: VaultFilterService, + private routedVaultFilterBridgeService: RoutedVaultFilterBridgeService, private router: Router, private changeDetectorRef: ChangeDetectorRef, private syncService: SyncService, @@ -139,6 +143,12 @@ export class VaultComponent implements OnInit, OnDestroy { }); await this.syncService.fullSync(false); } + + this.routedVaultFilterBridgeService.activeFilter$ + .pipe(takeUntil(this.destroy$)) + .subscribe((activeFilter) => { + this.activeFilter = activeFilter; + }); } ngOnDestroy() { @@ -147,16 +157,6 @@ export class VaultComponent implements OnInit, OnDestroy { this.destroy$.complete(); } - async applyVaultFilter(filter: VaultFilter) { - this.activeFilter = filter; - this.vaultItemsComponent.showAddNew = !this.activeFilter.isDeleted; - await this.vaultItemsComponent.reload( - this.activeFilter.buildFilter(), - this.activeFilter.isDeleted - ); - this.go(); - } - async refreshItems() { this.vaultItemsComponent.actionPromise = this.vaultItemsComponent.refresh(); await this.vaultItemsComponent.actionPromise; From 31944d0bb8770df302473db37eec4e8c0ee5907f Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Wed, 15 Feb 2023 14:49:30 +0100 Subject: [PATCH 12/30] [EC-775] feat: add support for orgId in path --- .../vault-filter/services/routed-vault-filter.service.ts | 7 +++++-- .../shared/models/routed-vault-filter-bridge.model.ts | 8 +++++++- .../shared/models/routed-vault-filter.model.ts | 2 ++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts index d367156062e5..a1d63ea13d7a 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts @@ -17,7 +17,9 @@ export class RoutedVaultFilterService implements OnDestroy { collectionId: queryParams.get("collectionId") ?? undefined, folderId: queryParams.get("folderId") ?? undefined, organizationId: - queryParams.get("organizationId") ?? params.get("organizationId") ?? undefined, + params.get("organizationId") ?? queryParams.get("organizationId") ?? undefined, + organizationIdParamType: + params.get("organizationId") != undefined ? ("path" as const) : ("query" as const), type: queryParams.get("type") ?? undefined, }; }), @@ -32,7 +34,8 @@ export class RoutedVaultFilterService implements OnDestroy { queryParams: { collectionId: filter.collectionId ?? null, folderId: filter.folderId ?? null, - organizationId: filter.organizationId ?? null, + organizationId: + filter.organizationIdParamType === "path" ? null : filter.organizationId ?? null, type: filter.type ?? null, }, queryParamsHandling: "merge", diff --git a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts index cce0331f3020..99e2647d71be 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts @@ -119,7 +119,13 @@ export class RoutedVaultFilterBridge implements VaultFilter { return this.legacyFilter.collectionId; } resetFilter(): void { - this.bridgeService.navigate({}); + this.bridgeService.navigate({ + ...this.routedFilter, + collectionId: null, + folderId: null, + organizationId: null, + type: null, + }); } resetOrganization(): void { this.bridgeService.navigate({ ...this.routedFilter, organizationId: null }); diff --git a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model.ts b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model.ts index a5a247896a13..ed82a6968078 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model.ts @@ -5,4 +5,6 @@ export interface RoutedVaultFilterModel { folderId?: string; organizationId?: string; type?: string; + + organizationIdParamType?: "path" | "query"; } From 9e4b5b31c5b1342054371ce2a7ead61baddec689 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Fri, 17 Feb 2023 11:28:21 +0100 Subject: [PATCH 13/30] [EC-775] feat: use proper treenode constructor --- .../services/routed-vault-filter-bridge.service.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts index f13423fa54e4..d122b5bf1fa2 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts @@ -55,9 +55,10 @@ export class RoutedVaultFilterBridgeService { } if (filter.type !== undefined && filter.type === "trash") { - legacyFilter.selectedCipherTypeNode = { - node: { id: "trash", name: "", icon: "", type: "trash" }, - } as unknown as TreeNode; + legacyFilter.selectedCipherTypeNode = new TreeNode( + { id: "trash", name: "", type: "trash", icon: "" }, + null + ); } if (filter.type !== undefined && filter.type !== "trash") { From 81295c55c76e6794e59c0da152e7862881cef2f1 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Fri, 17 Feb 2023 11:29:11 +0100 Subject: [PATCH 14/30] [EC-775] chore: remove unnecessary variable --- .../services/routed-vault-filter-bridge.service.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts index d122b5bf1fa2..e67762a89859 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts @@ -65,9 +65,7 @@ export class RoutedVaultFilterBridgeService { legacyFilter.selectedCipherTypeNode = this.findNode(cipherTypeTree, filter.type); } - const bridgeModel = new RoutedVaultFilterBridge(filter, legacyFilter, this); - - return bridgeModel; + return new RoutedVaultFilterBridge(filter, legacyFilter, this); }) ); } From a3b005935a6239912401d34bffd9d9534a485a09 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Fri, 17 Feb 2023 11:49:33 +0100 Subject: [PATCH 15/30] [EC-775] docs: add docs to relevant classes --- .../routed-vault-filter-bridge.service.ts | 9 +++++++++ .../services/routed-vault-filter.service.ts | 18 ++++++++++++++++++ .../models/routed-vault-filter-bridge.model.ts | 10 ++++++++++ 3 files changed, 37 insertions(+) diff --git a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts index e67762a89859..751aba666921 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts @@ -12,6 +12,15 @@ import { CipherTypeFilter } from "../shared/models/vault-filter.type"; import { VaultFilterService } from "./abstractions/vault-filter.service"; import { RoutedVaultFilterService } from "./routed-vault-filter.service"; +/** + * This file is part of a layer that is used to temporary bridge between URL filtering and the old state-in-code method. + * This should be removed after we have refactored the {@link VaultItemsComponent} and introduced vertical navigation + * (which will refactor the {@link VaultFilterComponent}). + * + * This class listens to both the new {@link RoutedVaultFilterService} and the old {@link VaultFilterService}. + * When a new filter is emitted the service uses the ids to find the corresponding tree nodes needed for + * the old {@link VaultFilter} model. It then emits a bridge model that contains this information. + */ @Injectable() export class RoutedVaultFilterBridgeService { readonly activeFilter$: Observable; diff --git a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts index a1d63ea13d7a..a49a6378c583 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts @@ -4,10 +4,21 @@ import { combineLatest, map, Observable, Subject, takeUntil } from "rxjs"; import { RoutedVaultFilterModel } from "../shared/models/routed-vault-filter.model"; +/** + * This service is an abstraction layer on top of ActivatedRoute that + * encapsulates the logic of how filters are stored in the URL. + * + * The service builds and emits filter models based on URL params and + * also contains a method for generating routes to corresponding to those params. + */ @Injectable() export class RoutedVaultFilterService implements OnDestroy { private onDestroy = new Subject(); + /** + * Filter values extracted from the URL. + * To change the values use {@link RoutedVaultFilterService.createRoute}. + */ filter$: Observable; constructor(activatedRoute: ActivatedRoute) { @@ -27,6 +38,13 @@ export class RoutedVaultFilterService implements OnDestroy { ); } + /** + * Create a route that can be used with Router or RouterLink. + * To subscribe to changes use {@link RoutedVaultFilterService.filter$} + * + * @param filter Filter values that should be applied to the URL. + * @returns route that can be used with Router or RouterLink + */ createRoute(filter: RoutedVaultFilterModel): { commands: any[]; extras?: NavigationExtras } { return { commands: [], diff --git a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts index 99e2647d71be..7a3961957897 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts @@ -13,6 +13,16 @@ import { CipherStatus, } from "./vault-filter.type"; +/** + * This file is part of a layer that is used to temporary bridge between URL filtering and the old state-in-code method. + * This should be removed after we have refactored the {@link VaultItemsComponent} and introduced vertical navigation + * (which will refactor the {@link VaultFilterComponent}). + * + * This model supplies legacy code with the old state-in-code models saved as tree nodes. + * It can also receive requests to select a new tree node by using setters. + * However instead of just replacing the tree node models, it requests a URL navigation, + * thereby bridging between legacy and URL filtering. + */ export class RoutedVaultFilterBridge implements VaultFilter { constructor( private routedFilter: RoutedVaultFilterModel, From bdd3d804d390074052695ece5e84aefede35eec8 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Fri, 17 Feb 2023 13:14:10 +0100 Subject: [PATCH 16/30] [EC-775] chore: use existing function for searching tree --- .../routed-vault-filter-bridge.service.ts | 45 +++++++++---------- libs/common/src/misc/serviceUtils.ts | 10 ++--- 2 files changed, 25 insertions(+), 30 deletions(-) diff --git a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts index 751aba666921..966996e76238 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts @@ -2,7 +2,8 @@ import { Injectable } from "@angular/core"; import { Router } from "@angular/router"; import { combineLatest, map, Observable } from "rxjs"; -import { ITreeNodeObject, TreeNode } from "@bitwarden/common/models/domain/tree-node"; +import { ServiceUtils } from "@bitwarden/common/misc/serviceUtils"; +import { TreeNode } from "@bitwarden/common/models/domain/tree-node"; import { RoutedVaultFilterBridge } from "../shared/models/routed-vault-filter-bridge.model"; import { RoutedVaultFilterModel, Unassigned } from "../shared/models/routed-vault-filter.model"; @@ -41,23 +42,32 @@ export class RoutedVaultFilterBridgeService { const legacyFilter = new VaultFilter(); if (filter.collectionId !== undefined && filter.collectionId === Unassigned) { - legacyFilter.selectedCollectionNode = this.findNode(collectionTree, null); + legacyFilter.selectedCollectionNode = ServiceUtils.getTreeNodeObject( + collectionTree, + null + ); } if (filter.collectionId !== undefined && filter.collectionId !== Unassigned) { - legacyFilter.selectedCollectionNode = this.findNode(collectionTree, filter.collectionId); + legacyFilter.selectedCollectionNode = ServiceUtils.getTreeNodeObject( + collectionTree, + filter.collectionId + ); } if (filter.folderId !== undefined && filter.folderId === Unassigned) { - legacyFilter.selectedFolderNode = this.findNode(folderTree, null); + legacyFilter.selectedFolderNode = ServiceUtils.getTreeNodeObject(folderTree, null); } if (filter.folderId !== undefined && filter.folderId !== Unassigned) { - legacyFilter.selectedFolderNode = this.findNode(folderTree, filter.folderId); + legacyFilter.selectedFolderNode = ServiceUtils.getTreeNodeObject( + folderTree, + filter.folderId + ); } if (filter.organizationId !== undefined) { - legacyFilter.selectedOrganizationNode = this.findNode( + legacyFilter.selectedOrganizationNode = ServiceUtils.getTreeNodeObject( organizationTree, filter.organizationId ); @@ -71,7 +81,10 @@ export class RoutedVaultFilterBridgeService { } if (filter.type !== undefined && filter.type !== "trash") { - legacyFilter.selectedCipherTypeNode = this.findNode(cipherTypeTree, filter.type); + legacyFilter.selectedCipherTypeNode = ServiceUtils.getTreeNodeObject( + cipherTypeTree, + filter.type + ); } return new RoutedVaultFilterBridge(filter, legacyFilter, this); @@ -83,22 +96,4 @@ export class RoutedVaultFilterBridgeService { const route = this.routedVaultFilterService.createRoute(filter); this.router.navigate(route.commands, route.extras); } - - private findNode( - node: TreeNode, - id: string - ): TreeNode | undefined { - if (node.node.id === id) { - return node; - } - - for (const child of node.children) { - const result = this.findNode(child, id); - if (result !== undefined) { - return result; - } - } - - return undefined; - } } diff --git a/libs/common/src/misc/serviceUtils.ts b/libs/common/src/misc/serviceUtils.ts index ac85cf0853a4..eb035a77ba34 100644 --- a/libs/common/src/misc/serviceUtils.ts +++ b/libs/common/src/misc/serviceUtils.ts @@ -70,14 +70,14 @@ export class ServiceUtils { /** * Searches a tree for a node with a matching `id` - * @param {TreeNode} nodeTree - A single TreeNode branch that will be searched + * @param {TreeNode} nodeTree - A single TreeNode branch that will be searched * @param {string} id - The id of the node to be found - * @returns {TreeNode} The node with a matching `id` + * @returns {TreeNode} The node with a matching `id` */ - static getTreeNodeObject( - nodeTree: TreeNode, + static getTreeNodeObject( + nodeTree: TreeNode, id: string - ): TreeNode { + ): TreeNode { if (nodeTree.node.id === id) { return nodeTree; } From 5dd50c0d2f8bd3f4746f24478ab83265e55539c6 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Fri, 17 Feb 2023 14:13:26 +0100 Subject: [PATCH 17/30] [EC-775] fix: hide "new" button in trash view --- apps/web/src/vault/individual-vault/vault-items.component.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/web/src/vault/individual-vault/vault-items.component.ts b/apps/web/src/vault/individual-vault/vault-items.component.ts index d749b9d62ec7..e38aaad24941 100644 --- a/apps/web/src/vault/individual-vault/vault-items.component.ts +++ b/apps/web/src/vault/individual-vault/vault-items.component.ts @@ -54,7 +54,6 @@ export type VaultItemRow = (CipherView | TreeNode) & { checked templateUrl: "vault-items.component.html", }) export class VaultItemsComponent extends BaseVaultItemsComponent implements OnDestroy { - @Input() showAddNew = true; @Output() activeFilterChanged = new EventEmitter(); @Output() onAttachmentsClicked = new EventEmitter(); @Output() onShareClicked = new EventEmitter(); @@ -90,6 +89,10 @@ export class VaultItemsComponent extends BaseVaultItemsComponent implements OnDe protected pagedCollections: TreeNode[] = []; protected searchedCollections: TreeNode[] = []; + get showAddNew() { + return !this.activeFilter.isDeleted; + } + get collections(): TreeNode[] { return this.activeFilter?.selectedCollectionNode?.children ?? []; } From ba558d814d4555ec9946ff8cb3a09e80fa701e4c Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Fri, 17 Feb 2023 15:01:11 +0100 Subject: [PATCH 18/30] [EC-775] feat: add explicit handling for `AllItems` --- .../services/routed-vault-filter-bridge.service.ts | 7 +++++++ .../shared/models/routed-vault-filter-bridge.model.ts | 10 ++++++++++ 2 files changed, 17 insertions(+) diff --git a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts index 966996e76238..2d4f923299dd 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts @@ -73,6 +73,13 @@ export class RoutedVaultFilterBridgeService { ); } + if (filter.type === undefined) { + legacyFilter.selectedCipherTypeNode = ServiceUtils.getTreeNodeObject( + cipherTypeTree, + "AllItems" + ); + } + if (filter.type !== undefined && filter.type === "trash") { legacyFilter.selectedCipherTypeNode = new TreeNode( { id: "trash", name: "", type: "trash", icon: "" }, diff --git a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts index 7a3961957897..8d0599693b1c 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts @@ -55,6 +55,16 @@ export class RoutedVaultFilterBridge implements VaultFilter { return this.legacyFilter.selectedCipherTypeNode; } set selectedCipherTypeNode(value: TreeNode) { + if (value?.node.id === "AllItems") { + this.bridgeService.navigate({ + ...this.routedFilter, + type: null, + folderId: null, + collectionId: null, + }); + return; + } + this.bridgeService.navigate({ ...this.routedFilter, type: value.node.id, From 7726d78e25fe5eb2085d35d8057951f21d2c1286 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Fri, 17 Feb 2023 15:02:50 +0100 Subject: [PATCH 19/30] [EC-775] fix: prune folderId when changing organization --- .../shared/models/routed-vault-filter-bridge.model.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts index 8d0599693b1c..b2a0353a78ba 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts @@ -48,6 +48,7 @@ export class RoutedVaultFilterBridge implements VaultFilter { this.bridgeService.navigate({ ...this.routedFilter, organizationId: value.node.id, + folderId: null, collectionId: null, }); } From d7d39a6d433b2ff7bdbcbc90f16f7ac7ce74fcfd Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Fri, 17 Feb 2023 15:11:27 +0100 Subject: [PATCH 20/30] [EC-775] fix: properly use `undefined` instead of `null` --- .../routed-vault-filter-bridge.model.ts | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts index b2a0353a78ba..83f9729763cb 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts @@ -48,8 +48,8 @@ export class RoutedVaultFilterBridge implements VaultFilter { this.bridgeService.navigate({ ...this.routedFilter, organizationId: value.node.id, - folderId: null, - collectionId: null, + folderId: undefined, + collectionId: undefined, }); } get selectedCipherTypeNode(): TreeNode { @@ -59,9 +59,9 @@ export class RoutedVaultFilterBridge implements VaultFilter { if (value?.node.id === "AllItems") { this.bridgeService.navigate({ ...this.routedFilter, - type: null, - folderId: null, - collectionId: null, + type: undefined, + folderId: undefined, + collectionId: undefined, }); return; } @@ -69,8 +69,8 @@ export class RoutedVaultFilterBridge implements VaultFilter { this.bridgeService.navigate({ ...this.routedFilter, type: value.node.id, - folderId: null, - collectionId: null, + folderId: undefined, + collectionId: undefined, }); } get selectedFolderNode(): TreeNode { @@ -81,8 +81,8 @@ export class RoutedVaultFilterBridge implements VaultFilter { this.bridgeService.navigate({ ...this.routedFilter, folderId: Unassigned, - type: null, - collectionId: null, + type: undefined, + collectionId: undefined, }); return; } @@ -90,8 +90,8 @@ export class RoutedVaultFilterBridge implements VaultFilter { this.bridgeService.navigate({ ...this.routedFilter, folderId: value.node.id, - type: null, - collectionId: null, + type: undefined, + collectionId: undefined, }); } get selectedCollectionNode(): TreeNode { @@ -102,16 +102,16 @@ export class RoutedVaultFilterBridge implements VaultFilter { this.bridgeService.navigate({ ...this.routedFilter, collectionId: Unassigned, - type: null, - folderId: null, + type: undefined, + folderId: undefined, }); return; } this.bridgeService.navigate({ ...this.routedFilter, - type: null, - folderId: null, + type: undefined, + folderId: undefined, collectionId: value.node.id, }); } @@ -142,14 +142,14 @@ export class RoutedVaultFilterBridge implements VaultFilter { resetFilter(): void { this.bridgeService.navigate({ ...this.routedFilter, - collectionId: null, - folderId: null, - organizationId: null, - type: null, + collectionId: undefined, + folderId: undefined, + organizationId: undefined, + type: undefined, }); } resetOrganization(): void { - this.bridgeService.navigate({ ...this.routedFilter, organizationId: null }); + this.bridgeService.navigate({ ...this.routedFilter, organizationId: undefined }); } buildFilter(): VaultFilterFunction { return this.legacyFilter.buildFilter(); From a8e66fc60a1d0f3c2b7f4741c6117a8f9235f4d8 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Fri, 17 Feb 2023 15:17:19 +0100 Subject: [PATCH 21/30] [EC-775] chore: simplify setters using ternary operator --- .../routed-vault-filter-bridge.model.ts | 39 +++---------------- 1 file changed, 6 insertions(+), 33 deletions(-) diff --git a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts index 83f9729763cb..c65030e76891 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts @@ -56,19 +56,10 @@ export class RoutedVaultFilterBridge implements VaultFilter { return this.legacyFilter.selectedCipherTypeNode; } set selectedCipherTypeNode(value: TreeNode) { - if (value?.node.id === "AllItems") { - this.bridgeService.navigate({ - ...this.routedFilter, - type: undefined, - folderId: undefined, - collectionId: undefined, - }); - return; - } - + const type = value?.node.id === "AllItems" ? null : value?.node.id; this.bridgeService.navigate({ ...this.routedFilter, - type: value.node.id, + type, folderId: undefined, collectionId: undefined, }); @@ -77,19 +68,10 @@ export class RoutedVaultFilterBridge implements VaultFilter { return this.legacyFilter.selectedFolderNode; } set selectedFolderNode(value: TreeNode) { - if (value != null && value.node.id === null) { - this.bridgeService.navigate({ - ...this.routedFilter, - folderId: Unassigned, - type: undefined, - collectionId: undefined, - }); - return; - } - + const folderId = value != null && value.node.id === null ? Unassigned : value?.node.id; this.bridgeService.navigate({ ...this.routedFilter, - folderId: value.node.id, + folderId, type: undefined, collectionId: undefined, }); @@ -98,21 +80,12 @@ export class RoutedVaultFilterBridge implements VaultFilter { return this.legacyFilter.selectedCollectionNode; } set selectedCollectionNode(value: TreeNode) { - if (value != null && value.node.id === null) { - this.bridgeService.navigate({ - ...this.routedFilter, - collectionId: Unassigned, - type: undefined, - folderId: undefined, - }); - return; - } - + const collectionId = value != null && value.node.id === null ? Unassigned : value?.node.id; this.bridgeService.navigate({ ...this.routedFilter, + collectionId, type: undefined, folderId: undefined, - collectionId: value.node.id, }); } get isFavorites(): boolean { From 284d64d6a605c1c70bfc7645c2f0c00c03fe86f0 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Fri, 17 Feb 2023 15:26:41 +0100 Subject: [PATCH 22/30] [EC-775] feat: add static typing to `type` filter --- .../services/routed-vault-filter.service.ts | 13 +++++++++++-- .../models/routed-vault-filter-bridge.model.ts | 14 ++++++++++++-- .../shared/models/routed-vault-filter.model.ts | 10 +++++++++- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts index a49a6378c583..19a444bd65ff 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts @@ -2,7 +2,10 @@ import { Injectable, OnDestroy } from "@angular/core"; import { ActivatedRoute, NavigationExtras } from "@angular/router"; import { combineLatest, map, Observable, Subject, takeUntil } from "rxjs"; -import { RoutedVaultFilterModel } from "../shared/models/routed-vault-filter.model"; +import { + RoutedVaultFilterItemType, + RoutedVaultFilterModel, +} from "../shared/models/routed-vault-filter.model"; /** * This service is an abstraction layer on top of ActivatedRoute that @@ -24,6 +27,12 @@ export class RoutedVaultFilterService implements OnDestroy { constructor(activatedRoute: ActivatedRoute) { this.filter$ = combineLatest([activatedRoute.paramMap, activatedRoute.queryParamMap]).pipe( map(([params, queryParams]) => { + const type = queryParams.get("type"); + let safeType: RoutedVaultFilterItemType | undefined = undefined; + if (["favorites", "login", "card", "identity", "note"].includes(type)) { + safeType = type as RoutedVaultFilterItemType; + } + return { collectionId: queryParams.get("collectionId") ?? undefined, folderId: queryParams.get("folderId") ?? undefined, @@ -31,7 +40,7 @@ export class RoutedVaultFilterService implements OnDestroy { params.get("organizationId") ?? queryParams.get("organizationId") ?? undefined, organizationIdParamType: params.get("organizationId") != undefined ? ("path" as const) : ("query" as const), - type: queryParams.get("type") ?? undefined, + type: safeType, }; }), takeUntil(this.onDestroy) diff --git a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts index c65030e76891..2e63485965dd 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts @@ -3,7 +3,11 @@ import { CipherType } from "@bitwarden/common/vault/enums/cipher-type"; import { RoutedVaultFilterBridgeService } from "../../services/routed-vault-filter-bridge.service"; -import { RoutedVaultFilterModel, Unassigned } from "./routed-vault-filter.model"; +import { + RoutedVaultFilterItemType, + RoutedVaultFilterModel, + Unassigned, +} from "./routed-vault-filter.model"; import { VaultFilter, VaultFilterFunction } from "./vault-filter.model"; import { OrganizationFilter, @@ -57,9 +61,15 @@ export class RoutedVaultFilterBridge implements VaultFilter { } set selectedCipherTypeNode(value: TreeNode) { const type = value?.node.id === "AllItems" ? null : value?.node.id; + + let safeType: RoutedVaultFilterItemType | undefined = undefined; + if (["favorites", "login", "card", "identity", "note", "trash"].includes(type)) { + safeType = type as RoutedVaultFilterItemType; + } + this.bridgeService.navigate({ ...this.routedFilter, - type, + type: safeType, folderId: undefined, collectionId: undefined, }); diff --git a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model.ts b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model.ts index ed82a6968078..f135d5ca478b 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model.ts @@ -1,10 +1,18 @@ export const Unassigned = "unassigned"; +export type RoutedVaultFilterItemType = + | "favorites" + | "login" + | "card" + | "identity" + | "note" + | "trash"; + export interface RoutedVaultFilterModel { collectionId?: string; folderId?: string; organizationId?: string; - type?: string; + type?: RoutedVaultFilterItemType; organizationIdParamType?: "path" | "query"; } From c274133ab0762760b82b1cd7d3315d654f620b8e Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Mon, 20 Feb 2023 14:41:28 +0100 Subject: [PATCH 23/30] [EC-775] feat: use new `All` variable for collections --- .../routed-vault-filter-bridge.service.ts | 15 +++++++++++---- .../models/routed-vault-filter-bridge.model.ts | 12 +++++++++++- .../shared/models/routed-vault-filter.model.ts | 2 ++ 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts index 2d4f923299dd..47358c4c5f8b 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts @@ -6,7 +6,11 @@ import { ServiceUtils } from "@bitwarden/common/misc/serviceUtils"; import { TreeNode } from "@bitwarden/common/models/domain/tree-node"; import { RoutedVaultFilterBridge } from "../shared/models/routed-vault-filter-bridge.model"; -import { RoutedVaultFilterModel, Unassigned } from "../shared/models/routed-vault-filter.model"; +import { + RoutedVaultFilterModel, + Unassigned, + All, +} from "../shared/models/routed-vault-filter.model"; import { VaultFilter } from "../shared/models/vault-filter.model"; import { CipherTypeFilter } from "../shared/models/vault-filter.type"; @@ -46,9 +50,12 @@ export class RoutedVaultFilterBridgeService { collectionTree, null ); - } - - if (filter.collectionId !== undefined && filter.collectionId !== Unassigned) { + } else if (filter.collectionId !== undefined && filter.collectionId === All) { + legacyFilter.selectedCollectionNode = ServiceUtils.getTreeNodeObject( + collectionTree, + "AllCollections" + ); + } else if (filter.collectionId !== undefined) { legacyFilter.selectedCollectionNode = ServiceUtils.getTreeNodeObject( collectionTree, filter.collectionId diff --git a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts index 2e63485965dd..0a81c965f912 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts @@ -4,6 +4,7 @@ import { CipherType } from "@bitwarden/common/vault/enums/cipher-type"; import { RoutedVaultFilterBridgeService } from "../../services/routed-vault-filter-bridge.service"; import { + All, RoutedVaultFilterItemType, RoutedVaultFilterModel, Unassigned, @@ -90,7 +91,16 @@ export class RoutedVaultFilterBridge implements VaultFilter { return this.legacyFilter.selectedCollectionNode; } set selectedCollectionNode(value: TreeNode) { - const collectionId = value != null && value.node.id === null ? Unassigned : value?.node.id; + let collectionId: string | undefined; + + if (value != null && value.node.id === null) { + collectionId = Unassigned; + } else if (value?.node.id === "AllCollections") { + collectionId = All; + } else { + collectionId = value?.node.id; + } + this.bridgeService.navigate({ ...this.routedFilter, collectionId, diff --git a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model.ts b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model.ts index f135d5ca478b..5bc9d1606f7e 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model.ts @@ -1,5 +1,7 @@ export const Unassigned = "unassigned"; +export const All = "all"; + export type RoutedVaultFilterItemType = | "favorites" | "login" From 7dbe8dc261912e09865fd90aeb1cd9ed52d5030d Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Mon, 20 Feb 2023 14:53:25 +0100 Subject: [PATCH 24/30] [EC-775] feat: return `RouterLink` compatible link from `createRoute` --- .../routed-vault-filter-bridge.service.ts | 4 +-- .../services/routed-vault-filter.service.ts | 26 ++++++++++--------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts index 47358c4c5f8b..773e5311c4eb 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts @@ -107,7 +107,7 @@ export class RoutedVaultFilterBridgeService { } navigate(filter: RoutedVaultFilterModel) { - const route = this.routedVaultFilterService.createRoute(filter); - this.router.navigate(route.commands, route.extras); + const [commands, extras] = this.routedVaultFilterService.createRoute(filter); + this.router.navigate(commands, extras); } } diff --git a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts index 19a444bd65ff..02ae4f4e7839 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts @@ -54,20 +54,22 @@ export class RoutedVaultFilterService implements OnDestroy { * @param filter Filter values that should be applied to the URL. * @returns route that can be used with Router or RouterLink */ - createRoute(filter: RoutedVaultFilterModel): { commands: any[]; extras?: NavigationExtras } { - return { - commands: [], - extras: { - queryParams: { - collectionId: filter.collectionId ?? null, - folderId: filter.folderId ?? null, - organizationId: - filter.organizationIdParamType === "path" ? null : filter.organizationId ?? null, - type: filter.type ?? null, - }, - queryParamsHandling: "merge", + createRoute(filter: RoutedVaultFilterModel): [commands: any[], extras?: NavigationExtras] { + const commands = + filter.organizationIdParamType === "path" + ? ["/", "organizations", filter.organizationId] + : []; + const extras: NavigationExtras = { + queryParams: { + collectionId: filter.collectionId ?? null, + folderId: filter.folderId ?? null, + organizationId: + filter.organizationIdParamType === "path" ? null : filter.organizationId ?? null, + type: filter.type ?? null, }, + queryParamsHandling: "merge", }; + return [commands, extras]; } ngOnDestroy(): void { From 3a996e9a9081dca0767c5e53f7ac3dc5ef9c270d Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Mon, 20 Feb 2023 14:59:56 +0100 Subject: [PATCH 25/30] [EC-775] feat: add ordId path support to `createRoute` --- .../vault-filter/services/routed-vault-filter.service.ts | 2 +- .../shared/models/routed-vault-filter-bridge.model.ts | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts index 02ae4f4e7839..447e4461e26b 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts @@ -57,7 +57,7 @@ export class RoutedVaultFilterService implements OnDestroy { createRoute(filter: RoutedVaultFilterModel): [commands: any[], extras?: NavigationExtras] { const commands = filter.organizationIdParamType === "path" - ? ["/", "organizations", filter.organizationId] + ? ["/", "organizations", filter.organizationId, "vault"] : []; const extras: NavigationExtras = { queryParams: { diff --git a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts index 0a81c965f912..17b574866040 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts @@ -137,7 +137,10 @@ export class RoutedVaultFilterBridge implements VaultFilter { ...this.routedFilter, collectionId: undefined, folderId: undefined, - organizationId: undefined, + organizationId: + this.routedFilter.organizationIdParamType === "path" + ? this.routedFilter.organizationId + : undefined, type: undefined, }); } From a69b064b58df989467d2bb52ecac6fa17f47c832 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Mon, 20 Feb 2023 16:17:17 +0100 Subject: [PATCH 26/30] [EC-775] fix: interpret params differently in org vault This is needed due to how defaults used to work when using `state-in-code`. We really want to get rid of this type of logic going forward. --- .../routed-vault-filter-bridge.service.ts | 130 +++++++++++------- .../services/routed-vault-filter.service.ts | 2 +- .../routed-vault-filter-bridge.model.ts | 28 +++- .../models/routed-vault-filter.model.ts | 3 +- 4 files changed, 105 insertions(+), 58 deletions(-) diff --git a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts index 773e5311c4eb..83c905f09f0e 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts @@ -45,60 +45,90 @@ export class RoutedVaultFilterBridgeService { map(([filter, collectionTree, folderTree, organizationTree, cipherTypeTree]) => { const legacyFilter = new VaultFilter(); - if (filter.collectionId !== undefined && filter.collectionId === Unassigned) { - legacyFilter.selectedCollectionNode = ServiceUtils.getTreeNodeObject( - collectionTree, - null - ); - } else if (filter.collectionId !== undefined && filter.collectionId === All) { - legacyFilter.selectedCollectionNode = ServiceUtils.getTreeNodeObject( - collectionTree, - "AllCollections" - ); - } else if (filter.collectionId !== undefined) { - legacyFilter.selectedCollectionNode = ServiceUtils.getTreeNodeObject( - collectionTree, - filter.collectionId - ); - } - - if (filter.folderId !== undefined && filter.folderId === Unassigned) { - legacyFilter.selectedFolderNode = ServiceUtils.getTreeNodeObject(folderTree, null); - } - - if (filter.folderId !== undefined && filter.folderId !== Unassigned) { - legacyFilter.selectedFolderNode = ServiceUtils.getTreeNodeObject( - folderTree, - filter.folderId - ); - } + if (filter.organizationIdParamType === "path") { + if (filter.collectionId === undefined && filter.type === undefined) { + legacyFilter.selectedCollectionNode = ServiceUtils.getTreeNodeObject( + collectionTree, + "AllCollections" + ); + } else if (filter.collectionId !== undefined && filter.collectionId === Unassigned) { + legacyFilter.selectedCollectionNode = ServiceUtils.getTreeNodeObject( + collectionTree, + null + ); + } else if (filter.collectionId !== undefined) { + legacyFilter.selectedCollectionNode = ServiceUtils.getTreeNodeObject( + collectionTree, + filter.collectionId + ); + } - if (filter.organizationId !== undefined) { - legacyFilter.selectedOrganizationNode = ServiceUtils.getTreeNodeObject( - organizationTree, - filter.organizationId - ); - } + if (filter.collectionId === undefined && filter.type === All) { + legacyFilter.selectedCipherTypeNode = ServiceUtils.getTreeNodeObject( + cipherTypeTree, + "AllItems" + ); + } else if (filter.type !== undefined && filter.type === "trash") { + legacyFilter.selectedCipherTypeNode = new TreeNode( + { id: "trash", name: "", type: "trash", icon: "" }, + null + ); + } else if (filter.type !== undefined && filter.type !== "trash") { + legacyFilter.selectedCipherTypeNode = ServiceUtils.getTreeNodeObject( + cipherTypeTree, + filter.type + ); + } + } else { + if (filter.collectionId !== undefined && filter.collectionId === Unassigned) { + legacyFilter.selectedCollectionNode = ServiceUtils.getTreeNodeObject( + collectionTree, + null + ); + } else if (filter.collectionId !== undefined && filter.collectionId === All) { + legacyFilter.selectedCollectionNode = ServiceUtils.getTreeNodeObject( + collectionTree, + "AllCollections" + ); + } else if (filter.collectionId !== undefined) { + legacyFilter.selectedCollectionNode = ServiceUtils.getTreeNodeObject( + collectionTree, + filter.collectionId + ); + } - if (filter.type === undefined) { - legacyFilter.selectedCipherTypeNode = ServiceUtils.getTreeNodeObject( - cipherTypeTree, - "AllItems" - ); - } + if (filter.folderId !== undefined && filter.folderId === Unassigned) { + legacyFilter.selectedFolderNode = ServiceUtils.getTreeNodeObject(folderTree, null); + } else if (filter.folderId !== undefined && filter.folderId !== Unassigned) { + legacyFilter.selectedFolderNode = ServiceUtils.getTreeNodeObject( + folderTree, + filter.folderId + ); + } - if (filter.type !== undefined && filter.type === "trash") { - legacyFilter.selectedCipherTypeNode = new TreeNode( - { id: "trash", name: "", type: "trash", icon: "" }, - null - ); - } + if (filter.organizationId !== undefined) { + legacyFilter.selectedOrganizationNode = ServiceUtils.getTreeNodeObject( + organizationTree, + filter.organizationId + ); + } - if (filter.type !== undefined && filter.type !== "trash") { - legacyFilter.selectedCipherTypeNode = ServiceUtils.getTreeNodeObject( - cipherTypeTree, - filter.type - ); + if (filter.type === undefined) { + legacyFilter.selectedCipherTypeNode = ServiceUtils.getTreeNodeObject( + cipherTypeTree, + "AllItems" + ); + } else if (filter.type !== undefined && filter.type === "trash") { + legacyFilter.selectedCipherTypeNode = new TreeNode( + { id: "trash", name: "", type: "trash", icon: "" }, + null + ); + } else if (filter.type !== undefined && filter.type !== "trash") { + legacyFilter.selectedCipherTypeNode = ServiceUtils.getTreeNodeObject( + cipherTypeTree, + filter.type + ); + } } return new RoutedVaultFilterBridge(filter, legacyFilter, this); diff --git a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts index 447e4461e26b..54735f9a9fe8 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts @@ -29,7 +29,7 @@ export class RoutedVaultFilterService implements OnDestroy { map(([params, queryParams]) => { const type = queryParams.get("type"); let safeType: RoutedVaultFilterItemType | undefined = undefined; - if (["favorites", "login", "card", "identity", "note"].includes(type)) { + if (["favorites", "login", "card", "identity", "note", "trash", "all"].includes(type)) { safeType = type as RoutedVaultFilterItemType; } diff --git a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts index 17b574866040..e81d76a3b426 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts @@ -61,16 +61,24 @@ export class RoutedVaultFilterBridge implements VaultFilter { return this.legacyFilter.selectedCipherTypeNode; } set selectedCipherTypeNode(value: TreeNode) { - const type = value?.node.id === "AllItems" ? null : value?.node.id; + let type: RoutedVaultFilterItemType | undefined; - let safeType: RoutedVaultFilterItemType | undefined = undefined; - if (["favorites", "login", "card", "identity", "note", "trash"].includes(type)) { - safeType = type as RoutedVaultFilterItemType; + if (value?.node.id === "AllItems" && this.routedFilter.organizationIdParamType === "path") { + type = "all"; + } else if ( + value?.node.id === "AllItems" && + this.routedFilter.organizationIdParamType === "query" + ) { + type = undefined; + } else if ( + ["favorites", "login", "card", "identity", "note", "trash", "all"].includes(value?.node.id) + ) { + type = value?.node.id as RoutedVaultFilterItemType; } this.bridgeService.navigate({ ...this.routedFilter, - type: safeType, + type, folderId: undefined, collectionId: undefined, }); @@ -95,7 +103,15 @@ export class RoutedVaultFilterBridge implements VaultFilter { if (value != null && value.node.id === null) { collectionId = Unassigned; - } else if (value?.node.id === "AllCollections") { + } else if ( + value?.node.id === "AllCollections" && + this.routedFilter.organizationIdParamType === "path" + ) { + collectionId = undefined; + } else if ( + value?.node.id === "AllCollections" && + this.routedFilter.organizationIdParamType === "query" + ) { collectionId = All; } else { collectionId = value?.node.id; diff --git a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model.ts b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model.ts index 5bc9d1606f7e..4901e2394bf4 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model.ts @@ -8,7 +8,8 @@ export type RoutedVaultFilterItemType = | "card" | "identity" | "note" - | "trash"; + | "trash" + | typeof All; // TODO: Remove `All` when moving to vertical navigation. export interface RoutedVaultFilterModel { collectionId?: string; From 61395dcadeba6333d4449df12121fdab16bf1934 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Mon, 20 Feb 2023 16:20:54 +0100 Subject: [PATCH 27/30] [EC-775] doc: clarify `createRoute` --- .../vault-filter/services/routed-vault-filter.service.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts index 54735f9a9fe8..824fa5d96a86 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts @@ -48,8 +48,10 @@ export class RoutedVaultFilterService implements OnDestroy { } /** - * Create a route that can be used with Router or RouterLink. - * To subscribe to changes use {@link RoutedVaultFilterService.filter$} + * Create a route that can be used to modify filters with Router or RouterLink. + * This method is specifically built to leave other query parameters untouched, + * meaning that navigation will only affect filters and not e.g. `cipherId`. + * To subscribe to changes use {@link RoutedVaultFilterService.filter$}. * * @param filter Filter values that should be applied to the URL. * @returns route that can be used with Router or RouterLink From 2529205424d4fda6f766a33e753e0e8c1d6fc84f Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Mon, 20 Feb 2023 16:32:35 +0100 Subject: [PATCH 28/30] [EC-775] fix: better `type` typing --- .../services/routed-vault-filter.service.ts | 11 ++++------- .../models/routed-vault-filter-bridge.model.ts | 7 +++---- .../shared/models/routed-vault-filter.model.ts | 16 ++++++++-------- 3 files changed, 15 insertions(+), 19 deletions(-) diff --git a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts index 824fa5d96a86..100fc23ad068 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts @@ -3,7 +3,7 @@ import { ActivatedRoute, NavigationExtras } from "@angular/router"; import { combineLatest, map, Observable, Subject, takeUntil } from "rxjs"; import { - RoutedVaultFilterItemType, + isRoutedVaultFilterItemType, RoutedVaultFilterModel, } from "../shared/models/routed-vault-filter.model"; @@ -27,11 +27,8 @@ export class RoutedVaultFilterService implements OnDestroy { constructor(activatedRoute: ActivatedRoute) { this.filter$ = combineLatest([activatedRoute.paramMap, activatedRoute.queryParamMap]).pipe( map(([params, queryParams]) => { - const type = queryParams.get("type"); - let safeType: RoutedVaultFilterItemType | undefined = undefined; - if (["favorites", "login", "card", "identity", "note", "trash", "all"].includes(type)) { - safeType = type as RoutedVaultFilterItemType; - } + const unsafeType = queryParams.get("type"); + const type = isRoutedVaultFilterItemType(unsafeType) ? unsafeType : undefined; return { collectionId: queryParams.get("collectionId") ?? undefined, @@ -40,7 +37,7 @@ export class RoutedVaultFilterService implements OnDestroy { params.get("organizationId") ?? queryParams.get("organizationId") ?? undefined, organizationIdParamType: params.get("organizationId") != undefined ? ("path" as const) : ("query" as const), - type: safeType, + type, }; }), takeUntil(this.onDestroy) diff --git a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts index e81d76a3b426..c2a25b6751fa 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts @@ -5,6 +5,7 @@ import { RoutedVaultFilterBridgeService } from "../../services/routed-vault-filt import { All, + isRoutedVaultFilterItemType, RoutedVaultFilterItemType, RoutedVaultFilterModel, Unassigned, @@ -70,10 +71,8 @@ export class RoutedVaultFilterBridge implements VaultFilter { this.routedFilter.organizationIdParamType === "query" ) { type = undefined; - } else if ( - ["favorites", "login", "card", "identity", "note", "trash", "all"].includes(value?.node.id) - ) { - type = value?.node.id as RoutedVaultFilterItemType; + } else if (isRoutedVaultFilterItemType(value?.node.id)) { + type = value?.node.id; } this.bridgeService.navigate({ diff --git a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model.ts b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model.ts index 4901e2394bf4..63a4c4ef0f0b 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model.ts @@ -2,14 +2,14 @@ export const Unassigned = "unassigned"; export const All = "all"; -export type RoutedVaultFilterItemType = - | "favorites" - | "login" - | "card" - | "identity" - | "note" - | "trash" - | typeof All; // TODO: Remove `All` when moving to vertical navigation. +// TODO: Remove `All` when moving to vertical navigation. +const itemTypes = ["favorites", "login", "card", "identity", "note", "trash", All] as const; + +export type RoutedVaultFilterItemType = typeof itemTypes[number]; + +export function isRoutedVaultFilterItemType(value: unknown): value is RoutedVaultFilterItemType { + return itemTypes.includes(value as any); +} export interface RoutedVaultFilterModel { collectionId?: string; From 10a8b32063e5d116b3e1a1a317a09695577408fc Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Tue, 21 Feb 2023 10:02:07 +0100 Subject: [PATCH 29/30] [EC-775] feat: remove support for path navigation It's better that we circle back to this type of navigationt when we're working on the VVR and have more knowledge about how this is supposed to work. --- .../services/routed-vault-filter.service.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts index 100fc23ad068..26578605dc03 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts @@ -50,14 +50,17 @@ export class RoutedVaultFilterService implements OnDestroy { * meaning that navigation will only affect filters and not e.g. `cipherId`. * To subscribe to changes use {@link RoutedVaultFilterService.filter$}. * + * Note: + * This method currently only supports changing filters that are stored + * in query parameters. This means that {@link RoutedVaultFilterModel.organizationId} + * will be ignored if {@link RoutedVaultFilterModel.organizationIdParamType} + * is set to `path`. + * * @param filter Filter values that should be applied to the URL. * @returns route that can be used with Router or RouterLink */ createRoute(filter: RoutedVaultFilterModel): [commands: any[], extras?: NavigationExtras] { - const commands = - filter.organizationIdParamType === "path" - ? ["/", "organizations", filter.organizationId, "vault"] - : []; + const commands: string[] = []; const extras: NavigationExtras = { queryParams: { collectionId: filter.collectionId ?? null, From e1803bf2de92ab0ae39323f769c488639caa1345 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Tue, 21 Feb 2023 10:16:36 +0100 Subject: [PATCH 30/30] [EC-775] fix: refactor bridge service to improve readability Refactor follows feedback from PR review --- .../routed-vault-filter-bridge.service.ts | 204 ++++++++++-------- 1 file changed, 120 insertions(+), 84 deletions(-) diff --git a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts index 83c905f09f0e..a532ecff7806 100644 --- a/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts +++ b/apps/web/src/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts @@ -12,7 +12,12 @@ import { All, } from "../shared/models/routed-vault-filter.model"; import { VaultFilter } from "../shared/models/vault-filter.model"; -import { CipherTypeFilter } from "../shared/models/vault-filter.type"; +import { + CipherTypeFilter, + CollectionFilter, + FolderFilter, + OrganizationFilter, +} from "../shared/models/vault-filter.type"; import { VaultFilterService } from "./abstractions/vault-filter.service"; import { RoutedVaultFilterService } from "./routed-vault-filter.service"; @@ -43,93 +48,15 @@ export class RoutedVaultFilterBridgeService { legacyVaultFilterService.cipherTypeTree$, ]).pipe( map(([filter, collectionTree, folderTree, organizationTree, cipherTypeTree]) => { - const legacyFilter = new VaultFilter(); - - if (filter.organizationIdParamType === "path") { - if (filter.collectionId === undefined && filter.type === undefined) { - legacyFilter.selectedCollectionNode = ServiceUtils.getTreeNodeObject( - collectionTree, - "AllCollections" - ); - } else if (filter.collectionId !== undefined && filter.collectionId === Unassigned) { - legacyFilter.selectedCollectionNode = ServiceUtils.getTreeNodeObject( + const legacyFilter = isAdminConsole(filter) + ? createLegacyFilterForAdminConsole(filter, collectionTree, cipherTypeTree) + : createLegacyFilterForEndUser( + filter, collectionTree, - null - ); - } else if (filter.collectionId !== undefined) { - legacyFilter.selectedCollectionNode = ServiceUtils.getTreeNodeObject( - collectionTree, - filter.collectionId - ); - } - - if (filter.collectionId === undefined && filter.type === All) { - legacyFilter.selectedCipherTypeNode = ServiceUtils.getTreeNodeObject( - cipherTypeTree, - "AllItems" - ); - } else if (filter.type !== undefined && filter.type === "trash") { - legacyFilter.selectedCipherTypeNode = new TreeNode( - { id: "trash", name: "", type: "trash", icon: "" }, - null - ); - } else if (filter.type !== undefined && filter.type !== "trash") { - legacyFilter.selectedCipherTypeNode = ServiceUtils.getTreeNodeObject( - cipherTypeTree, - filter.type - ); - } - } else { - if (filter.collectionId !== undefined && filter.collectionId === Unassigned) { - legacyFilter.selectedCollectionNode = ServiceUtils.getTreeNodeObject( - collectionTree, - null - ); - } else if (filter.collectionId !== undefined && filter.collectionId === All) { - legacyFilter.selectedCollectionNode = ServiceUtils.getTreeNodeObject( - collectionTree, - "AllCollections" - ); - } else if (filter.collectionId !== undefined) { - legacyFilter.selectedCollectionNode = ServiceUtils.getTreeNodeObject( - collectionTree, - filter.collectionId - ); - } - - if (filter.folderId !== undefined && filter.folderId === Unassigned) { - legacyFilter.selectedFolderNode = ServiceUtils.getTreeNodeObject(folderTree, null); - } else if (filter.folderId !== undefined && filter.folderId !== Unassigned) { - legacyFilter.selectedFolderNode = ServiceUtils.getTreeNodeObject( folderTree, - filter.folderId - ); - } - - if (filter.organizationId !== undefined) { - legacyFilter.selectedOrganizationNode = ServiceUtils.getTreeNodeObject( organizationTree, - filter.organizationId - ); - } - - if (filter.type === undefined) { - legacyFilter.selectedCipherTypeNode = ServiceUtils.getTreeNodeObject( - cipherTypeTree, - "AllItems" + cipherTypeTree ); - } else if (filter.type !== undefined && filter.type === "trash") { - legacyFilter.selectedCipherTypeNode = new TreeNode( - { id: "trash", name: "", type: "trash", icon: "" }, - null - ); - } else if (filter.type !== undefined && filter.type !== "trash") { - legacyFilter.selectedCipherTypeNode = ServiceUtils.getTreeNodeObject( - cipherTypeTree, - filter.type - ); - } - } return new RoutedVaultFilterBridge(filter, legacyFilter, this); }) @@ -141,3 +68,112 @@ export class RoutedVaultFilterBridgeService { this.router.navigate(commands, extras); } } + +/** + * Check if the filtering is being done as part of admin console. + * Admin console can be identified by checking if the `organizationId` + * is part of the path. + * + * @param filter Model to check if origin is admin console + * @returns true if filtering being done as part of admin console + */ +function isAdminConsole(filter: RoutedVaultFilterModel) { + return filter.organizationIdParamType === "path"; +} + +function createLegacyFilterForAdminConsole( + filter: RoutedVaultFilterModel, + collectionTree: TreeNode, + cipherTypeTree: TreeNode +): VaultFilter { + const legacyFilter = new VaultFilter(); + + if (filter.collectionId === undefined && filter.type === undefined) { + legacyFilter.selectedCollectionNode = ServiceUtils.getTreeNodeObject( + collectionTree, + "AllCollections" + ); + } else if (filter.collectionId !== undefined && filter.collectionId === Unassigned) { + legacyFilter.selectedCollectionNode = ServiceUtils.getTreeNodeObject(collectionTree, null); + } else if (filter.collectionId !== undefined) { + legacyFilter.selectedCollectionNode = ServiceUtils.getTreeNodeObject( + collectionTree, + filter.collectionId + ); + } + + if (filter.collectionId === undefined && filter.type === All) { + legacyFilter.selectedCipherTypeNode = ServiceUtils.getTreeNodeObject( + cipherTypeTree, + "AllItems" + ); + } else if (filter.type !== undefined && filter.type === "trash") { + legacyFilter.selectedCipherTypeNode = new TreeNode( + { id: "trash", name: "", type: "trash", icon: "" }, + null + ); + } else if (filter.type !== undefined && filter.type !== "trash") { + legacyFilter.selectedCipherTypeNode = ServiceUtils.getTreeNodeObject( + cipherTypeTree, + filter.type + ); + } + + return legacyFilter; +} + +function createLegacyFilterForEndUser( + filter: RoutedVaultFilterModel, + collectionTree: TreeNode, + folderTree: TreeNode, + organizationTree: TreeNode, + cipherTypeTree: TreeNode +): VaultFilter { + const legacyFilter = new VaultFilter(); + + if (filter.collectionId !== undefined && filter.collectionId === Unassigned) { + legacyFilter.selectedCollectionNode = ServiceUtils.getTreeNodeObject(collectionTree, null); + } else if (filter.collectionId !== undefined && filter.collectionId === All) { + legacyFilter.selectedCollectionNode = ServiceUtils.getTreeNodeObject( + collectionTree, + "AllCollections" + ); + } else if (filter.collectionId !== undefined) { + legacyFilter.selectedCollectionNode = ServiceUtils.getTreeNodeObject( + collectionTree, + filter.collectionId + ); + } + + if (filter.folderId !== undefined && filter.folderId === Unassigned) { + legacyFilter.selectedFolderNode = ServiceUtils.getTreeNodeObject(folderTree, null); + } else if (filter.folderId !== undefined && filter.folderId !== Unassigned) { + legacyFilter.selectedFolderNode = ServiceUtils.getTreeNodeObject(folderTree, filter.folderId); + } + + if (filter.organizationId !== undefined) { + legacyFilter.selectedOrganizationNode = ServiceUtils.getTreeNodeObject( + organizationTree, + filter.organizationId + ); + } + + if (filter.type === undefined) { + legacyFilter.selectedCipherTypeNode = ServiceUtils.getTreeNodeObject( + cipherTypeTree, + "AllItems" + ); + } else if (filter.type !== undefined && filter.type === "trash") { + legacyFilter.selectedCipherTypeNode = new TreeNode( + { id: "trash", name: "", type: "trash", icon: "" }, + null + ); + } else if (filter.type !== undefined && filter.type !== "trash") { + legacyFilter.selectedCipherTypeNode = ServiceUtils.getTreeNodeObject( + cipherTypeTree, + filter.type + ); + } + + return legacyFilter; +}