diff --git a/apps/web/src/app/modules/vault-filter/organization-vault-filter.component.ts b/apps/web/src/app/modules/vault-filter/organization-vault-filter.component.ts
new file mode 100644
index 0000000000..bda2f2ca78
--- /dev/null
+++ b/apps/web/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/apps/web/src/app/modules/vault-filter/vault-filter.component.html b/apps/web/src/app/modules/vault-filter/vault-filter.component.html
index e707474436..bbe75c61e6 100644
--- a/apps/web/src/app/modules/vault-filter/vault-filter.component.html
+++ b/apps/web/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/apps/web/src/app/modules/vault-filter/vault-filter.module.ts b/apps/web/src/app/modules/vault-filter/vault-filter.module.ts
index bf7cb3d517..1ec6c5d7db 100644
--- a/apps/web/src/app/modules/vault-filter/vault-filter.module.ts
+++ b/apps/web/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/apps/web/src/app/modules/vault-filter/vault-filter.service.ts b/apps/web/src/app/modules/vault-filter/vault-filter.service.ts
index 01b9d50b58..a8e23e7d8c 100644
--- a/apps/web/src/app/modules/vault-filter/vault-filter.service.ts
+++ b/apps/web/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/apps/web/src/app/modules/vault/modules/organization-vault/organization-vault.component.html b/apps/web/src/app/modules/vault/modules/organization-vault/organization-vault.component.html
index a187d3ba2d..a8837011e0 100644
--- a/apps/web/src/app/modules/vault/modules/organization-vault/organization-vault.component.html
+++ b/apps/web/src/app/modules/vault/modules/organization-vault/organization-vault.component.html
@@ -4,15 +4,12 @@
diff --git a/apps/web/src/app/modules/vault/modules/organization-vault/organization-vault.component.ts b/apps/web/src/app/modules/vault/modules/organization-vault/organization-vault.component.ts
index c7edcbe0e3..83c182ea08 100644
--- a/apps/web/src/app/modules/vault/modules/organization-vault/organization-vault.component.ts
+++ b/apps/web/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 = true;
+
await this.ciphersComponent.reload();
if (qParams.viewEvents != null) {