mirror of
https://github.com/bitwarden/browser
synced 2026-01-31 00:33:33 +00:00
moved 'collections' outside of 'organizations'
This commit is contained in:
@@ -1,30 +1,13 @@
|
||||
@if (show()) {
|
||||
@for (organization of organizations().children ?? []; track organization.node.id) {
|
||||
@if (getOrgCollections(organization.node.id)?.children?.length > 0) {
|
||||
<bit-nav-group
|
||||
[icon]="organization.node.icon"
|
||||
[class.active]="organization.node.id === activeFilter()?.organizationId"
|
||||
[text]="organization.node.name"
|
||||
variant="tree"
|
||||
[appA11yTitle]="organization.node.name"
|
||||
(click)="applyFilter(organization)"
|
||||
>
|
||||
@if (!hideCollections() && collections() != null) {
|
||||
@for (c of getOrgCollections(organization.node.id)?.children ?? []; track c.node.id) {
|
||||
<app-collection-filter [activeFilter]="activeFilter()" [collection]="c" />
|
||||
}
|
||||
}
|
||||
</bit-nav-group>
|
||||
} @else {
|
||||
<bit-nav-item
|
||||
[icon]="organization.node.icon"
|
||||
[forceActiveStyles]="organization.node.id === activeFilter()?.organizationId"
|
||||
[text]="organization.node.name"
|
||||
variant="tree"
|
||||
[appA11yTitle]="organization.node.name"
|
||||
(click)="applyFilter(organization)"
|
||||
/>
|
||||
}
|
||||
<bit-nav-item
|
||||
[icon]="organization.node.icon"
|
||||
[forceActiveStyles]="organization.node.id === activeFilter()?.organizationId"
|
||||
[text]="organization.node.name"
|
||||
variant="tree"
|
||||
[appA11yTitle]="organization.node.name"
|
||||
(click)="applyFilter(organization)"
|
||||
/>
|
||||
@if (!organization.node.enabled) {
|
||||
<span class="tw-ml-auto">
|
||||
<i
|
||||
|
||||
@@ -2,32 +2,28 @@ import { Component, computed, input, inject } from "@angular/core";
|
||||
|
||||
import { DisplayMode } from "@bitwarden/angular/vault/vault-filter/models/display-mode";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { OrganizationId } from "@bitwarden/common/types/guid";
|
||||
import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node";
|
||||
import { ToastService, NavigationModule, A11yTitleDirective } from "@bitwarden/components";
|
||||
import { I18nPipe } from "@bitwarden/ui-common";
|
||||
import { OrganizationFilter, VaultFilter, CollectionFilter } from "@bitwarden/vault";
|
||||
|
||||
import { CollectionFilterComponent } from "./collection-filter.component";
|
||||
import { OrganizationFilter, VaultFilter, VaultFilterServiceAbstraction } from "@bitwarden/vault";
|
||||
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
||||
@Component({
|
||||
selector: "app-organization-filter",
|
||||
templateUrl: "organization-filter.component.html",
|
||||
imports: [A11yTitleDirective, NavigationModule, CollectionFilterComponent, I18nPipe],
|
||||
imports: [A11yTitleDirective, NavigationModule, I18nPipe],
|
||||
})
|
||||
export class OrganizationFilterComponent {
|
||||
private toastService: ToastService = inject(ToastService);
|
||||
private i18nService: I18nService = inject(I18nService);
|
||||
private vaultFilterService: VaultFilterServiceAbstraction = inject(VaultFilterServiceAbstraction);
|
||||
|
||||
protected readonly hide = input(false);
|
||||
protected readonly organizations = input<TreeNode<OrganizationFilter>>();
|
||||
protected readonly activeFilter = input<VaultFilter>();
|
||||
protected readonly activeOrganizationDataOwnership = input<boolean>(false);
|
||||
protected readonly activeSingleOrganizationPolicy = input<boolean>(false);
|
||||
protected readonly hideCollections = input(false);
|
||||
protected readonly collections = input<TreeNode<CollectionFilter>>();
|
||||
|
||||
protected readonly show = computed(() => {
|
||||
const hiddenDisplayModes: DisplayMode[] = [
|
||||
@@ -65,36 +61,11 @@ export class OrganizationFilterComponent {
|
||||
return;
|
||||
}
|
||||
|
||||
this.vaultFilterService.setOrganizationFilter(organization.node);
|
||||
const filter = this.activeFilter();
|
||||
|
||||
if (filter) {
|
||||
filter.selectedOrganizationNode = organization;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly collectionsByOrganization = computed(() => {
|
||||
const collections = this.collections();
|
||||
const map = new Map<OrganizationId, TreeNode<CollectionFilter>>();
|
||||
const orgs = this.organizations()?.children;
|
||||
|
||||
if (!collections || !orgs) {
|
||||
return map;
|
||||
}
|
||||
|
||||
for (const org of orgs) {
|
||||
const filteredCollections = collections.children.filter(
|
||||
(node) => node.node.organizationId === org.node.id,
|
||||
);
|
||||
|
||||
const headNode = new TreeNode<CollectionFilter>(collections.node, null);
|
||||
headNode.children = filteredCollections;
|
||||
map.set(org.node.id, headNode);
|
||||
}
|
||||
|
||||
return map;
|
||||
});
|
||||
|
||||
protected getOrgCollections(organizationId: OrganizationId): TreeNode<CollectionFilter> {
|
||||
return this.collectionsByOrganization().get(organizationId) ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,18 +5,34 @@
|
||||
} @else {
|
||||
<bit-nav-group icon="bwi-vault" [text]="'vault' | i18n" route="new-vault">
|
||||
<app-organization-filter
|
||||
[activeFilter]="activeFilter"
|
||||
[activeFilter]="activeFilter()"
|
||||
[organizations]="organizations$ | async"
|
||||
[activeOrganizationDataOwnership]="activeOrganizationDataOwnershipPolicy"
|
||||
[activeSingleOrganizationPolicy]="activeSingleOrganizationPolicy"
|
||||
[collections]="collections$ | async"
|
||||
/>
|
||||
<app-type-filter [activeFilter]="activeFilter" [cipherTypes]="cipherTypes$ | async" />
|
||||
<app-status-filter [hideArchive]="!showArchiveVaultFilter" [activeFilter]="activeFilter" />
|
||||
<bit-nav-group icon="bwi-folder" [text]="'folders' | i18n" variant="tree">
|
||||
<app-type-filter [activeFilter]="activeFilter()" [cipherTypes]="cipherTypes$ | async" />
|
||||
<app-status-filter [hideArchive]="!showArchiveVaultFilter" [activeFilter]="activeFilter()" />
|
||||
@if (showCollectionsFilter()) {
|
||||
<bit-nav-group
|
||||
icon="bwi-collection"
|
||||
[text]="'collections' | i18n"
|
||||
variant="tree"
|
||||
[appA11yTitle]="'collections' | i18n"
|
||||
>
|
||||
@for (collection of (collections$ | async)?.children ?? []; track collection.node.id) {
|
||||
<app-collection-filter [activeFilter]="activeFilter()" [collection]="collection" />
|
||||
}
|
||||
</bit-nav-group>
|
||||
}
|
||||
<bit-nav-group
|
||||
icon="bwi-folder"
|
||||
[text]="'folders' | i18n"
|
||||
variant="tree"
|
||||
[appA11yTitle]="'folders' | i18n"
|
||||
>
|
||||
@for (folder of (folders$ | async)?.children ?? []; track folder.node.id) {
|
||||
<app-folder-filter
|
||||
[activeFilter]="activeFilter"
|
||||
[activeFilter]="activeFilter()"
|
||||
[folder]="folder"
|
||||
(onEditFolder)="editFolder($event)"
|
||||
/>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { Component, inject, OnInit, output } from "@angular/core";
|
||||
import { Component, inject, OnInit, output, computed, signal } from "@angular/core";
|
||||
import { firstValueFrom, Observable, Subject, takeUntil } from "rxjs";
|
||||
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
@@ -11,7 +11,7 @@ import { CipherArchiveService } from "@bitwarden/common/vault/abstractions/ciphe
|
||||
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
|
||||
import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service";
|
||||
import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node";
|
||||
import { NavigationModule, DialogService } from "@bitwarden/components";
|
||||
import { NavigationModule, DialogService, A11yTitleDirective } from "@bitwarden/components";
|
||||
import { I18nPipe } from "@bitwarden/ui-common";
|
||||
import {
|
||||
OrganizationFilter,
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
import { DesktopRoutedVaultFilterBridgeService } from "../../../../app/services/desktop-routed-vault-filter-bridge.service";
|
||||
import { DesktopPremiumUpgradePromptService } from "../../../../services/desktop-premium-upgrade-prompt.service";
|
||||
|
||||
import { CollectionFilterComponent } from "./filters/collection-filter.component";
|
||||
import { FolderFilterComponent } from "./filters/folder-filter.component";
|
||||
import { OrganizationFilterComponent } from "./filters/organization-filter.component";
|
||||
import { StatusFilterComponent } from "./filters/status-filter.component";
|
||||
@@ -43,7 +44,9 @@ import { TypeFilterComponent } from "./filters/type-filter.component";
|
||||
OrganizationFilterComponent,
|
||||
StatusFilterComponent,
|
||||
TypeFilterComponent,
|
||||
CollectionFilterComponent,
|
||||
FolderFilterComponent,
|
||||
A11yTitleDirective,
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
@@ -62,7 +65,7 @@ export class VaultFilterComponent implements OnInit {
|
||||
private dialogService: DialogService = inject(DialogService);
|
||||
private componentIsDestroyed$ = new Subject<boolean>();
|
||||
|
||||
protected activeFilter: VaultFilter;
|
||||
protected readonly activeFilter = signal<VaultFilter | null>(null);
|
||||
protected onFilterChange = output<VaultFilter>();
|
||||
|
||||
private activeUserId: UserId;
|
||||
@@ -75,6 +78,10 @@ export class VaultFilterComponent implements OnInit {
|
||||
protected folders$: Observable<TreeNode<FolderFilter>>;
|
||||
protected cipherTypes$: Observable<TreeNode<CipherTypeFilter>>;
|
||||
|
||||
protected readonly showCollectionsFilter = computed<boolean>(() => {
|
||||
return this.organizations$ != null && !this.activeFilter()?.isMyVaultSelected;
|
||||
});
|
||||
|
||||
private async setActivePolicies() {
|
||||
this.activeOrganizationDataOwnershipPolicy = await firstValueFrom(
|
||||
this.policyService.policyAppliesToUser$(
|
||||
@@ -108,7 +115,7 @@ export class VaultFilterComponent implements OnInit {
|
||||
this.routedVaultFilterBridgeService.activeFilter$
|
||||
.pipe(takeUntil(this.componentIsDestroyed$))
|
||||
.subscribe((filter) => {
|
||||
this.activeFilter = filter;
|
||||
this.activeFilter.set(filter);
|
||||
});
|
||||
|
||||
this.isLoaded = true;
|
||||
|
||||
Reference in New Issue
Block a user