diff --git a/jslib b/jslib index 2b647df001..f4066b4f58 160000 --- a/jslib +++ b/jslib @@ -1 +1 @@ -Subproject commit 2b647df001f6bdfbeb6f272a0b7b56396ac9564d +Subproject commit f4066b4f58af2e6e4ce82275b72a2a8501f25e78 diff --git a/src/app/modules/vault-filter/organization-vault-filter.component.ts b/src/app/modules/vault-filter/organization-vault-filter.component.ts new file mode 100644 index 0000000000..bda2f2ca78 --- /dev/null +++ b/src/app/modules/vault-filter/organization-vault-filter.component.ts @@ -0,0 +1,28 @@ +import { Component, Input } from "@angular/core"; + +import { Organization } from "jslib-common/models/domain/organization"; + +import { VaultFilterComponent } from "./vault-filter.component"; + +@Component({ + selector: "app-organization-vault-filter", + templateUrl: "vault-filter.component.html", +}) +export class OrganizationVaultFilterComponent extends VaultFilterComponent { + hideOrganizations = true; + hideFavorites = true; + hideFolders = true; + + organization: Organization; + + async initCollections() { + if (this.organization.canEditAnyCollection) { + return await this.vaultFilterService.buildAdminCollections(this.organization.id); + } + return await this.vaultFilterService.buildCollections(this.organization.id); + } + + async reloadCollectionsAndFolders() { + this.collections = await this.initCollections(); + } +} diff --git a/src/app/modules/vault-filter/vault-filter.component.html b/src/app/modules/vault-filter/vault-filter.component.html index e707474436..bbe75c61e6 100644 --- a/src/app/modules/vault-filter/vault-filter.component.html +++ b/src/app/modules/vault-filter/vault-filter.component.html @@ -27,7 +27,6 @@ appAutofocus />
(); searchPlaceholder: string; searchText = ""; - organization: Organization; - - constructor(vaultFilterService: VaultFilterService) { + constructor(protected vaultFilterService: VaultFilterService) { + // This empty constructor is required to provide the web vaultFilterService subclass to super() + // TODO: refactor this to use proper dependency injection super(vaultFilterService); } searchTextChanged() { this.onSearchTextChanged.emit(this.searchText); } - - async initCollections() { - return await this.vaultFilterService.buildCollections(this.organization?.id); - } } diff --git a/src/app/modules/vault-filter/vault-filter.module.ts b/src/app/modules/vault-filter/vault-filter.module.ts index bf7cb3d517..1ec6c5d7db 100644 --- a/src/app/modules/vault-filter/vault-filter.module.ts +++ b/src/app/modules/vault-filter/vault-filter.module.ts @@ -1,13 +1,5 @@ import { NgModule } from "@angular/core"; -import { VaultFilterService } from "jslib-angular/modules/vault-filter/vault-filter.service"; -import { CipherService } from "jslib-common/abstractions/cipher.service"; -import { CollectionService } from "jslib-common/abstractions/collection.service"; -import { FolderService } from "jslib-common/abstractions/folder.service"; -import { OrganizationService } from "jslib-common/abstractions/organization.service"; -import { PolicyService } from "jslib-common/abstractions/policy.service"; -import { StateService } from "jslib-common/abstractions/state.service"; - import { SharedModule } from "../shared.module"; import { CollectionFilterComponent } from "./components/collection-filter.component"; @@ -17,7 +9,9 @@ import { OrganizationFilterComponent } from "./components/organization-filter.co import { OrganizationOptionsComponent } from "./components/organization-options.component"; import { StatusFilterComponent } from "./components/status-filter.component"; import { TypeFilterComponent } from "./components/type-filter.component"; +import { OrganizationVaultFilterComponent } from "./organization-vault-filter.component"; import { VaultFilterComponent } from "./vault-filter.component"; +import { VaultFilterService } from "./vault-filter.service"; @NgModule({ imports: [SharedModule], @@ -29,22 +23,10 @@ import { VaultFilterComponent } from "./vault-filter.component"; OrganizationOptionsComponent, StatusFilterComponent, TypeFilterComponent, + OrganizationVaultFilterComponent, LinkSsoComponent, ], - exports: [VaultFilterComponent], - providers: [ - { - provide: VaultFilterService, - useClass: VaultFilterService, - deps: [ - StateService, - OrganizationService, - FolderService, - CipherService, - CollectionService, - PolicyService, - ], - }, - ], + exports: [VaultFilterComponent, OrganizationVaultFilterComponent], + providers: [VaultFilterService], }) export class VaultFilterModule {} diff --git a/src/app/modules/vault-filter/vault-filter.service.ts b/src/app/modules/vault-filter/vault-filter.service.ts index 01b9d50b58..a8e23e7d8c 100644 --- a/src/app/modules/vault-filter/vault-filter.service.ts +++ b/src/app/modules/vault-filter/vault-filter.service.ts @@ -1,3 +1,54 @@ -import { VaultFilterService as BaseVaultFilterService } from "jslib-angular/modules/vault-filter/vault-filter.service"; +import { Injectable } from "@angular/core"; -export class VaultFilterService extends BaseVaultFilterService {} +import { DynamicTreeNode } from "jslib-angular/modules/vault-filter/models/dynamic-tree-node.model"; +import { VaultFilterService as BaseVaultFilterService } from "jslib-angular/modules/vault-filter/vault-filter.service"; +import { ApiService } from "jslib-common/abstractions/api.service"; +import { CipherService } from "jslib-common/abstractions/cipher.service"; +import { CollectionService } from "jslib-common/abstractions/collection.service"; +import { FolderService } from "jslib-common/abstractions/folder.service"; +import { OrganizationService } from "jslib-common/abstractions/organization.service"; +import { PolicyService } from "jslib-common/abstractions/policy.service"; +import { StateService } from "jslib-common/abstractions/state.service"; +import { CollectionData } from "jslib-common/models/data/collectionData"; +import { Collection } from "jslib-common/models/domain/collection"; +import { CollectionDetailsResponse } from "jslib-common/models/response/collectionResponse"; +import { CollectionView } from "jslib-common/models/view/collectionView"; + +@Injectable() +export class VaultFilterService extends BaseVaultFilterService { + constructor( + stateService: StateService, + organizationService: OrganizationService, + folderService: FolderService, + cipherService: CipherService, + collectionService: CollectionService, + policyService: PolicyService, + protected apiService: ApiService + ) { + super( + stateService, + organizationService, + folderService, + cipherService, + collectionService, + policyService + ); + } + + async buildAdminCollections(organizationId: string) { + let result: CollectionView[] = []; + const collectionResponse = await this.apiService.getCollections(organizationId); + if (collectionResponse?.data != null && collectionResponse.data.length) { + const collectionDomains = collectionResponse.data.map( + (r: CollectionDetailsResponse) => new Collection(new CollectionData(r)) + ); + result = await this.collectionService.decryptMany(collectionDomains); + } + + const nestedCollections = await this.collectionService.getAllNested(result); + return new DynamicTreeNode({ + fullList: result, + nestedList: nestedCollections, + }); + } +} diff --git a/src/app/modules/vault/modules/organization-vault/organization-vault.component.html b/src/app/modules/vault/modules/organization-vault/organization-vault.component.html index a187d3ba2d..a8837011e0 100644 --- a/src/app/modules/vault/modules/organization-vault/organization-vault.component.html +++ b/src/app/modules/vault/modules/organization-vault/organization-vault.component.html @@ -4,15 +4,12 @@
- + >
diff --git a/src/app/modules/vault/modules/organization-vault/organization-vault.component.ts b/src/app/modules/vault/modules/organization-vault/organization-vault.component.ts index c7edcbe0e3..59d97bf736 100644 --- a/src/app/modules/vault/modules/organization-vault/organization-vault.component.ts +++ b/src/app/modules/vault/modules/organization-vault/organization-vault.component.ts @@ -29,7 +29,7 @@ import { AddEditComponent } from "../../../../organizations/vault/add-edit.compo import { AttachmentsComponent } from "../../../../organizations/vault/attachments.component"; import { CiphersComponent } from "../../../../organizations/vault/ciphers.component"; import { CollectionsComponent } from "../../../../organizations/vault/collections.component"; -import { VaultFilterComponent } from "../../../vault-filter/vault-filter.component"; +import { OrganizationVaultFilterComponent } from "../../../vault-filter/organization-vault-filter.component"; import { VaultService } from "../../vault.service"; const BroadcasterSubscriptionId = "OrgVaultComponent"; @@ -39,7 +39,8 @@ const BroadcasterSubscriptionId = "OrgVaultComponent"; templateUrl: "organization-vault.component.html", }) export class OrganizationVaultComponent implements OnInit, OnDestroy { - @ViewChild("vaultFilter", { static: true }) vaultFilterComponent: VaultFilterComponent; + @ViewChild("vaultFilter", { static: true }) + vaultFilterComponent: OrganizationVaultFilterComponent; @ViewChild(CiphersComponent, { static: true }) ciphersComponent: CiphersComponent; @ViewChild("attachments", { read: ViewContainerRef, static: true }) attachmentsModalRef: ViewContainerRef; @@ -57,6 +58,11 @@ export class OrganizationVaultComponent implements OnInit, OnDestroy { trashCleanupWarning: string = null; activeFilter: VaultFilter = new VaultFilter(); + // This is a hack to avoid redundant api calls that fetch OrganizationVaultFilterComponent collections + // When it makes sense to do so we should leverage some other communication method for change events that isn't directly tied to the query param for organizationId + // i.e. exposing the VaultFiltersService to the OrganizationSwitcherComponent to make relevant updates from a change event instead of just depending on the router + firstLoaded = true; + constructor( private route: ActivatedRoute, private organizationService: OrganizationService, @@ -95,11 +101,7 @@ export class OrganizationVaultComponent implements OnInit, OnDestroy { case "syncCompleted": if (message.successfully) { await Promise.all([ - this.vaultFilterComponent.reloadCollectionsAndFolders( - new VaultFilter({ - selectedOrganizationId: this.organization.id, - } as Partial) - ), + this.vaultFilterComponent.reloadCollectionsAndFolders(), this.ciphersComponent.refresh(), ]); this.changeDetectorRef.detectChanges(); @@ -109,9 +111,12 @@ export class OrganizationVaultComponent implements OnInit, OnDestroy { }); }); } - await this.vaultFilterComponent.reloadCollectionsAndFolders( - new VaultFilter({ selectedOrganizationId: this.organization.id } as Partial) - ); + + if (!this.firstLoaded) { + await this.vaultFilterComponent.reloadCollectionsAndFolders(); + } + this.firstLoaded = false; + await this.ciphersComponent.reload(); if (qParams.viewEvents != null) {