From e486e2d73c35424af7ea279f591432cf8ede9bf5 Mon Sep 17 00:00:00 2001 From: Isaac Ivins Date: Tue, 25 Nov 2025 09:35:29 -0500 Subject: [PATCH 01/56] apply patch from non forked repo --- apps/desktop/src/app/app-routing.module.ts | 25 ++++- .../app/layout/desktop-layout.component.html | 4 + .../layout/desktop-layout.component.spec.ts | 61 +++++++++++ .../app/layout/desktop-layout.component.ts | 14 +++ .../src/app/layout/desktop-layout.module.ts | 14 +++ .../layout/desktop-side-nav.component.html | 3 + .../layout/desktop-side-nav.component.spec.ts | 74 +++++++++++++ .../app/layout/desktop-side-nav.component.ts | 15 +++ .../src/app/layout/user-layout.component.html | 10 ++ .../app/layout/user-layout.component.spec.ts | 102 ++++++++++++++++++ .../src/app/layout/user-layout.component.ts | 19 ++++ .../app/tools/send-v2/sends.component.spec.ts | 22 ++++ .../src/app/tools/send-v2/sends.component.ts | 10 ++ .../app/vault-v3/vault.component.spec.ts | 22 ++++ .../src/vault/app/vault-v3/vault.component.ts | 10 ++ libs/common/src/enums/feature-flag.enum.ts | 6 ++ 16 files changed, 410 insertions(+), 1 deletion(-) create mode 100644 apps/desktop/src/app/layout/desktop-layout.component.html create mode 100644 apps/desktop/src/app/layout/desktop-layout.component.spec.ts create mode 100644 apps/desktop/src/app/layout/desktop-layout.component.ts create mode 100644 apps/desktop/src/app/layout/desktop-layout.module.ts create mode 100644 apps/desktop/src/app/layout/desktop-side-nav.component.html create mode 100644 apps/desktop/src/app/layout/desktop-side-nav.component.spec.ts create mode 100644 apps/desktop/src/app/layout/desktop-side-nav.component.ts create mode 100644 apps/desktop/src/app/layout/user-layout.component.html create mode 100644 apps/desktop/src/app/layout/user-layout.component.spec.ts create mode 100644 apps/desktop/src/app/layout/user-layout.component.ts create mode 100644 apps/desktop/src/app/tools/send-v2/sends.component.spec.ts create mode 100644 apps/desktop/src/app/tools/send-v2/sends.component.ts create mode 100644 apps/desktop/src/vault/app/vault-v3/vault.component.spec.ts create mode 100644 apps/desktop/src/vault/app/vault-v3/vault.component.ts diff --git a/apps/desktop/src/app/app-routing.module.ts b/apps/desktop/src/app/app-routing.module.ts index b6e86ba19ff..fd06640737c 100644 --- a/apps/desktop/src/app/app-routing.module.ts +++ b/apps/desktop/src/app/app-routing.module.ts @@ -14,6 +14,7 @@ import { } from "@bitwarden/angular/auth/guards"; import { ChangePasswordComponent } from "@bitwarden/angular/auth/password-management/change-password"; import { SetInitialPasswordComponent } from "@bitwarden/angular/auth/password-management/set-initial-password/set-initial-password.component"; +import { canAccessFeature } from "@bitwarden/angular/platform/guard/feature-flag.guard"; import { DevicesIcon, RegistrationUserAddIcon, @@ -39,15 +40,19 @@ import { TwoFactorAuthGuard, NewDeviceVerificationComponent, } from "@bitwarden/auth/angular"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { AnonLayoutWrapperComponent, AnonLayoutWrapperData } from "@bitwarden/components"; import { LockComponent, ConfirmKeyConnectorDomainComponent } from "@bitwarden/key-management-ui"; import { maxAccountsGuardFn } from "../auth/guards/max-accounts.guard"; import { RemovePasswordComponent } from "../key-management/key-connector/remove-password.component"; import { VaultV2Component } from "../vault/app/vault/vault-v2.component"; +import { VaultComponent } from "../vault/app/vault-v3/vault.component"; import { Fido2PlaceholderComponent } from "./components/fido2placeholder.component"; +import { UserLayoutComponent } from "./layout/user-layout.component"; import { SendComponent } from "./tools/send/send.component"; +import { SendsComponent } from "./tools/send-v2/sends.component"; /** * Data properties acceptable for use in route objects in the desktop @@ -99,7 +104,10 @@ const routes: Routes = [ { path: "vault", component: VaultV2Component, - canActivate: [authGuard], + canActivate: [ + authGuard, + canAccessFeature(FeatureFlag.DesktopUiMigrationMilestone1, false, "new-vault", false), + ], }, { path: "send", @@ -325,6 +333,21 @@ const routes: Routes = [ }, ], }, + { + path: "", + component: UserLayoutComponent, + canActivate: [authGuard], + children: [ + { + path: "new-vault", + component: VaultComponent, + }, + { + path: "new-sends", + component: SendsComponent, + }, + ], + }, ]; @NgModule({ diff --git a/apps/desktop/src/app/layout/desktop-layout.component.html b/apps/desktop/src/app/layout/desktop-layout.component.html new file mode 100644 index 00000000000..71a760735ca --- /dev/null +++ b/apps/desktop/src/app/layout/desktop-layout.component.html @@ -0,0 +1,4 @@ + + + + diff --git a/apps/desktop/src/app/layout/desktop-layout.component.spec.ts b/apps/desktop/src/app/layout/desktop-layout.component.spec.ts new file mode 100644 index 00000000000..cc2f7e58dfb --- /dev/null +++ b/apps/desktop/src/app/layout/desktop-layout.component.spec.ts @@ -0,0 +1,61 @@ +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { RouterModule } from "@angular/router"; +import { mock } from "jest-mock-extended"; + +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { NavigationModule } from "@bitwarden/components"; + +import { DesktopLayoutComponent } from "./desktop-layout.component"; + +Object.defineProperty(window, "matchMedia", { + writable: true, + value: jest.fn().mockImplementation((query) => ({ + matches: true, + media: query, + onchange: null, + addListener: jest.fn(), + removeListener: jest.fn(), + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + dispatchEvent: jest.fn(), + })), +}); + +describe("DesktopLayoutComponent", () => { + let component: DesktopLayoutComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [DesktopLayoutComponent, RouterModule.forRoot([]), NavigationModule], + providers: [ + { + provide: I18nService, + useValue: mock(), + }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(DesktopLayoutComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("creates component", () => { + expect(component).toBeTruthy(); + }); + + it("renders bit-layout component", () => { + const compiled = fixture.nativeElement; + const layoutElement = compiled.querySelector("bit-layout"); + + expect(layoutElement).toBeTruthy(); + }); + + it("supports content projection for side-nav", () => { + const compiled = fixture.nativeElement; + const ngContent = compiled.querySelectorAll("ng-content"); + + expect(ngContent).toBeTruthy(); + }); +}); diff --git a/apps/desktop/src/app/layout/desktop-layout.component.ts b/apps/desktop/src/app/layout/desktop-layout.component.ts new file mode 100644 index 00000000000..695f3db37ab --- /dev/null +++ b/apps/desktop/src/app/layout/desktop-layout.component.ts @@ -0,0 +1,14 @@ +import { CommonModule } from "@angular/common"; +import { ChangeDetectionStrategy, Component } from "@angular/core"; +import { RouterModule } from "@angular/router"; + +import { LayoutComponent, NavigationModule } from "@bitwarden/components"; + +@Component({ + selector: "app-desktop-layout", + standalone: true, + imports: [CommonModule, RouterModule, LayoutComponent, NavigationModule], + templateUrl: "./desktop-layout.component.html", + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class DesktopLayoutComponent {} diff --git a/apps/desktop/src/app/layout/desktop-layout.module.ts b/apps/desktop/src/app/layout/desktop-layout.module.ts new file mode 100644 index 00000000000..efef167c95e --- /dev/null +++ b/apps/desktop/src/app/layout/desktop-layout.module.ts @@ -0,0 +1,14 @@ +import { NgModule } from "@angular/core"; + +import { NavigationModule } from "@bitwarden/components"; + +import { DesktopLayoutComponent } from "./desktop-layout.component"; +import { DesktopSideNavComponent } from "./desktop-side-nav.component"; + +@NgModule({ + imports: [DesktopLayoutComponent, DesktopSideNavComponent], + exports: [NavigationModule, DesktopLayoutComponent, DesktopSideNavComponent], + declarations: [], + providers: [], +}) +export class DesktopLayoutModule {} diff --git a/apps/desktop/src/app/layout/desktop-side-nav.component.html b/apps/desktop/src/app/layout/desktop-side-nav.component.html new file mode 100644 index 00000000000..ede3f9131b7 --- /dev/null +++ b/apps/desktop/src/app/layout/desktop-side-nav.component.html @@ -0,0 +1,3 @@ + + + diff --git a/apps/desktop/src/app/layout/desktop-side-nav.component.spec.ts b/apps/desktop/src/app/layout/desktop-side-nav.component.spec.ts new file mode 100644 index 00000000000..59e743f430a --- /dev/null +++ b/apps/desktop/src/app/layout/desktop-side-nav.component.spec.ts @@ -0,0 +1,74 @@ +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { mock } from "jest-mock-extended"; + +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { NavigationModule } from "@bitwarden/components"; + +import { DesktopSideNavComponent } from "./desktop-side-nav.component"; + +Object.defineProperty(window, "matchMedia", { + writable: true, + value: jest.fn().mockImplementation((query) => ({ + matches: true, + media: query, + onchange: null, + addListener: jest.fn(), + removeListener: jest.fn(), + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + dispatchEvent: jest.fn(), + })), +}); + +describe("DesktopSideNavComponent", () => { + let component: DesktopSideNavComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [DesktopSideNavComponent, NavigationModule], + providers: [ + { + provide: I18nService, + useValue: mock(), + }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(DesktopSideNavComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("creates component", () => { + expect(component).toBeTruthy(); + }); + + it("renders bit-side-nav component", () => { + const compiled = fixture.nativeElement; + const sideNavElement = compiled.querySelector("bit-side-nav"); + + expect(sideNavElement).toBeTruthy(); + }); + + it("uses primary variant by default", () => { + expect(component.variant()).toBe("primary"); + }); + + it("accepts variant input", () => { + fixture.componentRef.setInput("variant", "secondary"); + fixture.detectChanges(); + + expect(component.variant()).toBe("secondary"); + }); + + it("passes variant to bit-side-nav", () => { + fixture.componentRef.setInput("variant", "secondary"); + fixture.detectChanges(); + + const compiled = fixture.nativeElement; + const sideNavElement = compiled.querySelector("bit-side-nav"); + + expect(sideNavElement.getAttribute("ng-reflect-variant")).toBe("secondary"); + }); +}); diff --git a/apps/desktop/src/app/layout/desktop-side-nav.component.ts b/apps/desktop/src/app/layout/desktop-side-nav.component.ts new file mode 100644 index 00000000000..c74e9bc989a --- /dev/null +++ b/apps/desktop/src/app/layout/desktop-side-nav.component.ts @@ -0,0 +1,15 @@ +import { CommonModule } from "@angular/common"; +import { ChangeDetectionStrategy, Component, input } from "@angular/core"; + +import { NavigationModule, SideNavVariant } from "@bitwarden/components"; + +@Component({ + selector: "app-desktop-side-nav", + standalone: true, + templateUrl: "desktop-side-nav.component.html", + imports: [CommonModule, NavigationModule], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class DesktopSideNavComponent { + readonly variant = input("primary"); +} diff --git a/apps/desktop/src/app/layout/user-layout.component.html b/apps/desktop/src/app/layout/user-layout.component.html new file mode 100644 index 00000000000..ff3ba579425 --- /dev/null +++ b/apps/desktop/src/app/layout/user-layout.component.html @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/apps/desktop/src/app/layout/user-layout.component.spec.ts b/apps/desktop/src/app/layout/user-layout.component.spec.ts new file mode 100644 index 00000000000..00aa8486529 --- /dev/null +++ b/apps/desktop/src/app/layout/user-layout.component.spec.ts @@ -0,0 +1,102 @@ +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { RouterModule } from "@angular/router"; +import { mock } from "jest-mock-extended"; + +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; + +import { DesktopLayoutModule } from "./desktop-layout.module"; +import { UserLayoutComponent } from "./user-layout.component"; + +Object.defineProperty(window, "matchMedia", { + writable: true, + value: jest.fn().mockImplementation((query) => ({ + matches: true, + media: query, + onchange: null, + addListener: jest.fn(), + removeListener: jest.fn(), + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + dispatchEvent: jest.fn(), + })), +}); + +describe("UserLayoutComponent", () => { + let component: UserLayoutComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [UserLayoutComponent, RouterModule.forRoot([]), DesktopLayoutModule], + providers: [ + { + provide: I18nService, + useValue: mock(), + }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(UserLayoutComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("creates component", () => { + expect(component).toBeTruthy(); + }); + + it("renders desktop layout", () => { + const compiled = fixture.nativeElement; + const layoutElement = compiled.querySelector("app-desktop-layout"); + + expect(layoutElement).toBeTruthy(); + }); + + it("renders desktop side nav", () => { + const compiled = fixture.nativeElement; + const sideNavElement = compiled.querySelector("app-desktop-side-nav"); + + expect(sideNavElement).toBeTruthy(); + }); + + it("renders logo with correct properties", () => { + const compiled = fixture.nativeElement; + const logoElement = compiled.querySelector("bit-nav-logo"); + + expect(logoElement).toBeTruthy(); + expect(logoElement.getAttribute("route")).toBe("."); + }); + + it("renders vault navigation item", () => { + const compiled = fixture.nativeElement; + const navItems = compiled.querySelectorAll("bit-nav-item"); + const vaultItem = Array.from(navItems).find( + (item) => (item as Element).getAttribute("icon") === "bwi-vault", + ) as Element | undefined; + + expect(vaultItem).toBeTruthy(); + expect(vaultItem?.getAttribute("route")).toBe("new-vault"); + }); + + it("renders send navigation item", () => { + const compiled = fixture.nativeElement; + const navItems = compiled.querySelectorAll("bit-nav-item"); + const sendItem = Array.from(navItems).find( + (item) => (item as Element).getAttribute("icon") === "bwi-send", + ) as Element | undefined; + + expect(sendItem).toBeTruthy(); + expect(sendItem?.getAttribute("route")).toBe("new-sends"); + }); + + it("renders router outlet", () => { + const compiled = fixture.nativeElement; + const routerOutlet = compiled.querySelector("router-outlet"); + + expect(routerOutlet).toBeTruthy(); + }); + + it("has logo property set", () => { + expect(component["logo"]).toBeDefined(); + }); +}); diff --git a/apps/desktop/src/app/layout/user-layout.component.ts b/apps/desktop/src/app/layout/user-layout.component.ts new file mode 100644 index 00000000000..81031b19d9a --- /dev/null +++ b/apps/desktop/src/app/layout/user-layout.component.ts @@ -0,0 +1,19 @@ +import { CommonModule } from "@angular/common"; +import { ChangeDetectionStrategy, Component } from "@angular/core"; +import { RouterModule } from "@angular/router"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { PasswordManagerLogo } from "@bitwarden/assets/svg"; + +import { DesktopLayoutModule } from "./desktop-layout.module"; + +@Component({ + selector: "app-user-layout", + standalone: true, + templateUrl: "user-layout.component.html", + imports: [CommonModule, RouterModule, JslibModule, DesktopLayoutModule], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class UserLayoutComponent { + protected readonly logo = PasswordManagerLogo; +} diff --git a/apps/desktop/src/app/tools/send-v2/sends.component.spec.ts b/apps/desktop/src/app/tools/send-v2/sends.component.spec.ts new file mode 100644 index 00000000000..2156bc80b1d --- /dev/null +++ b/apps/desktop/src/app/tools/send-v2/sends.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from "@angular/core/testing"; + +import { SendsComponent } from "./sends.component"; + +describe("SendsComponent", () => { + let component: SendsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [SendsComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(SendsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("creates component", () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/desktop/src/app/tools/send-v2/sends.component.ts b/apps/desktop/src/app/tools/send-v2/sends.component.ts new file mode 100644 index 00000000000..c8e9ac4c1d9 --- /dev/null +++ b/apps/desktop/src/app/tools/send-v2/sends.component.ts @@ -0,0 +1,10 @@ +import { Component, ChangeDetectionStrategy } from "@angular/core"; + +@Component({ + selector: "app-sends-v2", + standalone: true, + imports: [], + template: "

Sends V2 Component

", + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class SendsComponent {} diff --git a/apps/desktop/src/vault/app/vault-v3/vault.component.spec.ts b/apps/desktop/src/vault/app/vault-v3/vault.component.spec.ts new file mode 100644 index 00000000000..89ba05055f8 --- /dev/null +++ b/apps/desktop/src/vault/app/vault-v3/vault.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from "@angular/core/testing"; + +import { VaultComponent } from "./vault.component"; + +describe("VaultComponent", () => { + let component: VaultComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [VaultComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(VaultComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("creates component", () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/desktop/src/vault/app/vault-v3/vault.component.ts b/apps/desktop/src/vault/app/vault-v3/vault.component.ts new file mode 100644 index 00000000000..0a4a38742fc --- /dev/null +++ b/apps/desktop/src/vault/app/vault-v3/vault.component.ts @@ -0,0 +1,10 @@ +import { ChangeDetectionStrategy, Component } from "@angular/core"; + +@Component({ + selector: "app-vault-v3", + standalone: true, + imports: [], + template: "

Vault V3 Component

", + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class VaultComponent {} diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index 17d5f4e9df5..068a8f1e410 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -72,6 +72,9 @@ export enum FeatureFlag { /* Innovation */ PM19148_InnovationArchive = "pm-19148-innovation-archive", + + /* Desktop */ + DesktopUiMigrationMilestone1 = "desktop-ui-migration-milestone-1", } export type AllowedFeatureFlagTypes = boolean | number | string; @@ -150,6 +153,9 @@ export const DefaultFeatureFlagValue = { /* Innovation */ [FeatureFlag.PM19148_InnovationArchive]: FALSE, + + /* Desktop */ + [FeatureFlag.DesktopUiMigrationMilestone1]: FALSE, } satisfies Record; export type DefaultFeatureFlagValueType = typeof DefaultFeatureFlagValue; From af4c062fdaf849acf64f2e333e3dca6e6cfa4cce Mon Sep 17 00:00:00 2001 From: Isaac Ivins Date: Tue, 25 Nov 2025 10:40:46 -0500 Subject: [PATCH 02/56] updated locals for sidenav --- apps/desktop/src/app/layout/user-layout.component.html | 2 +- apps/desktop/src/locales/en/messages.json | 7 ++++++- libs/common/src/enums/feature-flag.enum.ts | 3 +++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/apps/desktop/src/app/layout/user-layout.component.html b/apps/desktop/src/app/layout/user-layout.component.html index ff3ba579425..91fa0a14f19 100644 --- a/apps/desktop/src/app/layout/user-layout.component.html +++ b/apps/desktop/src/app/layout/user-layout.component.html @@ -2,7 +2,7 @@ - + diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json index 6bef882d970..f6f078611c9 100644 --- a/apps/desktop/src/locales/en/messages.json +++ b/apps/desktop/src/locales/en/messages.json @@ -2228,6 +2228,10 @@ "contactInfo": { "message": "Contact information" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "All Sends", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2991,7 +2995,8 @@ "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." }, "vault": { - "message": "Vault" + "message": "Vault", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Log in with master password" diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index 068a8f1e410..47bb3f33c4a 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -166,6 +166,9 @@ export function getFeatureFlagValue( serverConfig: ServerConfig | null, flag: Flag, ) { + if (flag === FeatureFlag.DesktopUiMigrationMilestone1) { + return true; + } if (serverConfig?.featureStates == null || serverConfig.featureStates[flag] == null) { return DefaultFeatureFlagValue[flag]; } From d12a31596e4e9f1f1e2b770e5492933fef346256 Mon Sep 17 00:00:00 2001 From: Isaac Ivins Date: Tue, 25 Nov 2025 11:12:46 -0500 Subject: [PATCH 03/56] removed temp ff in dev --- libs/common/src/enums/feature-flag.enum.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index 47bb3f33c4a..068a8f1e410 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -166,9 +166,6 @@ export function getFeatureFlagValue( serverConfig: ServerConfig | null, flag: Flag, ) { - if (flag === FeatureFlag.DesktopUiMigrationMilestone1) { - return true; - } if (serverConfig?.featureStates == null || serverConfig.featureStates[flag] == null) { return DefaultFeatureFlagValue[flag]; } From 41e5da0928d6ebb6afaf3b91b523c5117102b8e3 Mon Sep 17 00:00:00 2001 From: Isaac Ivins Date: Tue, 25 Nov 2025 13:09:27 -0500 Subject: [PATCH 04/56] remove abstraction, standalone true --- apps/desktop/src/app/app-routing.module.ts | 4 +- .../app/layout/desktop-layout.component.html | 10 +- .../app/layout/desktop-layout.component.ts | 12 ++- .../src/app/layout/desktop-layout.module.ts | 14 --- .../app/layout/desktop-side-nav.component.ts | 1 - .../src/app/layout/user-layout.component.html | 10 -- .../app/layout/user-layout.component.spec.ts | 102 ------------------ .../src/app/layout/user-layout.component.ts | 19 ---- .../src/app/tools/send-v2/sends.component.ts | 1 - .../src/vault/app/vault-v3/vault.component.ts | 1 - 10 files changed, 18 insertions(+), 156 deletions(-) delete mode 100644 apps/desktop/src/app/layout/desktop-layout.module.ts delete mode 100644 apps/desktop/src/app/layout/user-layout.component.html delete mode 100644 apps/desktop/src/app/layout/user-layout.component.spec.ts delete mode 100644 apps/desktop/src/app/layout/user-layout.component.ts diff --git a/apps/desktop/src/app/app-routing.module.ts b/apps/desktop/src/app/app-routing.module.ts index fd06640737c..0f5b8250e0f 100644 --- a/apps/desktop/src/app/app-routing.module.ts +++ b/apps/desktop/src/app/app-routing.module.ts @@ -50,7 +50,7 @@ import { VaultV2Component } from "../vault/app/vault/vault-v2.component"; import { VaultComponent } from "../vault/app/vault-v3/vault.component"; import { Fido2PlaceholderComponent } from "./components/fido2placeholder.component"; -import { UserLayoutComponent } from "./layout/user-layout.component"; +import { DesktopLayoutComponent } from "./layout/desktop-layout.component"; import { SendComponent } from "./tools/send/send.component"; import { SendsComponent } from "./tools/send-v2/sends.component"; @@ -335,7 +335,7 @@ const routes: Routes = [ }, { path: "", - component: UserLayoutComponent, + component: DesktopLayoutComponent, canActivate: [authGuard], children: [ { diff --git a/apps/desktop/src/app/layout/desktop-layout.component.html b/apps/desktop/src/app/layout/desktop-layout.component.html index 71a760735ca..8633053d83a 100644 --- a/apps/desktop/src/app/layout/desktop-layout.component.html +++ b/apps/desktop/src/app/layout/desktop-layout.component.html @@ -1,4 +1,10 @@ - - + + + + + + + + diff --git a/apps/desktop/src/app/layout/desktop-layout.component.ts b/apps/desktop/src/app/layout/desktop-layout.component.ts index 695f3db37ab..c918da1d2ce 100644 --- a/apps/desktop/src/app/layout/desktop-layout.component.ts +++ b/apps/desktop/src/app/layout/desktop-layout.component.ts @@ -1,14 +1,18 @@ -import { CommonModule } from "@angular/common"; import { ChangeDetectionStrategy, Component } from "@angular/core"; import { RouterModule } from "@angular/router"; +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { PasswordManagerLogo } from "@bitwarden/assets/svg"; import { LayoutComponent, NavigationModule } from "@bitwarden/components"; +import { DesktopSideNavComponent } from "./desktop-side-nav.component"; + @Component({ selector: "app-desktop-layout", - standalone: true, - imports: [CommonModule, RouterModule, LayoutComponent, NavigationModule], + imports: [RouterModule, JslibModule, LayoutComponent, NavigationModule, DesktopSideNavComponent], templateUrl: "./desktop-layout.component.html", changeDetection: ChangeDetectionStrategy.OnPush, }) -export class DesktopLayoutComponent {} +export class DesktopLayoutComponent { + protected readonly logo = PasswordManagerLogo; +} diff --git a/apps/desktop/src/app/layout/desktop-layout.module.ts b/apps/desktop/src/app/layout/desktop-layout.module.ts deleted file mode 100644 index efef167c95e..00000000000 --- a/apps/desktop/src/app/layout/desktop-layout.module.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { NgModule } from "@angular/core"; - -import { NavigationModule } from "@bitwarden/components"; - -import { DesktopLayoutComponent } from "./desktop-layout.component"; -import { DesktopSideNavComponent } from "./desktop-side-nav.component"; - -@NgModule({ - imports: [DesktopLayoutComponent, DesktopSideNavComponent], - exports: [NavigationModule, DesktopLayoutComponent, DesktopSideNavComponent], - declarations: [], - providers: [], -}) -export class DesktopLayoutModule {} diff --git a/apps/desktop/src/app/layout/desktop-side-nav.component.ts b/apps/desktop/src/app/layout/desktop-side-nav.component.ts index c74e9bc989a..05298cb1367 100644 --- a/apps/desktop/src/app/layout/desktop-side-nav.component.ts +++ b/apps/desktop/src/app/layout/desktop-side-nav.component.ts @@ -5,7 +5,6 @@ import { NavigationModule, SideNavVariant } from "@bitwarden/components"; @Component({ selector: "app-desktop-side-nav", - standalone: true, templateUrl: "desktop-side-nav.component.html", imports: [CommonModule, NavigationModule], changeDetection: ChangeDetectionStrategy.OnPush, diff --git a/apps/desktop/src/app/layout/user-layout.component.html b/apps/desktop/src/app/layout/user-layout.component.html deleted file mode 100644 index 91fa0a14f19..00000000000 --- a/apps/desktop/src/app/layout/user-layout.component.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/apps/desktop/src/app/layout/user-layout.component.spec.ts b/apps/desktop/src/app/layout/user-layout.component.spec.ts deleted file mode 100644 index 00aa8486529..00000000000 --- a/apps/desktop/src/app/layout/user-layout.component.spec.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { ComponentFixture, TestBed } from "@angular/core/testing"; -import { RouterModule } from "@angular/router"; -import { mock } from "jest-mock-extended"; - -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; - -import { DesktopLayoutModule } from "./desktop-layout.module"; -import { UserLayoutComponent } from "./user-layout.component"; - -Object.defineProperty(window, "matchMedia", { - writable: true, - value: jest.fn().mockImplementation((query) => ({ - matches: true, - media: query, - onchange: null, - addListener: jest.fn(), - removeListener: jest.fn(), - addEventListener: jest.fn(), - removeEventListener: jest.fn(), - dispatchEvent: jest.fn(), - })), -}); - -describe("UserLayoutComponent", () => { - let component: UserLayoutComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [UserLayoutComponent, RouterModule.forRoot([]), DesktopLayoutModule], - providers: [ - { - provide: I18nService, - useValue: mock(), - }, - ], - }).compileComponents(); - - fixture = TestBed.createComponent(UserLayoutComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it("creates component", () => { - expect(component).toBeTruthy(); - }); - - it("renders desktop layout", () => { - const compiled = fixture.nativeElement; - const layoutElement = compiled.querySelector("app-desktop-layout"); - - expect(layoutElement).toBeTruthy(); - }); - - it("renders desktop side nav", () => { - const compiled = fixture.nativeElement; - const sideNavElement = compiled.querySelector("app-desktop-side-nav"); - - expect(sideNavElement).toBeTruthy(); - }); - - it("renders logo with correct properties", () => { - const compiled = fixture.nativeElement; - const logoElement = compiled.querySelector("bit-nav-logo"); - - expect(logoElement).toBeTruthy(); - expect(logoElement.getAttribute("route")).toBe("."); - }); - - it("renders vault navigation item", () => { - const compiled = fixture.nativeElement; - const navItems = compiled.querySelectorAll("bit-nav-item"); - const vaultItem = Array.from(navItems).find( - (item) => (item as Element).getAttribute("icon") === "bwi-vault", - ) as Element | undefined; - - expect(vaultItem).toBeTruthy(); - expect(vaultItem?.getAttribute("route")).toBe("new-vault"); - }); - - it("renders send navigation item", () => { - const compiled = fixture.nativeElement; - const navItems = compiled.querySelectorAll("bit-nav-item"); - const sendItem = Array.from(navItems).find( - (item) => (item as Element).getAttribute("icon") === "bwi-send", - ) as Element | undefined; - - expect(sendItem).toBeTruthy(); - expect(sendItem?.getAttribute("route")).toBe("new-sends"); - }); - - it("renders router outlet", () => { - const compiled = fixture.nativeElement; - const routerOutlet = compiled.querySelector("router-outlet"); - - expect(routerOutlet).toBeTruthy(); - }); - - it("has logo property set", () => { - expect(component["logo"]).toBeDefined(); - }); -}); diff --git a/apps/desktop/src/app/layout/user-layout.component.ts b/apps/desktop/src/app/layout/user-layout.component.ts deleted file mode 100644 index 81031b19d9a..00000000000 --- a/apps/desktop/src/app/layout/user-layout.component.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { CommonModule } from "@angular/common"; -import { ChangeDetectionStrategy, Component } from "@angular/core"; -import { RouterModule } from "@angular/router"; - -import { JslibModule } from "@bitwarden/angular/jslib.module"; -import { PasswordManagerLogo } from "@bitwarden/assets/svg"; - -import { DesktopLayoutModule } from "./desktop-layout.module"; - -@Component({ - selector: "app-user-layout", - standalone: true, - templateUrl: "user-layout.component.html", - imports: [CommonModule, RouterModule, JslibModule, DesktopLayoutModule], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class UserLayoutComponent { - protected readonly logo = PasswordManagerLogo; -} diff --git a/apps/desktop/src/app/tools/send-v2/sends.component.ts b/apps/desktop/src/app/tools/send-v2/sends.component.ts index c8e9ac4c1d9..901691f03f4 100644 --- a/apps/desktop/src/app/tools/send-v2/sends.component.ts +++ b/apps/desktop/src/app/tools/send-v2/sends.component.ts @@ -2,7 +2,6 @@ import { Component, ChangeDetectionStrategy } from "@angular/core"; @Component({ selector: "app-sends-v2", - standalone: true, imports: [], template: "

Sends V2 Component

", changeDetection: ChangeDetectionStrategy.OnPush, diff --git a/apps/desktop/src/vault/app/vault-v3/vault.component.ts b/apps/desktop/src/vault/app/vault-v3/vault.component.ts index 0a4a38742fc..b29b66225c7 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault.component.ts @@ -2,7 +2,6 @@ import { ChangeDetectionStrategy, Component } from "@angular/core"; @Component({ selector: "app-vault-v3", - standalone: true, imports: [], template: "

Vault V3 Component

", changeDetection: ChangeDetectionStrategy.OnPush, From 89bcbe6a98dd1b375b2f98950a774c1d3ea35fc8 Mon Sep 17 00:00:00 2001 From: Isaac Ivins Date: Tue, 25 Nov 2025 14:38:41 -0500 Subject: [PATCH 05/56] remove 'desktop' naming, Send w/ v2, i18n Pipe --- .../src/app/layout/desktop-layout.component.ts | 6 +++--- .../src/app/layout/desktop-side-nav.component.ts | 2 +- ...s.component.spec.ts => send-v2.component.spec.ts} | 12 ++++++------ .../{sends.component.ts => send-v2.component.ts} | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) rename apps/desktop/src/app/tools/send-v2/{sends.component.spec.ts => send-v2.component.spec.ts} (54%) rename apps/desktop/src/app/tools/send-v2/{sends.component.ts => send-v2.component.ts} (86%) diff --git a/apps/desktop/src/app/layout/desktop-layout.component.ts b/apps/desktop/src/app/layout/desktop-layout.component.ts index c918da1d2ce..5059a6e4d0b 100644 --- a/apps/desktop/src/app/layout/desktop-layout.component.ts +++ b/apps/desktop/src/app/layout/desktop-layout.component.ts @@ -1,15 +1,15 @@ import { ChangeDetectionStrategy, Component } from "@angular/core"; import { RouterModule } from "@angular/router"; -import { JslibModule } from "@bitwarden/angular/jslib.module"; import { PasswordManagerLogo } from "@bitwarden/assets/svg"; import { LayoutComponent, NavigationModule } from "@bitwarden/components"; +import { I18nPipe } from "@bitwarden/ui-common"; import { DesktopSideNavComponent } from "./desktop-side-nav.component"; @Component({ - selector: "app-desktop-layout", - imports: [RouterModule, JslibModule, LayoutComponent, NavigationModule, DesktopSideNavComponent], + selector: "app-layout", + imports: [RouterModule, I18nPipe, LayoutComponent, NavigationModule, DesktopSideNavComponent], templateUrl: "./desktop-layout.component.html", changeDetection: ChangeDetectionStrategy.OnPush, }) diff --git a/apps/desktop/src/app/layout/desktop-side-nav.component.ts b/apps/desktop/src/app/layout/desktop-side-nav.component.ts index 05298cb1367..b0d9fd16fcc 100644 --- a/apps/desktop/src/app/layout/desktop-side-nav.component.ts +++ b/apps/desktop/src/app/layout/desktop-side-nav.component.ts @@ -4,7 +4,7 @@ import { ChangeDetectionStrategy, Component, input } from "@angular/core"; import { NavigationModule, SideNavVariant } from "@bitwarden/components"; @Component({ - selector: "app-desktop-side-nav", + selector: "app-side-nav", templateUrl: "desktop-side-nav.component.html", imports: [CommonModule, NavigationModule], changeDetection: ChangeDetectionStrategy.OnPush, diff --git a/apps/desktop/src/app/tools/send-v2/sends.component.spec.ts b/apps/desktop/src/app/tools/send-v2/send-v2.component.spec.ts similarity index 54% rename from apps/desktop/src/app/tools/send-v2/sends.component.spec.ts rename to apps/desktop/src/app/tools/send-v2/send-v2.component.spec.ts index 2156bc80b1d..49ed167b356 100644 --- a/apps/desktop/src/app/tools/send-v2/sends.component.spec.ts +++ b/apps/desktop/src/app/tools/send-v2/send-v2.component.spec.ts @@ -1,17 +1,17 @@ import { ComponentFixture, TestBed } from "@angular/core/testing"; -import { SendsComponent } from "./sends.component"; +import { SendsV2Component } from "./send-V2.component"; -describe("SendsComponent", () => { - let component: SendsComponent; - let fixture: ComponentFixture; +describe("SendsV2Component", () => { + let component: SendsV2Component; + let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [SendsComponent], + imports: [SendsV2Component], }).compileComponents(); - fixture = TestBed.createComponent(SendsComponent); + fixture = TestBed.createComponent(SendsV2Component); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/apps/desktop/src/app/tools/send-v2/sends.component.ts b/apps/desktop/src/app/tools/send-v2/send-v2.component.ts similarity index 86% rename from apps/desktop/src/app/tools/send-v2/sends.component.ts rename to apps/desktop/src/app/tools/send-v2/send-v2.component.ts index 901691f03f4..660610942ce 100644 --- a/apps/desktop/src/app/tools/send-v2/sends.component.ts +++ b/apps/desktop/src/app/tools/send-v2/send-v2.component.ts @@ -6,4 +6,4 @@ import { Component, ChangeDetectionStrategy } from "@angular/core"; template: "

Sends V2 Component

", changeDetection: ChangeDetectionStrategy.OnPush, }) -export class SendsComponent {} +export class SendsV2Component {} From 0ffbacc032eacd7d6463b80689e1a1f4d52acdbe Mon Sep 17 00:00:00 2001 From: Isaac Ivins Date: Tue, 25 Nov 2025 14:44:17 -0500 Subject: [PATCH 06/56] updated naming change on desktop layout --- apps/desktop/src/app/layout/desktop-layout.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/desktop/src/app/layout/desktop-layout.component.html b/apps/desktop/src/app/layout/desktop-layout.component.html index 8633053d83a..94b9201ae21 100644 --- a/apps/desktop/src/app/layout/desktop-layout.component.html +++ b/apps/desktop/src/app/layout/desktop-layout.component.html @@ -1,10 +1,10 @@ - + - + From 8016b3bfceb102a7cbb2415aa7ef6a363daebd6c Mon Sep 17 00:00:00 2001 From: Isaac Ivins Date: Tue, 25 Nov 2025 14:48:23 -0500 Subject: [PATCH 07/56] fixed import of SendsV2 --- apps/desktop/src/app/app-routing.module.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/desktop/src/app/app-routing.module.ts b/apps/desktop/src/app/app-routing.module.ts index 0f5b8250e0f..a01e5434b4a 100644 --- a/apps/desktop/src/app/app-routing.module.ts +++ b/apps/desktop/src/app/app-routing.module.ts @@ -52,7 +52,7 @@ import { VaultComponent } from "../vault/app/vault-v3/vault.component"; import { Fido2PlaceholderComponent } from "./components/fido2placeholder.component"; import { DesktopLayoutComponent } from "./layout/desktop-layout.component"; import { SendComponent } from "./tools/send/send.component"; -import { SendsComponent } from "./tools/send-v2/sends.component"; +import { SendsV2Component } from "./tools/send-v2/send-v2.component"; /** * Data properties acceptable for use in route objects in the desktop @@ -344,7 +344,7 @@ const routes: Routes = [ }, { path: "new-sends", - component: SendsComponent, + component: SendsV2Component, }, ], }, From 9db2e0ccf5a2a80a2e02812dbf48e6cf7579ce16 Mon Sep 17 00:00:00 2001 From: Isaac Ivins Date: Tue, 25 Nov 2025 16:17:59 -0500 Subject: [PATCH 08/56] SendsV2 -> SendV2 --- apps/desktop/src/app/app-routing.module.ts | 4 ++-- .../src/app/tools/send-v2/send-v2.component.spec.ts | 12 ++++++------ .../src/app/tools/send-v2/send-v2.component.ts | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/desktop/src/app/app-routing.module.ts b/apps/desktop/src/app/app-routing.module.ts index a01e5434b4a..8fab7df1cd8 100644 --- a/apps/desktop/src/app/app-routing.module.ts +++ b/apps/desktop/src/app/app-routing.module.ts @@ -52,7 +52,7 @@ import { VaultComponent } from "../vault/app/vault-v3/vault.component"; import { Fido2PlaceholderComponent } from "./components/fido2placeholder.component"; import { DesktopLayoutComponent } from "./layout/desktop-layout.component"; import { SendComponent } from "./tools/send/send.component"; -import { SendsV2Component } from "./tools/send-v2/send-v2.component"; +import { SendV2Component } from "./tools/send-v2/send-v2.component"; /** * Data properties acceptable for use in route objects in the desktop @@ -344,7 +344,7 @@ const routes: Routes = [ }, { path: "new-sends", - component: SendsV2Component, + component: SendV2Component, }, ], }, diff --git a/apps/desktop/src/app/tools/send-v2/send-v2.component.spec.ts b/apps/desktop/src/app/tools/send-v2/send-v2.component.spec.ts index 49ed167b356..73d406807fd 100644 --- a/apps/desktop/src/app/tools/send-v2/send-v2.component.spec.ts +++ b/apps/desktop/src/app/tools/send-v2/send-v2.component.spec.ts @@ -1,17 +1,17 @@ import { ComponentFixture, TestBed } from "@angular/core/testing"; -import { SendsV2Component } from "./send-V2.component"; +import { SendV2Component } from "./send-V2.component"; -describe("SendsV2Component", () => { - let component: SendsV2Component; - let fixture: ComponentFixture; +describe("SendV2Component", () => { + let component: SendV2Component; + let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [SendsV2Component], + imports: [SendV2Component], }).compileComponents(); - fixture = TestBed.createComponent(SendsV2Component); + fixture = TestBed.createComponent(SendV2Component); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/apps/desktop/src/app/tools/send-v2/send-v2.component.ts b/apps/desktop/src/app/tools/send-v2/send-v2.component.ts index 660610942ce..29b323251fc 100644 --- a/apps/desktop/src/app/tools/send-v2/send-v2.component.ts +++ b/apps/desktop/src/app/tools/send-v2/send-v2.component.ts @@ -6,4 +6,4 @@ import { Component, ChangeDetectionStrategy } from "@angular/core"; template: "

Sends V2 Component

", changeDetection: ChangeDetectionStrategy.OnPush, }) -export class SendsV2Component {} +export class SendV2Component {} From c2b6b7d505d11087f7ad5c0309405877d75a4179 Mon Sep 17 00:00:00 2001 From: Isaac Ivins Date: Tue, 25 Nov 2025 16:19:38 -0500 Subject: [PATCH 09/56] addressing last sends -> send --- apps/desktop/src/app/tools/send-v2/send-v2.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/desktop/src/app/tools/send-v2/send-v2.component.ts b/apps/desktop/src/app/tools/send-v2/send-v2.component.ts index 29b323251fc..4840cd4cce8 100644 --- a/apps/desktop/src/app/tools/send-v2/send-v2.component.ts +++ b/apps/desktop/src/app/tools/send-v2/send-v2.component.ts @@ -1,7 +1,7 @@ import { Component, ChangeDetectionStrategy } from "@angular/core"; @Component({ - selector: "app-sends-v2", + selector: "app-send-v2", imports: [], template: "

Sends V2 Component

", changeDetection: ChangeDetectionStrategy.OnPush, From 81537a82c06f526cef0108f7c88d0246f9448588 Mon Sep 17 00:00:00 2001 From: Leslie Xiong Date: Wed, 26 Nov 2025 14:34:27 -0500 Subject: [PATCH 10/56] created new vault component copied over from `vault-v2` --- .../vault/app/vault-v3/vault.component.html | 70 ++ .../src/vault/app/vault-v3/vault.component.ts | 1021 ++++++++++++++++- 2 files changed, 1086 insertions(+), 5 deletions(-) create mode 100644 apps/desktop/src/vault/app/vault-v3/vault.component.html diff --git a/apps/desktop/src/vault/app/vault-v3/vault.component.html b/apps/desktop/src/vault/app/vault-v3/vault.component.html new file mode 100644 index 00000000000..a9a25f57994 --- /dev/null +++ b/apps/desktop/src/vault/app/vault-v3/vault.component.html @@ -0,0 +1,70 @@ +
+ + +
+ +
+
+
+ + + + + + + +
+
+
+
+ +
+ diff --git a/apps/desktop/src/vault/app/vault-v3/vault.component.ts b/apps/desktop/src/vault/app/vault-v3/vault.component.ts index b29b66225c7..21ba7547f8b 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault.component.ts @@ -1,9 +1,1020 @@ -import { ChangeDetectionStrategy, Component } from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { + ChangeDetectorRef, + Component, + NgZone, + OnDestroy, + OnInit, + ViewChild, + ViewContainerRef, +} from "@angular/core"; +import { ActivatedRoute, Router } from "@angular/router"; +import { firstValueFrom, Subject, takeUntil, switchMap, lastValueFrom, Observable } from "rxjs"; +import { filter, map, take } from "rxjs/operators"; +import { CollectionService, CollectionView } from "@bitwarden/admin-console/common"; +import { PremiumBadgeComponent } from "@bitwarden/angular/billing/components/premium-badge"; +import { VaultViewPasswordHistoryService } from "@bitwarden/angular/services/view-password-history.service"; +import { VaultFilter } from "@bitwarden/angular/vault/vault-filter/models/vault-filter.model"; +import { AuthRequestServiceAbstraction } from "@bitwarden/auth/common"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; +import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { PolicyType } from "@bitwarden/common/admin-console/enums"; +import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { getUserId } from "@bitwarden/common/auth/services/account.service"; +import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; +import { EventType } from "@bitwarden/common/enums"; +import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { getByIds } from "@bitwarden/common/platform/misc"; +import { SyncService } from "@bitwarden/common/platform/sync"; +import { CipherId, OrganizationId, UserId } from "@bitwarden/common/types/guid"; +import { CipherArchiveService } from "@bitwarden/common/vault/abstractions/cipher-archive.service"; +import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; +import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; +import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service"; +import { ViewPasswordHistoryService } from "@bitwarden/common/vault/abstractions/view-password-history.service"; +import { CipherType, toCipherType } from "@bitwarden/common/vault/enums"; +import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type"; +import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { + CipherViewLike, + CipherViewLikeUtils, +} from "@bitwarden/common/vault/utils/cipher-view-like-utils"; +import { filterOutNullish } from "@bitwarden/common/vault/utils/observable-utilities"; +import { + BadgeModule, + ButtonModule, + DialogService, + ItemModule, + ToastService, + CopyClickListener, + COPY_CLICK_LISTENER, +} from "@bitwarden/components"; +import { I18nPipe } from "@bitwarden/ui-common"; +import { + AddEditFolderDialogComponent, + AddEditFolderDialogResult, + AttachmentDialogResult, + AttachmentsV2Component, + ChangeLoginPasswordService, + CipherFormConfig, + CipherFormConfigService, + CipherFormGenerationService, + CipherFormMode, + CipherFormModule, + CipherViewComponent, + CollectionAssignmentResult, + DecryptionFailureDialogComponent, + DefaultChangeLoginPasswordService, + DefaultCipherFormConfigService, + PasswordRepromptService, + CipherFormComponent, + ArchiveCipherUtilitiesService, +} from "@bitwarden/vault"; + +import { SearchBarService } from "../../../app/layout/search/search-bar.service"; +import { DesktopCredentialGenerationService } from "../../../services/desktop-cipher-form-generator.service"; +import { DesktopPremiumUpgradePromptService } from "../../../services/desktop-premium-upgrade-prompt.service"; +import { invokeMenu, RendererMenuItem } from "../../../utils"; +import { AssignCollectionsDesktopComponent } from "../vault/assign-collections"; +import { ItemFooterComponent } from "../vault/item-footer.component"; +import { VaultFilterComponent } from "../vault/vault-filter/vault-filter.component"; +import { VaultFilterModule } from "../vault/vault-filter/vault-filter.module"; +import { VaultItemsV2Component } from "../vault/vault-items-v2.component"; + +const BroadcasterSubscriptionId = "VaultComponent"; + +// 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-vault-v3", - imports: [], - template: "

Vault V3 Component

", - changeDetection: ChangeDetectionStrategy.OnPush, + templateUrl: "vault.component.html", + imports: [ + BadgeModule, + CommonModule, + CipherFormModule, + CipherViewComponent, + ItemFooterComponent, + I18nPipe, + ItemModule, + ButtonModule, + PremiumBadgeComponent, + VaultFilterModule, + VaultItemsV2Component, + ], + providers: [ + { + provide: CipherFormConfigService, + useClass: DefaultCipherFormConfigService, + }, + { + provide: ChangeLoginPasswordService, + useClass: DefaultChangeLoginPasswordService, + }, + { + provide: ViewPasswordHistoryService, + useClass: VaultViewPasswordHistoryService, + }, + { + provide: PremiumUpgradePromptService, + useClass: DesktopPremiumUpgradePromptService, + }, + { provide: CipherFormGenerationService, useClass: DesktopCredentialGenerationService }, + { + provide: COPY_CLICK_LISTENER, + useExisting: VaultComponent, + }, + ], }) -export class VaultComponent {} +export class VaultComponent + implements OnInit, OnDestroy, CopyClickListener +{ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals + @ViewChild(VaultItemsV2Component, { static: true }) + vaultItemsComponent: VaultItemsV2Component | null = null; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals + @ViewChild(VaultFilterComponent, { static: true }) + vaultFilterComponent: VaultFilterComponent | null = null; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals + @ViewChild("folderAddEdit", { read: ViewContainerRef, static: true }) + folderAddEditModalRef: ViewContainerRef | null = null; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals + @ViewChild(CipherFormComponent) + cipherFormComponent: CipherFormComponent | null = null; + + action: CipherFormMode | "view" | null = null; + cipherId: string | null = null; + favorites = false; + type: CipherType | null = null; + folderId: string | null = null; + collectionId: string | null = null; + organizationId: string | null = null; + myVaultOnly = false; + addType: CipherType | undefined = undefined; + addOrganizationId: string | null = null; + addCollectionIds: string[] | null = null; + showingModal = false; + deleted = false; + userHasPremiumAccess = false; + activeFilter: VaultFilter = new VaultFilter(); + activeUserId: UserId | null = null; + cipherRepromptId: string | null = null; + cipher: CipherView | null = new CipherView(); + collections: CollectionView[] | null = null; + config: CipherFormConfig | null = null; + + /** Tracks the disabled status of the edit cipher form */ + protected formDisabled: boolean = false; + + private organizations$: Observable = this.accountService.activeAccount$.pipe( + map((a) => a?.id), + filterOutNullish(), + switchMap((id) => this.organizationService.organizations$(id)), + ); + + protected canAccessAttachments$ = this.accountService.activeAccount$.pipe( + filter((account): account is Account => !!account), + switchMap((account) => + this.billingAccountProfileStateService.hasPremiumFromAnySource$(account.id), + ), + ); + + private componentIsDestroyed$ = new Subject(); + private allOrganizations: Organization[] = []; + private allCollections: CollectionView[] = []; + + constructor( + private route: ActivatedRoute, + private router: Router, + private i18nService: I18nService, + private broadcasterService: BroadcasterService, + private changeDetectorRef: ChangeDetectorRef, + private ngZone: NgZone, + private syncService: SyncService, + private messagingService: MessagingService, + private platformUtilsService: PlatformUtilsService, + private eventCollectionService: EventCollectionService, + private totpService: TotpService, + private passwordRepromptService: PasswordRepromptService, + private searchBarService: SearchBarService, + private apiService: ApiService, + private dialogService: DialogService, + private billingAccountProfileStateService: BillingAccountProfileStateService, + private toastService: ToastService, + private accountService: AccountService, + private cipherService: CipherService, + private formConfigService: CipherFormConfigService, + private premiumUpgradePromptService: PremiumUpgradePromptService, + private collectionService: CollectionService, + private organizationService: OrganizationService, + private folderService: FolderService, + private configService: ConfigService, + private authRequestService: AuthRequestServiceAbstraction, + private cipherArchiveService: CipherArchiveService, + private policyService: PolicyService, + private archiveCipherUtilitiesService: ArchiveCipherUtilitiesService, + ) {} + + async ngOnInit() { + this.accountService.activeAccount$ + .pipe( + filter((account): account is Account => !!account), + switchMap((account) => + this.billingAccountProfileStateService.hasPremiumFromAnySource$(account.id), + ), + takeUntil(this.componentIsDestroyed$), + ) + .subscribe((canAccessPremium: boolean) => { + this.userHasPremiumAccess = canAccessPremium; + }); + + this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => { + this.ngZone + .run(async () => { + let detectChanges = true; + try { + switch (message.command) { + case "newLogin": + await this.addCipher(CipherType.Login).catch(() => {}); + break; + case "newCard": + await this.addCipher(CipherType.Card).catch(() => {}); + break; + case "newIdentity": + await this.addCipher(CipherType.Identity).catch(() => {}); + break; + case "newSecureNote": + await this.addCipher(CipherType.SecureNote).catch(() => {}); + break; + case "newSshKey": + await this.addCipher(CipherType.SshKey).catch(() => {}); + break; + case "focusSearch": + (document.querySelector("#search") as HTMLInputElement)?.select(); + detectChanges = false; + break; + case "syncCompleted": + if (this.vaultItemsComponent) { + await this.vaultItemsComponent + .reload(this.activeFilter.buildFilter()) + .catch(() => {}); + } + if (this.vaultFilterComponent) { + await this.vaultFilterComponent + .reloadCollectionsAndFolders(this.activeFilter) + .catch(() => {}); + await this.vaultFilterComponent.reloadOrganizations().catch(() => {}); + } + break; + case "modalShown": + this.showingModal = true; + break; + case "modalClosed": + this.showingModal = false; + break; + case "copyUsername": { + if (this.cipher?.login?.username) { + this.copyValue(this.cipher, this.cipher?.login?.username, "username", "Username"); + } + break; + } + case "copyPassword": { + if (this.cipher?.login?.password && this.cipher.viewPassword) { + this.copyValue(this.cipher, this.cipher.login.password, "password", "Password"); + await this.eventCollectionService + .collect(EventType.Cipher_ClientCopiedPassword, this.cipher.id) + .catch(() => {}); + } + break; + } + case "copyTotp": { + if ( + this.cipher?.login?.hasTotp && + (this.cipher.organizationUseTotp || this.userHasPremiumAccess) + ) { + const value = await firstValueFrom( + this.totpService.getCode$(this.cipher.login.totp), + ).catch((): any => null); + if (value) { + this.copyValue(this.cipher, value.code, "verificationCodeTotp", "TOTP"); + } + } + break; + } + default: + detectChanges = false; + break; + } + } catch { + // Ignore errors + } + if (detectChanges) { + this.changeDetectorRef.detectChanges(); + } + }) + .catch(() => {}); + }); + + if (!this.syncService.syncInProgress) { + await this.load().catch(() => {}); + } + + this.searchBarService.setEnabled(true); + this.searchBarService.setPlaceholderText(this.i18nService.t("searchVault")); + + const authRequests = await firstValueFrom( + this.authRequestService.getLatestPendingAuthRequest$()!, + ); + if (authRequests != null) { + this.messagingService.send("openLoginApproval", { + notificationId: authRequests.id, + }); + } + + this.activeUserId = await firstValueFrom( + this.accountService.activeAccount$.pipe(getUserId), + ).catch((): any => null); + + if (this.activeUserId) { + this.cipherService + .failedToDecryptCiphers$(this.activeUserId) + .pipe( + map((ciphers) => ciphers?.filter((c) => !c.isDeleted) ?? []), + filter((ciphers) => ciphers.length > 0), + take(1), + takeUntil(this.componentIsDestroyed$), + ) + .subscribe((ciphers) => { + DecryptionFailureDialogComponent.open(this.dialogService, { + cipherIds: ciphers.map((c) => c.id as CipherId), + }); + }); + } + + this.organizations$.pipe(takeUntil(this.componentIsDestroyed$)).subscribe((orgs) => { + this.allOrganizations = orgs; + }); + + if (!this.activeUserId) { + throw new Error("No user found."); + } + + this.collectionService + .decryptedCollections$(this.activeUserId) + .pipe(takeUntil(this.componentIsDestroyed$)) + .subscribe((collections) => { + this.allCollections = collections; + }); + } + + ngOnDestroy() { + this.searchBarService.setEnabled(false); + this.broadcasterService.unsubscribe(BroadcasterSubscriptionId); + this.componentIsDestroyed$.next(true); + this.componentIsDestroyed$.complete(); + } + + async load() { + const params = await firstValueFrom(this.route.queryParams).catch(); + const paramCipherAddType = toCipherType(params.addType); + if (params.cipherId) { + const cipherView = new CipherView(); + cipherView.id = params.cipherId; + if (params.action === "clone") { + await this.cloneCipher(cipherView).catch(() => {}); + } else if (params.action === "edit") { + await this.editCipher(cipherView).catch(() => {}); + } else { + await this.viewCipher(cipherView).catch(() => {}); + } + } else if (params.action === "add" && paramCipherAddType) { + this.addType = paramCipherAddType; + await this.addCipher(this.addType).catch(() => {}); + } + + const paramCipherType = toCipherType(params.type); + this.activeFilter = new VaultFilter({ + status: params.deleted ? "trash" : params.favorites ? "favorites" : "all", + cipherType: params.action === "add" || paramCipherType == null ? undefined : paramCipherType, + selectedFolderId: params.folderId, + selectedCollectionId: params.selectedCollectionId, + selectedOrganizationId: params.selectedOrganizationId, + myVaultOnly: params.myVaultOnly ?? false, + }); + if (this.vaultItemsComponent) { + await this.vaultItemsComponent.reload(this.activeFilter.buildFilter()).catch(() => {}); + } + } + + /** + * Handler for Vault level CopyClickDirectives to send the minimizeOnCopy message + */ + onCopy() { + this.messagingService.send("minimizeOnCopy"); + } + + async viewCipher(c: CipherViewLike) { + if (CipherViewLikeUtils.decryptionFailure(c)) { + DecryptionFailureDialogComponent.open(this.dialogService, { + cipherIds: [c.id as CipherId], + }); + return; + } + const cipher = await this.cipherService.getFullCipherView(c); + if (await this.shouldReprompt(cipher, "view")) { + return; + } + this.cipherId = cipher.id; + this.cipher = cipher; + this.collections = + this.vaultFilterComponent?.collections?.fullList.filter((c) => + cipher.collectionIds.includes(c.id), + ) ?? null; + this.action = "view"; + + await this.go().catch(() => {}); + await this.eventCollectionService.collect( + EventType.Cipher_ClientViewed, + cipher.id, + false, + cipher.organizationId, + ); + } + + formStatusChanged(status: "disabled" | "enabled") { + this.formDisabled = status === "disabled"; + } + + async openAttachmentsDialog() { + if (!this.userHasPremiumAccess) { + return; + } + const dialogRef = AttachmentsV2Component.open(this.dialogService, { + cipherId: this.cipherId as CipherId, + }); + const result = await firstValueFrom(dialogRef.closed).catch((): any => null); + if ( + result?.action === AttachmentDialogResult.Removed || + result?.action === AttachmentDialogResult.Uploaded + ) { + await this.vaultItemsComponent?.refresh().catch(() => {}); + + if (this.cipherFormComponent == null) { + return; + } + + // The encrypted state of ciphers is updated when an attachment is added, + // but the cache is also cleared. Depending on timing, `cipherService.get` can return the + // old cipher. Retrieve the updated cipher from `cipherViews$`, + // which refreshes after the cached is cleared. + const updatedCipherView = await firstValueFrom( + this.cipherService.cipherViews$(this.activeUserId!).pipe( + filter((c) => !!c), + map((ciphers) => ciphers.find((c) => c.id === this.cipherId)), + ), + ); + + // `find` can return undefined but that shouldn't happen as + // this would mean that the cipher was deleted. + // To make TypeScript happy, exit early if it isn't found. + if (!updatedCipherView) { + return; + } + + this.cipherFormComponent.patchCipher((currentCipher) => { + currentCipher.attachments = updatedCipherView.attachments; + currentCipher.revisionDate = updatedCipherView.revisionDate; + + return currentCipher; + }); + } + } + + async viewCipherMenu(c: CipherViewLike) { + const cipher = await this.cipherService.getFullCipherView(c); + const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); + const userCanArchive = await firstValueFrom(this.cipherArchiveService.userCanArchive$(userId)); + const orgOwnershipPolicy = await firstValueFrom( + this.policyService.policyAppliesToUser$(PolicyType.OrganizationDataOwnership, userId), + ); + + const menu: RendererMenuItem[] = [ + { + label: this.i18nService.t("view"), + click: () => { + this.functionWithChangeDetection(() => { + this.viewCipher(cipher).catch(() => {}); + }); + }, + }, + ]; + + if (cipher.decryptionFailure) { + invokeMenu(menu); + } + + if (!cipher.isDeleted) { + menu.push({ + label: this.i18nService.t("edit"), + click: () => { + this.functionWithChangeDetection(() => { + this.editCipher(cipher).catch(() => {}); + }); + }, + }); + + const archivedWithOrgOwnership = cipher.isArchived && orgOwnershipPolicy; + const canCloneArchived = !cipher.isArchived || userCanArchive; + + if (!cipher.organizationId && !archivedWithOrgOwnership && canCloneArchived) { + menu.push({ + label: this.i18nService.t("clone"), + click: () => { + this.functionWithChangeDetection(() => { + this.cloneCipher(cipher).catch(() => {}); + }); + }, + }); + } + + const hasEditableCollections = this.allCollections.some((collection) => !collection.readOnly); + + if (cipher.canAssignToCollections && hasEditableCollections) { + menu.push({ + label: this.i18nService.t("assignToCollections"), + click: () => + this.functionWithChangeDetection(async () => { + await this.shareCipher(cipher); + }), + }); + } + } + + if (!cipher.organizationId && !cipher.isDeleted && !cipher.isArchived) { + menu.push({ + label: this.i18nService.t("archiveVerb"), + click: async () => { + if (!userCanArchive) { + await this.premiumUpgradePromptService.promptForPremium(); + return; + } + + await this.archiveCipherUtilitiesService.archiveCipher(cipher); + await this.refreshCurrentCipher(); + }, + }); + } + + if (cipher.isArchived) { + menu.push({ + label: this.i18nService.t("unArchive"), + click: async () => { + await this.archiveCipherUtilitiesService.unarchiveCipher(cipher); + await this.refreshCurrentCipher(); + }, + }); + } + + switch (cipher.type) { + case CipherType.Login: + if ( + cipher.login.canLaunch || + cipher.login.username != null || + cipher.login.password != null + ) { + menu.push({ type: "separator" }); + } + if (cipher.login.canLaunch) { + menu.push({ + label: this.i18nService.t("launch"), + click: () => this.platformUtilsService.launchUri(cipher.login.launchUri), + }); + } + if (cipher.login.username != null) { + menu.push({ + label: this.i18nService.t("copyUsername"), + click: () => this.copyValue(cipher, cipher.login.username, "username", "Username"), + }); + } + if (cipher.login.password != null && cipher.viewPassword) { + menu.push({ + label: this.i18nService.t("copyPassword"), + click: () => { + this.copyValue(cipher, cipher.login.password, "password", "Password"); + this.eventCollectionService + .collect(EventType.Cipher_ClientCopiedPassword, cipher.id) + .catch(() => {}); + }, + }); + } + if (cipher.login.hasTotp && (cipher.organizationUseTotp || this.userHasPremiumAccess)) { + menu.push({ + label: this.i18nService.t("copyVerificationCodeTotp"), + click: async () => { + const value = await firstValueFrom( + this.totpService.getCode$(cipher.login.totp), + ).catch((): any => null); + if (value) { + this.copyValue(cipher, value.code, "verificationCodeTotp", "TOTP"); + } + }, + }); + } + break; + case CipherType.Card: + if (cipher.card.number != null || cipher.card.code != null) { + menu.push({ type: "separator" }); + } + if (cipher.card.number != null) { + menu.push({ + label: this.i18nService.t("copyNumber"), + click: () => this.copyValue(cipher, cipher.card.number, "number", "Card Number"), + }); + } + if (cipher.card.code != null) { + menu.push({ + label: this.i18nService.t("copySecurityCode"), + click: () => { + this.copyValue(cipher, cipher.card.code, "securityCode", "Security Code"); + this.eventCollectionService + .collect(EventType.Cipher_ClientCopiedCardCode, cipher.id) + .catch(() => {}); + }, + }); + } + break; + default: + break; + } + invokeMenu(menu); + } + + async shouldReprompt(cipher: CipherView, action: "edit" | "clone" | "view"): Promise { + return !(await this.canNavigateAway(action, cipher)) || !(await this.passwordReprompt(cipher)); + } + + async buildFormConfig(action: CipherFormMode) { + this.config = await this.formConfigService + .buildConfig(action, this.cipherId as CipherId, this.addType) + .catch((): any => null); + } + + async editCipher(cipher: CipherView) { + if (await this.shouldReprompt(cipher, "edit")) { + return; + } + this.cipherId = cipher.id; + this.cipher = cipher; + await this.buildFormConfig("edit"); + if (!cipher.edit && this.config) { + this.config.mode = "partial-edit"; + } + this.action = "edit"; + await this.go().catch(() => {}); + } + + async cloneCipher(cipher: CipherView) { + if (await this.shouldReprompt(cipher, "clone")) { + return; + } + this.cipherId = cipher.id; + this.cipher = cipher; + await this.buildFormConfig("clone"); + this.action = "clone"; + await this.go().catch(() => {}); + } + + async shareCipher(cipher: CipherView) { + if (!cipher) { + this.toastService.showToast({ + variant: "error", + title: this.i18nService.t("errorOccurred"), + message: this.i18nService.t("nothingSelected"), + }); + return; + } + + if (!(await this.passwordReprompt(cipher))) { + return; + } + + const availableCollections = this.getAvailableCollections(cipher); + + const dialog = AssignCollectionsDesktopComponent.open(this.dialogService, { + data: { + ciphers: [cipher], + organizationId: cipher.organizationId as OrganizationId, + availableCollections, + }, + }); + + const result = await lastValueFrom(dialog.closed); + if (result === CollectionAssignmentResult.Saved) { + const updatedCipher = await firstValueFrom( + // Fetch the updated cipher from the service + this.cipherService.cipherViews$(this.activeUserId as UserId).pipe( + filter((ciphers) => ciphers != null), + map((ciphers) => ciphers!.find((c) => c.id === cipher.id)), + filter((foundCipher) => foundCipher != null), + ), + ); + await this.savedCipher(updatedCipher); + } + } + + async addCipher(type: CipherType) { + if (this.action === "add") { + return; + } + this.addType = type || this.activeFilter.cipherType; + this.cipher = new CipherView(); + this.cipherId = null; + await this.buildFormConfig("add"); + this.action = "add"; + this.prefillCipherFromFilter(); + await this.go().catch(() => {}); + + if (type === CipherType.SshKey) { + this.toastService.showToast({ + variant: "success", + title: "", + message: this.i18nService.t("sshKeyGenerated"), + }); + } + } + + async savedCipher(cipher: CipherView) { + this.cipherId = null; + this.action = "view"; + await this.vaultItemsComponent?.refresh().catch(() => {}); + + if (!this.activeUserId) { + throw new Error("No userId provided."); + } + + this.collections = await firstValueFrom( + this.collectionService + .decryptedCollections$(this.activeUserId) + .pipe(getByIds(cipher.collectionIds)), + ); + + this.cipherId = cipher.id; + this.cipher = cipher; + await this.go().catch(() => {}); + await this.vaultItemsComponent?.refresh().catch(() => {}); + } + + async deleteCipher() { + this.cipherId = null; + this.cipher = null; + this.action = null; + await this.go().catch(() => {}); + await this.vaultItemsComponent?.refresh().catch(() => {}); + } + + async restoreCipher() { + this.cipherId = null; + this.action = null; + await this.go().catch(() => {}); + await this.vaultItemsComponent?.refresh().catch(() => {}); + } + + async cancelCipher(cipher: CipherView) { + this.cipherId = cipher.id; + this.cipher = cipher; + this.action = this.cipherId ? "view" : null; + await this.go().catch(() => {}); + } + + async applyVaultFilter(vaultFilter: VaultFilter) { + this.searchBarService.setPlaceholderText( + this.i18nService.t(this.calculateSearchBarLocalizationString(vaultFilter)), + ); + this.activeFilter = vaultFilter; + await this.vaultItemsComponent + ?.reload( + this.activeFilter.buildFilter(), + vaultFilter.status === "trash", + vaultFilter.status === "archive", + ) + .catch(() => {}); + await this.go().catch(() => {}); + } + + private getAvailableCollections(cipher: CipherView): CollectionView[] { + const orgId = cipher.organizationId; + if (!orgId || orgId === "MyVault") { + return []; + } + + const organization = this.allOrganizations.find((o) => o.id === orgId); + return this.allCollections.filter((c) => c.organizationId === organization?.id && !c.readOnly); + } + + private calculateSearchBarLocalizationString(vaultFilter: VaultFilter): string { + if (vaultFilter.status === "favorites") { + return "searchFavorites"; + } + if (vaultFilter.status === "trash") { + return "searchTrash"; + } + if (vaultFilter.cipherType != null) { + return "searchType"; + } + if (vaultFilter.selectedFolderId != null && vaultFilter.selectedFolderId !== "none") { + return "searchFolder"; + } + if (vaultFilter.selectedCollectionId != null) { + return "searchCollection"; + } + if (vaultFilter.selectedOrganizationId != null) { + return "searchOrganization"; + } + if (vaultFilter.myVaultOnly) { + return "searchMyVault"; + } + return "searchVault"; + } + + async addFolder() { + this.messagingService.send("newFolder"); + } + + async editFolder(folderId: string) { + if (!this.activeUserId) { + return; + } + const folderView = await firstValueFrom( + this.folderService.getDecrypted$(folderId, this.activeUserId), + ); + + if (!folderView) { + return; + } + + const dialogRef = AddEditFolderDialogComponent.open(this.dialogService, { + editFolderConfig: { + folder: { + ...folderView, + }, + }, + }); + + const result = await lastValueFrom(dialogRef.closed); + + if ( + result === AddEditFolderDialogResult.Deleted || + result === AddEditFolderDialogResult.Created + ) { + await this.vaultFilterComponent?.reloadCollectionsAndFolders(this.activeFilter); + } + } + + /** Refresh the current cipher object */ + protected async refreshCurrentCipher() { + if (!this.cipher) { + return; + } + + this.cipher = await firstValueFrom( + this.cipherService.cipherViews$(this.activeUserId!).pipe( + filter((c) => !!c), + map((ciphers) => ciphers.find((c) => c.id === this.cipherId) ?? null), + ), + ); + } + + private dirtyInput(): boolean { + return ( + (this.action === "add" || this.action === "edit" || this.action === "clone") && + document.querySelectorAll("vault-cipher-form .ng-dirty").length > 0 + ); + } + + private async wantsToSaveChanges(): Promise { + const confirmed = await this.dialogService + .openSimpleDialog({ + title: { key: "unsavedChangesTitle" }, + content: { key: "unsavedChangesConfirmation" }, + type: "warning", + }) + .catch(() => false); + return !confirmed; + } + + private async go(queryParams: any = null) { + if (queryParams == null) { + queryParams = { + action: this.action, + cipherId: this.cipherId, + favorites: this.favorites ? true : null, + type: this.type, + folderId: this.folderId, + collectionId: this.collectionId, + deleted: this.deleted ? true : null, + organizationId: this.organizationId, + myVaultOnly: this.myVaultOnly, + }; + } + this.router + .navigate([], { + relativeTo: this.route, + queryParams: queryParams, + replaceUrl: true, + }) + .catch(() => {}); + } + + private copyValue(cipher: CipherView, value: string, labelI18nKey: string, aType: string) { + this.functionWithChangeDetection(() => { + (async () => { + if ( + cipher.reprompt !== CipherRepromptType.None && + this.passwordRepromptService.protectedFields().includes(aType) && + !(await this.passwordReprompt(cipher)) + ) { + return; + } + this.platformUtilsService.copyToClipboard(value); + this.toastService.showToast({ + variant: "info", + title: undefined, + message: this.i18nService.t("valueCopied", this.i18nService.t(labelI18nKey)), + }); + this.messagingService.send("minimizeOnCopy"); + })().catch(() => {}); + }); + } + + private functionWithChangeDetection(func: () => void) { + this.ngZone.run(() => { + func(); + this.changeDetectorRef.detectChanges(); + }); + } + + private prefillCipherFromFilter() { + if (this.activeFilter.selectedCollectionId != null && this.vaultFilterComponent != null) { + const collections = this.vaultFilterComponent.collections?.fullList.filter( + (c) => c.id === this.activeFilter.selectedCollectionId, + ); + if (collections.length > 0) { + this.addOrganizationId = collections[0].organizationId; + this.addCollectionIds = [this.activeFilter.selectedCollectionId]; + } + } else if (this.activeFilter.selectedOrganizationId) { + this.addOrganizationId = this.activeFilter.selectedOrganizationId; + } else { + // clear out organizationId when the user switches to a personal vault filter + this.addOrganizationId = null; + } + if (this.activeFilter.selectedFolderId && this.activeFilter.selectedFolder) { + this.folderId = this.activeFilter.selectedFolderId; + } + + if (this.config == null) { + return; + } + + this.config.initialValues = { + ...this.config.initialValues, + organizationId: this.addOrganizationId as OrganizationId, + }; + } + + private async canNavigateAway(action: string, cipher?: CipherView) { + if (this.action === action && (!cipher || this.cipherId === cipher.id)) { + return false; + } else if (this.dirtyInput() && (await this.wantsToSaveChanges())) { + return false; + } + return true; + } + + private async passwordReprompt(cipher: CipherView) { + if (cipher.reprompt === CipherRepromptType.None) { + this.cipherRepromptId = null; + return true; + } + if (this.cipherRepromptId === cipher.id) { + return true; + } + const repromptResult = await this.passwordRepromptService.showPasswordPrompt(); + if (repromptResult) { + this.cipherRepromptId = cipher.id; + } + return repromptResult; + } +} From aadfd6c1e57207f377d0f720b8d80dcc13245446 Mon Sep 17 00:00:00 2001 From: Leslie Xiong Date: Wed, 26 Nov 2025 14:35:39 -0500 Subject: [PATCH 11/56] removed ChangeDetectionStrategy.OnPush from desktop-layout and added FIXME/lint disable --- apps/desktop/src/app/layout/desktop-layout.component.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/desktop/src/app/layout/desktop-layout.component.ts b/apps/desktop/src/app/layout/desktop-layout.component.ts index 5059a6e4d0b..006055f475f 100644 --- a/apps/desktop/src/app/layout/desktop-layout.component.ts +++ b/apps/desktop/src/app/layout/desktop-layout.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component } from "@angular/core"; +import { Component } from "@angular/core"; import { RouterModule } from "@angular/router"; import { PasswordManagerLogo } from "@bitwarden/assets/svg"; @@ -7,11 +7,12 @@ import { I18nPipe } from "@bitwarden/ui-common"; import { DesktopSideNavComponent } from "./desktop-side-nav.component"; +// 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-layout", imports: [RouterModule, I18nPipe, LayoutComponent, NavigationModule, DesktopSideNavComponent], templateUrl: "./desktop-layout.component.html", - changeDetection: ChangeDetectionStrategy.OnPush, }) export class DesktopLayoutComponent { protected readonly logo = PasswordManagerLogo; From 77bd7b61ee59228f4d95677d9d21d70d8ca0b8f7 Mon Sep 17 00:00:00 2001 From: Leslie Xiong Date: Mon, 1 Dec 2025 18:48:20 -0500 Subject: [PATCH 12/56] removed test (based on vault-v2 which is missing a test) --- .../app/vault-v3/vault.component.spec.ts | 22 ------------------- 1 file changed, 22 deletions(-) delete mode 100644 apps/desktop/src/vault/app/vault-v3/vault.component.spec.ts diff --git a/apps/desktop/src/vault/app/vault-v3/vault.component.spec.ts b/apps/desktop/src/vault/app/vault-v3/vault.component.spec.ts deleted file mode 100644 index 89ba05055f8..00000000000 --- a/apps/desktop/src/vault/app/vault-v3/vault.component.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { ComponentFixture, TestBed } from "@angular/core/testing"; - -import { VaultComponent } from "./vault.component"; - -describe("VaultComponent", () => { - let component: VaultComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [VaultComponent], - }).compileComponents(); - - fixture = TestBed.createComponent(VaultComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it("creates component", () => { - expect(component).toBeTruthy(); - }); -}); From d960ba1e45e31ca986339931dd644e60c59e0f9e Mon Sep 17 00:00:00 2001 From: Leslie Xiong Date: Mon, 1 Dec 2025 19:13:35 -0500 Subject: [PATCH 13/56] created `VaultNavComponent` and attached to `DesktopLayoutComponent` --- .../src/app/layout/desktop-layout.component.html | 2 +- .../src/app/layout/desktop-layout.component.ts | 11 ++++++++++- .../vault/app/vault-v3/nav/vault-nav.component.html | 1 + .../vault/app/vault-v3/nav/vault-nav.component.ts | 12 ++++++++++++ 4 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 apps/desktop/src/vault/app/vault-v3/nav/vault-nav.component.html create mode 100644 apps/desktop/src/vault/app/vault-v3/nav/vault-nav.component.ts diff --git a/apps/desktop/src/app/layout/desktop-layout.component.html b/apps/desktop/src/app/layout/desktop-layout.component.html index 94b9201ae21..c7dca09427a 100644 --- a/apps/desktop/src/app/layout/desktop-layout.component.html +++ b/apps/desktop/src/app/layout/desktop-layout.component.html @@ -2,7 +2,7 @@ - + diff --git a/apps/desktop/src/app/layout/desktop-layout.component.ts b/apps/desktop/src/app/layout/desktop-layout.component.ts index 006055f475f..b3f708cbbe8 100644 --- a/apps/desktop/src/app/layout/desktop-layout.component.ts +++ b/apps/desktop/src/app/layout/desktop-layout.component.ts @@ -5,13 +5,22 @@ import { PasswordManagerLogo } from "@bitwarden/assets/svg"; import { LayoutComponent, NavigationModule } from "@bitwarden/components"; import { I18nPipe } from "@bitwarden/ui-common"; +import { VaultNavComponent } from "../../vault/app/vault-v3/nav/vault-nav.component"; + import { DesktopSideNavComponent } from "./desktop-side-nav.component"; // 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-layout", - imports: [RouterModule, I18nPipe, LayoutComponent, NavigationModule, DesktopSideNavComponent], + imports: [ + RouterModule, + I18nPipe, + LayoutComponent, + NavigationModule, + DesktopSideNavComponent, + VaultNavComponent, + ], templateUrl: "./desktop-layout.component.html", }) export class DesktopLayoutComponent { diff --git a/apps/desktop/src/vault/app/vault-v3/nav/vault-nav.component.html b/apps/desktop/src/vault/app/vault-v3/nav/vault-nav.component.html new file mode 100644 index 00000000000..d1c0399c1b2 --- /dev/null +++ b/apps/desktop/src/vault/app/vault-v3/nav/vault-nav.component.html @@ -0,0 +1 @@ + diff --git a/apps/desktop/src/vault/app/vault-v3/nav/vault-nav.component.ts b/apps/desktop/src/vault/app/vault-v3/nav/vault-nav.component.ts new file mode 100644 index 00000000000..6169124e76b --- /dev/null +++ b/apps/desktop/src/vault/app/vault-v3/nav/vault-nav.component.ts @@ -0,0 +1,12 @@ +import { ChangeDetectionStrategy, Component } from "@angular/core"; + +import { NavigationModule } from "@bitwarden/components"; +import { I18nPipe } from "@bitwarden/ui-common"; + +@Component({ + selector: "app-vault-nav", + imports: [I18nPipe, NavigationModule], + templateUrl: "./vault-nav.component.html", + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class VaultNavComponent {} From 6bd297dee3e7c122dbafe229edd282ae80a2a1f4 Mon Sep 17 00:00:00 2001 From: Leslie Xiong Date: Mon, 1 Dec 2025 19:15:32 -0500 Subject: [PATCH 14/56] Duplicated '/vault-filters' into '/vault-v3' --- .../filters/collection-filter.component.html | 84 +++++++++++ .../filters/collection-filter.component.ts | 12 ++ .../filters/folder-filter.component.html | 94 ++++++++++++ .../filters/folder-filter.component.ts | 12 ++ .../organization-filter.component.html | 140 ++++++++++++++++++ .../filters/organization-filter.component.ts | 53 +++++++ .../filters/status-filter.component.html | 68 +++++++++ .../filters/status-filter.component.spec.ts | 98 ++++++++++++ .../filters/status-filter.component.ts | 51 +++++++ .../filters/type-filter.component.html | 39 +++++ .../filters/type-filter.component.ts | 34 +++++ .../vault-filter/vault-filter.component.html | 51 +++++++ .../vault-filter/vault-filter.component.ts | 12 ++ .../vault-filter/vault-filter.module.ts | 34 +++++ 14 files changed, 782 insertions(+) create mode 100644 apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.html create mode 100644 apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.ts create mode 100644 apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.html create mode 100644 apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.ts create mode 100644 apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.html create mode 100644 apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.ts create mode 100644 apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.html create mode 100644 apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.spec.ts create mode 100644 apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts create mode 100644 apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.html create mode 100644 apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.ts create mode 100644 apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.html create mode 100644 apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts create mode 100644 apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.module.ts diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.html new file mode 100644 index 00000000000..f83a2e1c91f --- /dev/null +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.html @@ -0,0 +1,84 @@ + +
+

+ +

+
+
    + +
  • + + + + +
      + + +
    +
  • +
    + + +
+
diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.ts new file mode 100644 index 00000000000..015b301efdb --- /dev/null +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.ts @@ -0,0 +1,12 @@ +import { Component } from "@angular/core"; + +import { CollectionFilterComponent as BaseCollectionFilterComponent } from "@bitwarden/angular/vault/vault-filter/components/collection-filter.component"; + +// 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-collection-filter", + templateUrl: "collection-filter.component.html", + standalone: false, +}) +export class CollectionFilterComponent extends BaseCollectionFilterComponent {} diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.html new file mode 100644 index 00000000000..a2240b03ff5 --- /dev/null +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.html @@ -0,0 +1,94 @@ + +
+

+ +

+ +
+
    + +
  • + + + + + +
      + + +
    +
  • +
    + +
+
diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.ts new file mode 100644 index 00000000000..f340e4082b8 --- /dev/null +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.ts @@ -0,0 +1,12 @@ +import { Component } from "@angular/core"; + +import { FolderFilterComponent as BaseFolderFilterComponent } from "@bitwarden/angular/vault/vault-filter/components/folder-filter.component"; + +// 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-folder-filter", + templateUrl: "folder-filter.component.html", + standalone: false, +}) +export class FolderFilterComponent extends BaseFolderFilterComponent {} diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.html new file mode 100644 index 00000000000..8c73891dc09 --- /dev/null +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.html @@ -0,0 +1,140 @@ + + + +
+ +   +

+ +

+
+
    +
  • + + + + + + +
  • +
+
+ +
+ +   +

+ +

+
+
    +
  • + + + +
  • +
  • + + + + + + +
  • +
+
+
+
+
diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.ts new file mode 100644 index 00000000000..99338ddbb7c --- /dev/null +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.ts @@ -0,0 +1,53 @@ +// FIXME: Update this file to be type safe and remove this and next line +// @ts-strict-ignore +import { Component } from "@angular/core"; + +import { OrganizationFilterComponent as BaseOrganizationFilterComponent } from "@bitwarden/angular/vault/vault-filter/components/organization-filter.component"; +import { DisplayMode } from "@bitwarden/angular/vault/vault-filter/models/display-mode"; +import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { ToastService } from "@bitwarden/components"; + +// 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", + standalone: false, +}) +export class OrganizationFilterComponent extends BaseOrganizationFilterComponent { + get show() { + const hiddenDisplayModes: DisplayMode[] = [ + "singleOrganizationAndOrganizatonDataOwnershipPolicies", + ]; + return ( + !this.hide && + this.organizations.length > 0 && + hiddenDisplayModes.indexOf(this.displayMode) === -1 + ); + } + + constructor( + private i18nService: I18nService, + private platformUtilsService: PlatformUtilsService, + private toastService: ToastService, + ) { + super(); + } + + async applyOrganizationFilter(organization: Organization) { + if (organization.enabled) { + //proceed with default behaviour for enabled organizations + // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. + // eslint-disable-next-line @typescript-eslint/no-floating-promises + super.applyOrganizationFilter(organization); + } else { + this.toastService.showToast({ + variant: "error", + title: null, + message: this.i18nService.t("disabledOrganizationFilterError"), + }); + } + } +} diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.html new file mode 100644 index 00000000000..8b064778444 --- /dev/null +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.html @@ -0,0 +1,68 @@ + +

{{ "filters" | i18n }}

+
    +
  • + + + +
  • +
  • + + + +
  • +
  • + + + + @if (!(canArchive$ | async)) { + + } +
  • +
  • + + + +
  • +
+
diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.spec.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.spec.ts new file mode 100644 index 00000000000..ba785310a0a --- /dev/null +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.spec.ts @@ -0,0 +1,98 @@ +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { mock } from "jest-mock-extended"; +import { of } from "rxjs"; + +import { PremiumBadgeComponent } from "@bitwarden/angular/billing/components/premium-badge"; +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { VaultFilter } from "@bitwarden/angular/vault/vault-filter/models/vault-filter.model"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec"; +import { UserId } from "@bitwarden/common/types/guid"; +import { CipherArchiveService } from "@bitwarden/common/vault/abstractions/cipher-archive.service"; +import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; +import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; + +import { StatusFilterComponent } from "./status-filter.component"; + +describe("StatusFilterComponent", () => { + let component: StatusFilterComponent; + let fixture: ComponentFixture; + let cipherArchiveService: jest.Mocked; + let accountService: FakeAccountService; + + const mockUserId = Utils.newGuid() as UserId; + const event = new Event("click"); + + beforeEach(async () => { + accountService = mockAccountServiceWith(mockUserId); + cipherArchiveService = mock(); + + await TestBed.configureTestingModule({ + declarations: [StatusFilterComponent], + providers: [ + { provide: AccountService, useValue: accountService }, + { provide: CipherArchiveService, useValue: cipherArchiveService }, + { provide: PremiumUpgradePromptService, useValue: mock() }, + { + provide: BillingAccountProfileStateService, + useValue: mock(), + }, + { provide: I18nService, useValue: { t: (key: string) => key } }, + ], + imports: [JslibModule, PremiumBadgeComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(StatusFilterComponent); + component = fixture.componentInstance; + component.activeFilter = new VaultFilter(); + fixture.detectChanges(); + }); + + describe("handleArchiveFilter", () => { + const applyFilter = jest.fn(); + let promptForPremiumSpy: jest.SpyInstance; + + beforeEach(() => { + applyFilter.mockClear(); + component["applyFilter"] = applyFilter; + + promptForPremiumSpy = jest.spyOn(component["premiumBadgeComponent"]()!, "promptForPremium"); + }); + + it("should apply archive filter when userCanArchive returns true", async () => { + cipherArchiveService.userCanArchive$.mockReturnValue(of(true)); + cipherArchiveService.archivedCiphers$.mockReturnValue(of([])); + + await component["handleArchiveFilter"](event); + + expect(applyFilter).toHaveBeenCalledWith("archive"); + expect(promptForPremiumSpy).not.toHaveBeenCalled(); + }); + + it("should apply archive filter when userCanArchive returns false but hasArchivedCiphers is true", async () => { + const mockCipher = new CipherView(); + mockCipher.id = "test-id"; + + cipherArchiveService.userCanArchive$.mockReturnValue(of(false)); + cipherArchiveService.archivedCiphers$.mockReturnValue(of([mockCipher])); + + await component["handleArchiveFilter"](event); + + expect(applyFilter).toHaveBeenCalledWith("archive"); + expect(promptForPremiumSpy).not.toHaveBeenCalled(); + }); + + it("should prompt for premium when userCanArchive returns false and hasArchivedCiphers is false", async () => { + cipherArchiveService.userCanArchive$.mockReturnValue(of(false)); + cipherArchiveService.archivedCiphers$.mockReturnValue(of([])); + + await component["handleArchiveFilter"](event); + + expect(applyFilter).not.toHaveBeenCalled(); + expect(promptForPremiumSpy).toHaveBeenCalled(); + }); + }); +}); diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts new file mode 100644 index 00000000000..95ffd3f0212 --- /dev/null +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts @@ -0,0 +1,51 @@ +import { Component, viewChild } from "@angular/core"; +import { combineLatest, firstValueFrom, map, switchMap } from "rxjs"; + +import { PremiumBadgeComponent } from "@bitwarden/angular/billing/components/premium-badge"; +import { StatusFilterComponent as BaseStatusFilterComponent } from "@bitwarden/angular/vault/vault-filter/components/status-filter.component"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { getUserId } from "@bitwarden/common/auth/services/account.service"; +import { CipherArchiveService } from "@bitwarden/common/vault/abstractions/cipher-archive.service"; + +// 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-status-filter", + templateUrl: "status-filter.component.html", + standalone: false, +}) +export class StatusFilterComponent extends BaseStatusFilterComponent { + private readonly premiumBadgeComponent = viewChild(PremiumBadgeComponent); + + private userId$ = this.accountService.activeAccount$.pipe(getUserId); + protected canArchive$ = this.userId$.pipe( + switchMap((userId) => this.cipherArchiveService.userCanArchive$(userId)), + ); + + protected hasArchivedCiphers$ = this.userId$.pipe( + switchMap((userId) => + this.cipherArchiveService.archivedCiphers$(userId).pipe(map((ciphers) => ciphers.length > 0)), + ), + ); + + constructor( + private accountService: AccountService, + private cipherArchiveService: CipherArchiveService, + ) { + super(); + } + + protected async handleArchiveFilter(event: Event) { + const [canArchive, hasArchivedCiphers] = await firstValueFrom( + combineLatest([this.canArchive$, this.hasArchivedCiphers$]), + ); + + if (canArchive || hasArchivedCiphers) { + this.applyFilter("archive"); + } else if (this.premiumBadgeComponent()) { + // The `premiumBadgeComponent` should always be defined here, adding the + // if to satisfy TypeScript. + await this.premiumBadgeComponent().promptForPremium(event); + } + } +} diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.html new file mode 100644 index 00000000000..f8a83e01266 --- /dev/null +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.html @@ -0,0 +1,39 @@ +
+

+ +

+
+
    + @for (typeFilter of typeFilters$ | async; track typeFilter) { +
  • + + + +
  • + } +
diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.ts new file mode 100644 index 00000000000..fbab7ce4667 --- /dev/null +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.ts @@ -0,0 +1,34 @@ +import { Component } from "@angular/core"; +import { map, shareReplay } from "rxjs"; + +import { TypeFilterComponent as BaseTypeFilterComponent } from "@bitwarden/angular/vault/vault-filter/components/type-filter.component"; +import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service"; +import { CIPHER_MENU_ITEMS } from "@bitwarden/common/vault/types/cipher-menu-items"; + +// 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-type-filter", + templateUrl: "type-filter.component.html", + standalone: false, +}) +export class TypeFilterComponent extends BaseTypeFilterComponent { + protected typeFilters$ = this.restrictedItemTypesService.restricted$.pipe( + map((restrictedItemTypes) => + // Filter out restricted item types from the typeFilters array + CIPHER_MENU_ITEMS.filter( + (typeFilter) => + !restrictedItemTypes.some( + (restrictedType) => + restrictedType.allowViewOrgIds.length === 0 && + restrictedType.cipherType === typeFilter.type, + ), + ), + ), + shareReplay({ bufferSize: 1, refCount: true }), + ); + + constructor(private restrictedItemTypesService: RestrictedItemTypesService) { + super(); + } +} diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.html new file mode 100644 index 00000000000..14e72f3bb9d --- /dev/null +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.html @@ -0,0 +1,51 @@ +
+ +
+ + + + + + + diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts new file mode 100644 index 00000000000..d7c5bafc3a4 --- /dev/null +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts @@ -0,0 +1,12 @@ +import { Component } from "@angular/core"; + +import { VaultFilterComponent as BaseVaultFilterComponent } from "@bitwarden/angular/vault/vault-filter/components/vault-filter.component"; + +// 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-vault-filter", + templateUrl: "vault-filter.component.html", + standalone: false, +}) +export class VaultFilterComponent extends BaseVaultFilterComponent {} diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.module.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.module.ts new file mode 100644 index 00000000000..54a6d33ca6a --- /dev/null +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.module.ts @@ -0,0 +1,34 @@ +import { CommonModule } from "@angular/common"; +import { NgModule } from "@angular/core"; + +import { PremiumBadgeComponent } from "@bitwarden/angular/billing/components/premium-badge"; +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { DeprecatedVaultFilterService as DeprecatedVaultFilterServiceAbstraction } from "@bitwarden/angular/vault/abstractions/deprecated-vault-filter.service"; +import { VaultFilterService } from "@bitwarden/angular/vault/vault-filter/services/vault-filter.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"; +import { TypeFilterComponent } from "./filters/type-filter.component"; +import { VaultFilterComponent } from "./vault-filter.component"; + +@NgModule({ + imports: [CommonModule, JslibModule, PremiumBadgeComponent], + declarations: [ + VaultFilterComponent, + CollectionFilterComponent, + FolderFilterComponent, + OrganizationFilterComponent, + StatusFilterComponent, + TypeFilterComponent, + ], + exports: [VaultFilterComponent], + providers: [ + { + provide: DeprecatedVaultFilterServiceAbstraction, + useClass: VaultFilterService, + }, + ], +}) +export class VaultFilterModule {} From bbf20eaf5a48b82be681f83b30edac3764e55f35 Mon Sep 17 00:00:00 2001 From: Leslie Xiong Date: Tue, 2 Dec 2025 11:52:33 -0500 Subject: [PATCH 15/56] created `VaultStateService` --- .../src/services/vault-state.service.ts | 108 ++++++++++++++++++ .../app/vault-v3/nav/vault-nav.component.html | 10 +- .../app/vault-v3/nav/vault-nav.component.ts | 9 +- .../src/vault/app/vault-v3/vault.component.ts | 26 +++++ 4 files changed, 149 insertions(+), 4 deletions(-) create mode 100644 apps/desktop/src/services/vault-state.service.ts diff --git a/apps/desktop/src/services/vault-state.service.ts b/apps/desktop/src/services/vault-state.service.ts new file mode 100644 index 00000000000..e262ba2dc0e --- /dev/null +++ b/apps/desktop/src/services/vault-state.service.ts @@ -0,0 +1,108 @@ +import { Injectable } from "@angular/core"; +import { Subject } from "rxjs"; + +import { VaultFilter } from "@bitwarden/angular/vault/vault-filter/models/vault-filter.model"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; + +import { SearchBarService } from "../app/layout/search/search-bar.service"; + +/** + * Service to coordinate vault state, including filter state and folder actions, + * between the navigation component and the vault component. + */ +@Injectable({ providedIn: "root" }) +export class VaultStateService { + private filterChangeSubject = new Subject(); + private addFolderSubject = new Subject(); + private editFolderSubject = new Subject(); + + /** + * The currently active vault filter. + */ + activeFilter: VaultFilter = new VaultFilter(); + + /** + * Observable stream of vault filter changes. + * Subscribe to this to react to filter changes from the navigation. + */ + readonly filterChange$ = this.filterChangeSubject.asObservable(); + + /** + * Observable stream of add folder requests. + * Subscribe to this to handle folder creation. + */ + readonly addFolder$ = this.addFolderSubject.asObservable(); + + /** + * Observable stream of edit folder requests. + * Subscribe to this to handle folder editing. + * Emits the folder ID to edit. + */ + readonly editFolder$ = this.editFolderSubject.asObservable(); + + constructor( + private i18nService: I18nService, + private searchBarService: SearchBarService, + ) {} + + /** + * Apply a new vault filter. + * This updates the search bar placeholder and notifies all subscribers. + */ + applyFilter(filter: VaultFilter): void { + // Store the active filter + this.activeFilter = filter; + + // Update search bar placeholder text based on the filter + this.searchBarService.setPlaceholderText( + this.i18nService.t(this.calculateSearchBarLocalizationString(filter)), + ); + + // Emit the filter change to subscribers + this.filterChangeSubject.next(filter); + } + + /** + * Request to add a new folder. + * This will notify subscribers to show the folder creation dialog. + */ + requestAddFolder(): void { + this.addFolderSubject.next(); + } + + /** + * Request to edit an existing folder. + * This will notify subscribers to show the folder edit dialog. + */ + requestEditFolder(folderId: string): void { + this.editFolderSubject.next(folderId); + } + + /** + * Calculate the appropriate search bar localization string based on the active filter. + */ + private calculateSearchBarLocalizationString(vaultFilter: VaultFilter): string { + if (vaultFilter.status === "favorites") { + return "searchFavorites"; + } + if (vaultFilter.status === "trash") { + return "searchTrash"; + } + if (vaultFilter.cipherType != null) { + return "searchType"; + } + if (vaultFilter.selectedFolderId != null && vaultFilter.selectedFolderId !== "none") { + return "searchFolder"; + } + if (vaultFilter.selectedCollectionId != null) { + return "searchCollection"; + } + if (vaultFilter.selectedOrganizationId != null) { + return "searchOrganization"; + } + if (vaultFilter.myVaultOnly) { + return "searchMyVault"; + } + return "searchVault"; + } +} diff --git a/apps/desktop/src/vault/app/vault-v3/nav/vault-nav.component.html b/apps/desktop/src/vault/app/vault-v3/nav/vault-nav.component.html index d1c0399c1b2..3c8e5eb16da 100644 --- a/apps/desktop/src/vault/app/vault-v3/nav/vault-nav.component.html +++ b/apps/desktop/src/vault/app/vault-v3/nav/vault-nav.component.html @@ -1 +1,9 @@ - + + + diff --git a/apps/desktop/src/vault/app/vault-v3/nav/vault-nav.component.ts b/apps/desktop/src/vault/app/vault-v3/nav/vault-nav.component.ts index 6169124e76b..d30be43ecb8 100644 --- a/apps/desktop/src/vault/app/vault-v3/nav/vault-nav.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/nav/vault-nav.component.ts @@ -1,12 +1,15 @@ import { ChangeDetectionStrategy, Component } from "@angular/core"; - import { NavigationModule } from "@bitwarden/components"; import { I18nPipe } from "@bitwarden/ui-common"; +import { VaultStateService } from "../../../../services/vault-state.service"; +import { VaultFilterModule } from "../vault-filter/vault-filter.module"; @Component({ selector: "app-vault-nav", - imports: [I18nPipe, NavigationModule], + imports: [I18nPipe, NavigationModule, VaultFilterModule], templateUrl: "./vault-nav.component.html", changeDetection: ChangeDetectionStrategy.OnPush, }) -export class VaultNavComponent {} +export class VaultNavComponent { + constructor(protected vaultStateService: VaultStateService) {} +} diff --git a/apps/desktop/src/vault/app/vault-v3/vault.component.ts b/apps/desktop/src/vault/app/vault-v3/vault.component.ts index 21ba7547f8b..6cb5eb05379 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault.component.ts @@ -83,6 +83,7 @@ import { import { SearchBarService } from "../../../app/layout/search/search-bar.service"; import { DesktopCredentialGenerationService } from "../../../services/desktop-cipher-form-generator.service"; import { DesktopPremiumUpgradePromptService } from "../../../services/desktop-premium-upgrade-prompt.service"; +import { VaultStateService } from "../../../services/vault-state.service"; import { invokeMenu, RendererMenuItem } from "../../../utils"; import { AssignCollectionsDesktopComponent } from "../vault/assign-collections"; import { ItemFooterComponent } from "../vault/item-footer.component"; @@ -225,6 +226,7 @@ export class VaultComponent private cipherArchiveService: CipherArchiveService, private policyService: PolicyService, private archiveCipherUtilitiesService: ArchiveCipherUtilitiesService, + private vaultStateService: VaultStateService, ) {} async ngOnInit() { @@ -240,6 +242,30 @@ export class VaultComponent this.userHasPremiumAccess = canAccessPremium; }); + // Subscribe to filter changes from VaultNavComponent + this.vaultStateService.filterChange$ + .pipe( + switchMap((vaultFilter: VaultFilter) => this.applyVaultFilter(vaultFilter)), + takeUntil(this.componentIsDestroyed$), + ) + .subscribe(); + + // Subscribe to add folder requests from VaultNavComponent + this.vaultStateService.addFolder$ + .pipe( + switchMap(() => this.addFolder()), + takeUntil(this.componentIsDestroyed$), + ) + .subscribe(); + + // Subscribe to edit folder requests from VaultNavComponent + this.vaultStateService.editFolder$ + .pipe( + switchMap((folderId: string) => this.editFolder(folderId)), + takeUntil(this.componentIsDestroyed$), + ) + .subscribe(); + this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => { this.ngZone .run(async () => { From f9635b55ec6d95effb4d30caf2603c8a86582d93 Mon Sep 17 00:00:00 2001 From: Leslie Xiong Date: Wed, 10 Dec 2025 17:51:06 -0500 Subject: [PATCH 16/56] - moved `vault-filter` services and models, and `collection utils` from web-vault to shared lib --- .../organizations/collections/index.ts | 1 - .../vault-filter/vault-filter.component.ts | 10 +++---- .../vault-filter/vault-filter.module.ts | 2 +- .../vault-filter/vault-filter.service.ts | 4 +-- .../collections/vault.component.ts | 21 ++++++++------- .../inactive-two-factor-report.component.ts | 9 ++++--- .../src/app/dirt/reports/reports.module.ts | 9 ++++--- .../organization-options.component.ts | 2 +- .../components/vault-filter.component.ts | 11 +++----- .../vault-filter-section.component.ts | 10 ++++--- .../vault-filter/vault-filter.module.ts | 3 +-- .../vault-header/vault-header.component.ts | 6 +---- .../vault/individual-vault/vault.component.ts | 27 +++++++++---------- .../utils/collection-utils.spec.ts | 1 + .../admin-console}/utils/collection-utils.ts | 1 + .../common/src/admin-console}/utils/index.ts | 0 .../src}/abstractions/vault-filter.service.ts | 3 ++- libs/vault/src/index.ts | 11 ++++++++ .../vault/src}/models/filter-function.spec.ts | 1 + .../vault/src}/models/filter-function.ts | 1 + .../routed-vault-filter-bridge.model.ts | 3 ++- .../src}/models/routed-vault-filter.model.ts | 1 + .../src}/models/vault-filter-section.type.ts | 0 .../src}/models/vault-filter.model.spec.ts | 1 + .../vault/src}/models/vault-filter.model.ts | 0 .../vault/src}/models/vault-filter.type.ts | 1 + .../routed-vault-filter-bridge.service.ts | 16 +++++------ .../services/routed-vault-filter.service.ts | 2 +- .../services/vault-filter.service.spec.ts | 1 + .../src}/services/vault-filter.service.ts | 9 +++---- 30 files changed, 92 insertions(+), 75 deletions(-) rename {apps/web/src/app/admin-console/organizations/collections => libs/common/src/admin-console}/utils/collection-utils.spec.ts (98%) rename {apps/web/src/app/admin-console/organizations/collections => libs/common/src/admin-console}/utils/collection-utils.ts (98%) rename {apps/web/src/app/admin-console/organizations/collections => libs/common/src/admin-console}/utils/index.ts (100%) rename {apps/web/src/app/vault/individual-vault/vault-filter/services => libs/vault/src}/abstractions/vault-filter.service.ts (95%) rename {apps/web/src/app/vault/individual-vault/vault-filter/shared => libs/vault/src}/models/filter-function.spec.ts (99%) rename {apps/web/src/app/vault/individual-vault/vault-filter/shared => libs/vault/src}/models/filter-function.ts (98%) rename {apps/web/src/app/vault/individual-vault/vault-filter/shared => libs/vault/src}/models/routed-vault-filter-bridge.model.ts (97%) rename {apps/web/src/app/vault/individual-vault/vault-filter/shared => libs/vault/src}/models/routed-vault-filter.model.ts (94%) rename {apps/web/src/app/vault/individual-vault/vault-filter/shared => libs/vault/src}/models/vault-filter-section.type.ts (100%) rename {apps/web/src/app/vault/individual-vault/vault-filter/shared => libs/vault/src}/models/vault-filter.model.spec.ts (99%) rename {apps/web/src/app/vault/individual-vault/vault-filter/shared => libs/vault/src}/models/vault-filter.model.ts (100%) rename {apps/web/src/app/vault/individual-vault/vault-filter/shared => libs/vault/src}/models/vault-filter.type.ts (94%) rename {apps/web/src/app/vault/individual-vault/vault-filter => libs/vault/src}/services/routed-vault-filter-bridge.service.ts (93%) rename {apps/web/src/app/vault/individual-vault/vault-filter => libs/vault/src}/services/routed-vault-filter.service.ts (98%) rename {apps/web/src/app/vault/individual-vault/vault-filter => libs/vault/src}/services/vault-filter.service.spec.ts (99%) rename {apps/web/src/app/vault/individual-vault/vault-filter => libs/vault/src}/services/vault-filter.service.ts (98%) diff --git a/apps/web/src/app/admin-console/organizations/collections/index.ts b/apps/web/src/app/admin-console/organizations/collections/index.ts index 57f936ab590..e85b3efcab7 100644 --- a/apps/web/src/app/admin-console/organizations/collections/index.ts +++ b/apps/web/src/app/admin-console/organizations/collections/index.ts @@ -1,2 +1 @@ -export * from "./utils"; export * from "./collection-badge"; diff --git a/apps/web/src/app/admin-console/organizations/collections/vault-filter/vault-filter.component.ts b/apps/web/src/app/admin-console/organizations/collections/vault-filter/vault-filter.component.ts index 01e61f0ab28..6db8fa5e4d6 100644 --- a/apps/web/src/app/admin-console/organizations/collections/vault-filter/vault-filter.component.ts +++ b/apps/web/src/app/admin-console/organizations/collections/vault-filter/vault-filter.component.ts @@ -14,15 +14,15 @@ import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.servi import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service"; import { DialogService, ToastService } from "@bitwarden/components"; - -import { VaultFilterComponent as BaseVaultFilterComponent } from "../../../../vault/individual-vault/vault-filter/components/vault-filter.component"; -import { VaultFilterService } from "../../../../vault/individual-vault/vault-filter/services/abstractions/vault-filter.service"; import { + VaultFilterService, VaultFilterList, VaultFilterSection, VaultFilterType, -} from "../../../../vault/individual-vault/vault-filter/shared/models/vault-filter-section.type"; -import { CollectionFilter } from "../../../../vault/individual-vault/vault-filter/shared/models/vault-filter.type"; + CollectionFilter, +} from "@bitwarden/vault"; + +import { VaultFilterComponent as BaseVaultFilterComponent } from "../../../../vault/individual-vault/vault-filter/components/vault-filter.component"; // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection diff --git a/apps/web/src/app/admin-console/organizations/collections/vault-filter/vault-filter.module.ts b/apps/web/src/app/admin-console/organizations/collections/vault-filter/vault-filter.module.ts index a0dba839b22..97b838216c3 100644 --- a/apps/web/src/app/admin-console/organizations/collections/vault-filter/vault-filter.module.ts +++ b/apps/web/src/app/admin-console/organizations/collections/vault-filter/vault-filter.module.ts @@ -1,8 +1,8 @@ import { NgModule } from "@angular/core"; import { SearchModule } from "@bitwarden/components"; +import { VaultFilterServiceAbstraction } from "@bitwarden/vault"; -import { VaultFilterService as VaultFilterServiceAbstraction } from "../../../../vault/individual-vault/vault-filter/services/abstractions/vault-filter.service"; import { VaultFilterSharedModule } from "../../../../vault/individual-vault/vault-filter/shared/vault-filter-shared.module"; import { VaultFilterComponent } from "./vault-filter.component"; diff --git a/apps/web/src/app/admin-console/organizations/collections/vault-filter/vault-filter.service.ts b/apps/web/src/app/admin-console/organizations/collections/vault-filter/vault-filter.service.ts index dc05248d7ba..2967edf5ccc 100644 --- a/apps/web/src/app/admin-console/organizations/collections/vault-filter/vault-filter.service.ts +++ b/apps/web/src/app/admin-console/organizations/collections/vault-filter/vault-filter.service.ts @@ -11,9 +11,7 @@ import { StateProvider } from "@bitwarden/common/platform/state"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; - -import { VaultFilterService as BaseVaultFilterService } from "../../../../vault/individual-vault/vault-filter/services/vault-filter.service"; -import { CollectionFilter } from "../../../../vault/individual-vault/vault-filter/shared/models/vault-filter.type"; +import { VaultFilterService as BaseVaultFilterService, CollectionFilter } from "@bitwarden/vault"; @Injectable() export class VaultFilterService extends BaseVaultFilterService implements OnDestroy { diff --git a/apps/web/src/app/admin-console/organizations/collections/vault.component.ts b/apps/web/src/app/admin-console/organizations/collections/vault.component.ts index f827dda9a9b..f3b5858597b 100644 --- a/apps/web/src/app/admin-console/organizations/collections/vault.component.ts +++ b/apps/web/src/app/admin-console/organizations/collections/vault.component.ts @@ -40,6 +40,10 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { + getFlatCollectionTree, + getNestedCollectionTree, +} from "@bitwarden/common/admin-console/utils"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billing-api.service.abstraction"; @@ -81,6 +85,13 @@ import { CollectionAssignmentResult, DecryptionFailureDialogComponent, PasswordRepromptService, + VaultFilterServiceAbstraction as VaultFilterService, + RoutedVaultFilterBridgeService, + RoutedVaultFilterService, + createFilterFunction, + All, + RoutedVaultFilterModel, + VaultFilter, } from "@bitwarden/vault"; import { OrganizationFreeTrialWarningComponent, @@ -102,15 +113,6 @@ import { BulkDeleteDialogResult, openBulkDeleteDialog, } from "../../../vault/individual-vault/bulk-action-dialogs/bulk-delete-dialog/bulk-delete-dialog.component"; -import { VaultFilterService } from "../../../vault/individual-vault/vault-filter/services/abstractions/vault-filter.service"; -import { RoutedVaultFilterBridgeService } from "../../../vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service"; -import { RoutedVaultFilterService } from "../../../vault/individual-vault/vault-filter/services/routed-vault-filter.service"; -import { createFilterFunction } from "../../../vault/individual-vault/vault-filter/shared/models/filter-function"; -import { - All, - RoutedVaultFilterModel, -} from "../../../vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model"; -import { VaultFilter } from "../../../vault/individual-vault/vault-filter/shared/models/vault-filter.model"; import { AdminConsoleCipherFormConfigService } from "../../../vault/org-vault/services/admin-console-cipher-form-config.service"; import { GroupApiService, GroupView } from "../core"; import { openEntityEventsDialog } from "../manage/entity-events.component"; @@ -126,7 +128,6 @@ import { BulkCollectionsDialogResult, } from "./bulk-collections-dialog"; import { CollectionAccessRestrictedComponent } from "./collection-access-restricted.component"; -import { getFlatCollectionTree, getNestedCollectionTree } from "./utils"; import { VaultFilterModule } from "./vault-filter/vault-filter.module"; import { VaultHeaderComponent } from "./vault-header/vault-header.component"; diff --git a/apps/web/src/app/dirt/reports/pages/organizations/inactive-two-factor-report.component.ts b/apps/web/src/app/dirt/reports/pages/organizations/inactive-two-factor-report.component.ts index b1adbd26eb3..23d1330dad7 100644 --- a/apps/web/src/app/dirt/reports/pages/organizations/inactive-two-factor-report.component.ts +++ b/apps/web/src/app/dirt/reports/pages/organizations/inactive-two-factor-report.component.ts @@ -12,14 +12,17 @@ import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.serv import { Cipher } from "@bitwarden/common/vault/models/domain/cipher"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { DialogService } from "@bitwarden/components"; -import { CipherFormConfigService, PasswordRepromptService } from "@bitwarden/vault"; +import { + CipherFormConfigService, + PasswordRepromptService, + RoutedVaultFilterBridgeService, + RoutedVaultFilterService, +} from "@bitwarden/vault"; import { HeaderModule } from "../../../../layouts/header/header.module"; import { SharedModule } from "../../../../shared"; import { OrganizationBadgeModule } from "../../../../vault/individual-vault/organization-badge/organization-badge.module"; import { PipesModule } from "../../../../vault/individual-vault/pipes/pipes.module"; -import { RoutedVaultFilterBridgeService } from "../../../../vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service"; -import { RoutedVaultFilterService } from "../../../../vault/individual-vault/vault-filter/services/routed-vault-filter.service"; import { AdminConsoleCipherFormConfigService } from "../../../../vault/org-vault/services/admin-console-cipher-form-config.service"; import { InactiveTwoFactorReportComponent as BaseInactiveTwoFactorReportComponent } from "../inactive-two-factor-report.component"; diff --git a/apps/web/src/app/dirt/reports/reports.module.ts b/apps/web/src/app/dirt/reports/reports.module.ts index 358768e71ee..5648b40982a 100644 --- a/apps/web/src/app/dirt/reports/reports.module.ts +++ b/apps/web/src/app/dirt/reports/reports.module.ts @@ -1,14 +1,17 @@ import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; -import { CipherFormConfigService, DefaultCipherFormConfigService } from "@bitwarden/vault"; +import { + CipherFormConfigService, + DefaultCipherFormConfigService, + RoutedVaultFilterBridgeService, + RoutedVaultFilterService, +} from "@bitwarden/vault"; import { HeaderModule } from "../../layouts/header/header.module"; import { SharedModule } from "../../shared"; import { OrganizationBadgeModule } from "../../vault/individual-vault/organization-badge/organization-badge.module"; import { PipesModule } from "../../vault/individual-vault/pipes/pipes.module"; -import { RoutedVaultFilterBridgeService } from "../../vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service"; -import { RoutedVaultFilterService } from "../../vault/individual-vault/vault-filter/services/routed-vault-filter.service"; import { AdminConsoleCipherFormConfigService } from "../../vault/org-vault/services/admin-console-cipher-form-config.service"; import { BreachReportComponent } from "./pages/breach-report.component"; diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/components/organization-options.component.ts b/apps/web/src/app/vault/individual-vault/vault-filter/components/organization-options.component.ts index 37b881406e3..d23fc7f958a 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/components/organization-options.component.ts +++ b/apps/web/src/app/vault/individual-vault/vault-filter/components/organization-options.component.ts @@ -32,12 +32,12 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { SyncService } from "@bitwarden/common/platform/sync"; import { DialogService, ToastService } from "@bitwarden/components"; import { KeyService } from "@bitwarden/key-management"; +import { OrganizationFilter } from "@bitwarden/vault"; import { OrganizationUserResetPasswordService } from "../../../../admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service"; import { EnrollMasterPasswordReset } from "../../../../admin-console/organizations/users/enroll-master-password-reset.component"; import { LinkSsoService } from "../../../../auth/core/services"; import { OptionsInput } from "../shared/components/vault-filter-section.component"; -import { OrganizationFilter } from "../shared/models/vault-filter.type"; // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/components/vault-filter.component.ts b/apps/web/src/app/vault/individual-vault/vault-filter/components/vault-filter.component.ts index e40a32dc8b9..82f71e50ce6 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/components/vault-filter.component.ts +++ b/apps/web/src/app/vault/individual-vault/vault-filter/components/vault-filter.component.ts @@ -25,22 +25,19 @@ import { CipherType } from "@bitwarden/common/vault/enums"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service"; import { DialogService, ToastService } from "@bitwarden/components"; -import { OrganizationWarningsService } from "@bitwarden/web-vault/app/billing/organizations/warnings/services"; - -import { VaultFilterService } from "../services/abstractions/vault-filter.service"; import { + VaultFilterServiceAbstraction as VaultFilterService, VaultFilterList, VaultFilterSection, VaultFilterType, -} from "../shared/models/vault-filter-section.type"; -import { VaultFilter } from "../shared/models/vault-filter.model"; -import { + VaultFilter, CipherStatus, CipherTypeFilter, CollectionFilter, FolderFilter, OrganizationFilter, -} from "../shared/models/vault-filter.type"; +} from "@bitwarden/vault"; +import { OrganizationWarningsService } from "@bitwarden/web-vault/app/billing/organizations/warnings/services"; import { OrganizationOptionsComponent } from "./organization-options.component"; diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/shared/components/vault-filter-section.component.ts b/apps/web/src/app/vault/individual-vault/vault-filter/shared/components/vault-filter-section.component.ts index e8cf49c3208..6ea2ad469b1 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/shared/components/vault-filter-section.component.ts +++ b/apps/web/src/app/vault/individual-vault/vault-filter/shared/components/vault-filter-section.component.ts @@ -8,10 +8,12 @@ import { Organization } from "@bitwarden/common/admin-console/models/domain/orga import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { ITreeNodeObject, TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; - -import { VaultFilterService } from "../../services/abstractions/vault-filter.service"; -import { VaultFilterSection, VaultFilterType } from "../models/vault-filter-section.type"; -import { VaultFilter } from "../models/vault-filter.model"; +import { + VaultFilterServiceAbstraction as VaultFilterService, + VaultFilterSection, + VaultFilterType, + VaultFilter, +} 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 diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/vault-filter.module.ts b/apps/web/src/app/vault/individual-vault/vault-filter/vault-filter.module.ts index dc70561bcb2..4d98bcd42bc 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/vault-filter.module.ts +++ b/apps/web/src/app/vault/individual-vault/vault-filter/vault-filter.module.ts @@ -1,14 +1,13 @@ import { NgModule } from "@angular/core"; import { SearchModule } from "@bitwarden/components"; +import { VaultFilterServiceAbstraction, VaultFilterService } from "@bitwarden/vault"; import { OrganizationWarningsModule } from "@bitwarden/web-vault/app/billing/organizations/warnings/organization-warnings.module"; import { VaultFilterSharedModule } from "../../individual-vault/vault-filter/shared/vault-filter-shared.module"; import { OrganizationOptionsComponent } from "./components/organization-options.component"; import { VaultFilterComponent } from "./components/vault-filter.component"; -import { VaultFilterService as VaultFilterServiceAbstraction } from "./services/abstractions/vault-filter.service"; -import { VaultFilterService } from "./services/vault-filter.service"; @NgModule({ imports: [VaultFilterSharedModule, SearchModule, OrganizationWarningsModule], diff --git a/apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.ts b/apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.ts index 8fa801f5dc0..b4dbc5cfb1e 100644 --- a/apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.ts +++ b/apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.ts @@ -24,16 +24,12 @@ import { MenuModule, SimpleDialogOptions, } from "@bitwarden/components"; -import { NewCipherMenuComponent } from "@bitwarden/vault"; +import { NewCipherMenuComponent, All, RoutedVaultFilterModel } from "@bitwarden/vault"; import { CollectionDialogTabType } from "../../../admin-console/organizations/shared/components/collection-dialog"; import { HeaderModule } from "../../../layouts/header/header.module"; import { SharedModule } from "../../../shared"; import { PipesModule } from "../pipes/pipes.module"; -import { - All, - RoutedVaultFilterModel, -} from "../vault-filter/shared/models/routed-vault-filter.model"; @Component({ selector: "app-vault-header", diff --git a/apps/web/src/app/vault/individual-vault/vault.component.ts b/apps/web/src/app/vault/individual-vault/vault.component.ts index 3b0a7a6f141..8122e96eb64 100644 --- a/apps/web/src/app/vault/individual-vault/vault.component.ts +++ b/apps/web/src/app/vault/individual-vault/vault.component.ts @@ -51,6 +51,10 @@ import { import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { + getNestedCollectionTree, + getFlatCollectionTree, +} from "@bitwarden/common/admin-console/utils"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; @@ -97,15 +101,20 @@ import { DecryptionFailureDialogComponent, DefaultCipherFormConfigService, PasswordRepromptService, + VaultFilterServiceAbstraction as VaultFilterService, + RoutedVaultFilterBridgeService, + RoutedVaultFilterService, + createFilterFunction, + All, + RoutedVaultFilterModel, + VaultFilter, + FolderFilter, + OrganizationFilter, } from "@bitwarden/vault"; import { UnifiedUpgradePromptService } from "@bitwarden/web-vault/app/billing/individual/upgrade/services"; import { OrganizationWarningsModule } from "@bitwarden/web-vault/app/billing/organizations/warnings/organization-warnings.module"; import { OrganizationWarningsService } from "@bitwarden/web-vault/app/billing/organizations/warnings/services"; -import { - getNestedCollectionTree, - getFlatCollectionTree, -} from "../../admin-console/organizations/collections"; import { AutoConfirmPolicy, AutoConfirmPolicyDialogComponent, @@ -138,16 +147,6 @@ import { } from "./bulk-action-dialogs/bulk-move-dialog/bulk-move-dialog.component"; import { VaultBannersComponent } from "./vault-banners/vault-banners.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 { RoutedVaultFilterService } from "./vault-filter/services/routed-vault-filter.service"; -import { createFilterFunction } from "./vault-filter/shared/models/filter-function"; -import { - All, - RoutedVaultFilterModel, -} from "./vault-filter/shared/models/routed-vault-filter.model"; -import { VaultFilter } from "./vault-filter/shared/models/vault-filter.model"; -import { FolderFilter, OrganizationFilter } from "./vault-filter/shared/models/vault-filter.type"; import { VaultFilterModule } from "./vault-filter/vault-filter.module"; import { VaultHeaderComponent } from "./vault-header/vault-header.component"; import { VaultOnboardingComponent } from "./vault-onboarding/vault-onboarding.component"; diff --git a/apps/web/src/app/admin-console/organizations/collections/utils/collection-utils.spec.ts b/libs/common/src/admin-console/utils/collection-utils.spec.ts similarity index 98% rename from apps/web/src/app/admin-console/organizations/collections/utils/collection-utils.spec.ts rename to libs/common/src/admin-console/utils/collection-utils.spec.ts index ad3d0d8169a..19360e1a87b 100644 --- a/apps/web/src/app/admin-console/organizations/collections/utils/collection-utils.spec.ts +++ b/libs/common/src/admin-console/utils/collection-utils.spec.ts @@ -1,3 +1,4 @@ +// eslint-disable-next-line no-restricted-imports import { CollectionView } from "@bitwarden/admin-console/common"; import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; diff --git a/apps/web/src/app/admin-console/organizations/collections/utils/collection-utils.ts b/libs/common/src/admin-console/utils/collection-utils.ts similarity index 98% rename from apps/web/src/app/admin-console/organizations/collections/utils/collection-utils.ts rename to libs/common/src/admin-console/utils/collection-utils.ts index 33325b3a4bd..900821510bf 100644 --- a/apps/web/src/app/admin-console/organizations/collections/utils/collection-utils.ts +++ b/libs/common/src/admin-console/utils/collection-utils.ts @@ -1,5 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore +// eslint-disable-next-line no-restricted-imports import { CollectionAdminView, CollectionView, diff --git a/apps/web/src/app/admin-console/organizations/collections/utils/index.ts b/libs/common/src/admin-console/utils/index.ts similarity index 100% rename from apps/web/src/app/admin-console/organizations/collections/utils/index.ts rename to libs/common/src/admin-console/utils/index.ts diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/services/abstractions/vault-filter.service.ts b/libs/vault/src/abstractions/vault-filter.service.ts similarity index 95% rename from apps/web/src/app/vault/individual-vault/vault-filter/services/abstractions/vault-filter.service.ts rename to libs/vault/src/abstractions/vault-filter.service.ts index 0e3ee69a2c6..b3f9c118555 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/services/abstractions/vault-filter.service.ts +++ b/libs/vault/src/abstractions/vault-filter.service.ts @@ -2,6 +2,7 @@ // @ts-strict-ignore import { Observable } from "rxjs"; +// eslint-disable-next-line no-restricted-imports import { CollectionAdminView, CollectionView } from "@bitwarden/admin-console/common"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { UserId } from "@bitwarden/common/types/guid"; @@ -13,7 +14,7 @@ import { CollectionFilter, FolderFilter, OrganizationFilter, -} from "../../shared/models/vault-filter.type"; +} from "../models/vault-filter.type"; export abstract class VaultFilterService { collapsedFilterNodes$: Observable>; diff --git a/libs/vault/src/index.ts b/libs/vault/src/index.ts index 93a72ba14e0..2503d92299c 100644 --- a/libs/vault/src/index.ts +++ b/libs/vault/src/index.ts @@ -36,3 +36,14 @@ export { SshImportPromptService } from "./services/ssh-import-prompt.service"; export * from "./abstractions/change-login-password.service"; export * from "./services/default-change-login-password.service"; export * from "./services/archive-cipher-utilities.service"; + +export * from "./models/vault-filter.type"; +export * from "./models/vault-filter.model"; +export * from "./models/routed-vault-filter.model"; +export * from "./models/routed-vault-filter-bridge.model"; +export * from "./models/vault-filter-section.type"; +export * from "./models/filter-function"; +export { VaultFilterService as VaultFilterServiceAbstraction } from "./abstractions/vault-filter.service"; +export * from "./services/vault-filter.service"; +export * from "./services/routed-vault-filter.service"; +export * from "./services/routed-vault-filter-bridge.service"; diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/filter-function.spec.ts b/libs/vault/src/models/filter-function.spec.ts similarity index 99% rename from apps/web/src/app/vault/individual-vault/vault-filter/shared/models/filter-function.spec.ts rename to libs/vault/src/models/filter-function.spec.ts index 00c540f6029..5eaa38e6ef1 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/filter-function.spec.ts +++ b/libs/vault/src/models/filter-function.spec.ts @@ -1,5 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore +// eslint-disable-next-line no-restricted-imports import { Unassigned } from "@bitwarden/admin-console/common"; import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/filter-function.ts b/libs/vault/src/models/filter-function.ts similarity index 98% rename from apps/web/src/app/vault/individual-vault/vault-filter/shared/models/filter-function.ts rename to libs/vault/src/models/filter-function.ts index c15dd51a969..55b5a446d4b 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/filter-function.ts +++ b/libs/vault/src/models/filter-function.ts @@ -1,3 +1,4 @@ +// eslint-disable-next-line no-restricted-imports import { Unassigned } from "@bitwarden/admin-console/common"; import { CipherType } from "@bitwarden/common/vault/enums"; import { diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts b/libs/vault/src/models/routed-vault-filter-bridge.model.ts similarity index 97% rename from apps/web/src/app/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts rename to libs/vault/src/models/routed-vault-filter-bridge.model.ts index f9a80791030..d2df82e0053 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts +++ b/libs/vault/src/models/routed-vault-filter-bridge.model.ts @@ -1,9 +1,10 @@ +// eslint-disable-next-line no-restricted-imports import { Unassigned } from "@bitwarden/admin-console/common"; import { CollectionId } from "@bitwarden/common/types/guid"; import { CipherType } from "@bitwarden/common/vault/enums"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; -import { RoutedVaultFilterBridgeService } from "../../services/routed-vault-filter-bridge.service"; +import { RoutedVaultFilterBridgeService } from "../services/routed-vault-filter-bridge.service"; import { All, diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model.ts b/libs/vault/src/models/routed-vault-filter.model.ts similarity index 94% rename from apps/web/src/app/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model.ts rename to libs/vault/src/models/routed-vault-filter.model.ts index 13f1ed7b1a3..9c6801dd449 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model.ts +++ b/libs/vault/src/models/routed-vault-filter.model.ts @@ -1,3 +1,4 @@ +// eslint-disable-next-line no-restricted-imports import { Unassigned } from "@bitwarden/admin-console/common"; import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid"; diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/vault-filter-section.type.ts b/libs/vault/src/models/vault-filter-section.type.ts similarity index 100% rename from apps/web/src/app/vault/individual-vault/vault-filter/shared/models/vault-filter-section.type.ts rename to libs/vault/src/models/vault-filter-section.type.ts diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/vault-filter.model.spec.ts b/libs/vault/src/models/vault-filter.model.spec.ts similarity index 99% rename from apps/web/src/app/vault/individual-vault/vault-filter/shared/models/vault-filter.model.spec.ts rename to libs/vault/src/models/vault-filter.model.spec.ts index 51fe837468c..35e5b668929 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/vault-filter.model.spec.ts +++ b/libs/vault/src/models/vault-filter.model.spec.ts @@ -1,5 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore +// eslint-disable-next-line no-restricted-imports import { CollectionView } from "@bitwarden/admin-console/common"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { CollectionId } from "@bitwarden/common/types/guid"; diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/vault-filter.model.ts b/libs/vault/src/models/vault-filter.model.ts similarity index 100% rename from apps/web/src/app/vault/individual-vault/vault-filter/shared/models/vault-filter.model.ts rename to libs/vault/src/models/vault-filter.model.ts diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/vault-filter.type.ts b/libs/vault/src/models/vault-filter.type.ts similarity index 94% rename from apps/web/src/app/vault/individual-vault/vault-filter/shared/models/vault-filter.type.ts rename to libs/vault/src/models/vault-filter.type.ts index 7a92db6a381..764e7ea0ff9 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/vault-filter.type.ts +++ b/libs/vault/src/models/vault-filter.type.ts @@ -1,3 +1,4 @@ +// eslint-disable-next-line no-restricted-imports import { CollectionAdminView } from "@bitwarden/admin-console/common"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { CipherType } from "@bitwarden/common/vault/enums"; diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts b/libs/vault/src/services/routed-vault-filter-bridge.service.ts similarity index 93% rename from apps/web/src/app/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts rename to libs/vault/src/services/routed-vault-filter-bridge.service.ts index 936dfb0e675..ac7b8921fed 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service.ts +++ b/libs/vault/src/services/routed-vault-filter-bridge.service.ts @@ -4,22 +4,22 @@ import { Injectable } from "@angular/core"; import { Router } from "@angular/router"; import { combineLatest, map, Observable } from "rxjs"; +// eslint-disable-next-line no-restricted-imports import { Unassigned } from "@bitwarden/admin-console/common"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; import { ServiceUtils } from "@bitwarden/common/vault/service-utils"; - -import { RoutedVaultFilterBridge } from "../shared/models/routed-vault-filter-bridge.model"; -import { RoutedVaultFilterModel, All } from "../shared/models/routed-vault-filter.model"; -import { VaultFilter } from "../shared/models/vault-filter.model"; import { + VaultFilterServiceAbstraction as VaultFilterService, + RoutedVaultFilterService, + RoutedVaultFilterBridge, + RoutedVaultFilterModel, + All, + VaultFilter, CipherTypeFilter, CollectionFilter, FolderFilter, OrganizationFilter, -} from "../shared/models/vault-filter.type"; - -import { VaultFilterService } from "./abstractions/vault-filter.service"; -import { RoutedVaultFilterService } from "./routed-vault-filter.service"; +} from "@bitwarden/vault"; /** * This file is part of a layer that is used to temporary bridge between URL filtering and the old state-in-code method. diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts b/libs/vault/src/services/routed-vault-filter.service.ts similarity index 98% rename from apps/web/src/app/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts rename to libs/vault/src/services/routed-vault-filter.service.ts index a5a99428b2d..8d744611812 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts +++ b/libs/vault/src/services/routed-vault-filter.service.ts @@ -7,7 +7,7 @@ import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid"; import { isRoutedVaultFilterItemType, RoutedVaultFilterModel, -} from "../shared/models/routed-vault-filter.model"; +} from "../models/routed-vault-filter.model"; /** * This service is an abstraction layer on top of ActivatedRoute that diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/services/vault-filter.service.spec.ts b/libs/vault/src/services/vault-filter.service.spec.ts similarity index 99% rename from apps/web/src/app/vault/individual-vault/vault-filter/services/vault-filter.service.spec.ts rename to libs/vault/src/services/vault-filter.service.spec.ts index c05459250c0..da66ad33a81 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/services/vault-filter.service.spec.ts +++ b/libs/vault/src/services/vault-filter.service.spec.ts @@ -7,6 +7,7 @@ import { FakeStateProvider } from "@bitwarden/common/../spec/fake-state-provider import { mock, MockProxy } from "jest-mock-extended"; import { firstValueFrom, of, ReplaySubject } from "rxjs"; +// eslint-disable-next-line no-restricted-imports import { CollectionService, CollectionType, diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/services/vault-filter.service.ts b/libs/vault/src/services/vault-filter.service.ts similarity index 98% rename from apps/web/src/app/vault/individual-vault/vault-filter/services/vault-filter.service.ts rename to libs/vault/src/services/vault-filter.service.ts index 1f27773c467..a345daa4438 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/services/vault-filter.service.ts +++ b/libs/vault/src/services/vault-filter.service.ts @@ -12,6 +12,7 @@ import { switchMap, } from "rxjs"; +// eslint-disable-next-line no-restricted-imports import { CollectionService, CollectionTypes, @@ -22,6 +23,7 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { cloneCollection } from "@bitwarden/common/admin-console/utils/collection-utils"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; @@ -38,16 +40,13 @@ import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; import { ServiceUtils } from "@bitwarden/common/vault/service-utils"; import { COLLAPSED_GROUPINGS } from "@bitwarden/common/vault/services/key-state/collapsed-groupings.state"; import { CipherListView } from "@bitwarden/sdk-internal"; -import { cloneCollection } from "@bitwarden/web-vault/app/admin-console/organizations/collections"; - import { + VaultFilterServiceAbstraction, CipherTypeFilter, CollectionFilter, FolderFilter, OrganizationFilter, -} from "../shared/models/vault-filter.type"; - -import { VaultFilterService as VaultFilterServiceAbstraction } from "./abstractions/vault-filter.service"; +} from "@bitwarden/vault"; const NestingDelimiter = "/"; From 1c4deaab9433008f0488cc0f2f29742e73476253 Mon Sep 17 00:00:00 2001 From: Leslie Xiong Date: Wed, 10 Dec 2025 19:45:36 -0500 Subject: [PATCH 17/56] migrated vault filters using router params --- apps/desktop/src/app/app-routing.module.ts | 20 ++ .../app/layout/desktop-layout.component.html | 2 +- .../app/layout/desktop-layout.component.ts | 4 +- .../src/services/vault-state.service.ts | 108 ---------- .../app/vault-v3/nav/vault-nav.component.html | 9 - .../app/vault-v3/nav/vault-nav.component.ts | 15 -- .../filters/collection-filter.component.html | 107 +++------- .../filters/collection-filter.component.ts | 36 +++- .../filters/folder-filter.component.html | 117 +++-------- .../filters/folder-filter.component.ts | 35 +++- .../organization-filter.component.html | 178 ++++------------- .../filters/organization-filter.component.ts | 103 +++++++--- .../filters/status-filter.component.html | 93 +++------ .../filters/status-filter.component.spec.ts | 98 --------- .../filters/status-filter.component.ts | 55 ++++-- .../filters/type-filter.component.html | 49 +---- .../filters/type-filter.component.ts | 38 ++-- .../vault-filter/shared-filter-imports.ts | 9 + .../vault-filter/vault-filter.component.html | 81 +++----- .../vault-filter/vault-filter.component.ts | 105 +++++++++- .../vault-filter/vault-filter.module.ts | 34 ---- .../src/vault/app/vault-v3/vault.component.ts | 187 ++++++++---------- 22 files changed, 573 insertions(+), 910 deletions(-) delete mode 100644 apps/desktop/src/services/vault-state.service.ts delete mode 100644 apps/desktop/src/vault/app/vault-v3/nav/vault-nav.component.html delete mode 100644 apps/desktop/src/vault/app/vault-v3/nav/vault-nav.component.ts delete mode 100644 apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.spec.ts create mode 100644 apps/desktop/src/vault/app/vault-v3/vault-filter/shared-filter-imports.ts delete mode 100644 apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.module.ts diff --git a/apps/desktop/src/app/app-routing.module.ts b/apps/desktop/src/app/app-routing.module.ts index 8fab7df1cd8..f822a9ae4d9 100644 --- a/apps/desktop/src/app/app-routing.module.ts +++ b/apps/desktop/src/app/app-routing.module.ts @@ -41,11 +41,19 @@ import { NewDeviceVerificationComponent, } from "@bitwarden/auth/angular"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; import { AnonLayoutWrapperComponent, AnonLayoutWrapperData } from "@bitwarden/components"; import { LockComponent, ConfirmKeyConnectorDomainComponent } from "@bitwarden/key-management-ui"; +import { + VaultFilterServiceAbstraction, + VaultFilterService, + RoutedVaultFilterBridgeService, + RoutedVaultFilterService, +} from "@bitwarden/vault"; import { maxAccountsGuardFn } from "../auth/guards/max-accounts.guard"; import { RemovePasswordComponent } from "../key-management/key-connector/remove-password.component"; +import { DesktopPremiumUpgradePromptService } from "../services/desktop-premium-upgrade-prompt.service"; import { VaultV2Component } from "../vault/app/vault/vault-v2.component"; import { VaultComponent } from "../vault/app/vault-v3/vault.component"; @@ -337,6 +345,18 @@ const routes: Routes = [ path: "", component: DesktopLayoutComponent, canActivate: [authGuard], + providers: [ + RoutedVaultFilterService, + RoutedVaultFilterBridgeService, + { + provide: VaultFilterServiceAbstraction, + useClass: VaultFilterService, + }, + { + provide: PremiumUpgradePromptService, + useClass: DesktopPremiumUpgradePromptService, + }, + ], children: [ { path: "new-vault", diff --git a/apps/desktop/src/app/layout/desktop-layout.component.html b/apps/desktop/src/app/layout/desktop-layout.component.html index c7dca09427a..dd4c77f1021 100644 --- a/apps/desktop/src/app/layout/desktop-layout.component.html +++ b/apps/desktop/src/app/layout/desktop-layout.component.html @@ -2,7 +2,7 @@ - + diff --git a/apps/desktop/src/app/layout/desktop-layout.component.ts b/apps/desktop/src/app/layout/desktop-layout.component.ts index b3f708cbbe8..798b8a629a3 100644 --- a/apps/desktop/src/app/layout/desktop-layout.component.ts +++ b/apps/desktop/src/app/layout/desktop-layout.component.ts @@ -5,7 +5,7 @@ import { PasswordManagerLogo } from "@bitwarden/assets/svg"; import { LayoutComponent, NavigationModule } from "@bitwarden/components"; import { I18nPipe } from "@bitwarden/ui-common"; -import { VaultNavComponent } from "../../vault/app/vault-v3/nav/vault-nav.component"; +import { VaultFilterComponent } from "../../vault/app/vault-v3/vault-filter/vault-filter.component"; import { DesktopSideNavComponent } from "./desktop-side-nav.component"; @@ -19,7 +19,7 @@ import { DesktopSideNavComponent } from "./desktop-side-nav.component"; LayoutComponent, NavigationModule, DesktopSideNavComponent, - VaultNavComponent, + VaultFilterComponent, ], templateUrl: "./desktop-layout.component.html", }) diff --git a/apps/desktop/src/services/vault-state.service.ts b/apps/desktop/src/services/vault-state.service.ts deleted file mode 100644 index e262ba2dc0e..00000000000 --- a/apps/desktop/src/services/vault-state.service.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { Injectable } from "@angular/core"; -import { Subject } from "rxjs"; - -import { VaultFilter } from "@bitwarden/angular/vault/vault-filter/models/vault-filter.model"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; - -import { SearchBarService } from "../app/layout/search/search-bar.service"; - -/** - * Service to coordinate vault state, including filter state and folder actions, - * between the navigation component and the vault component. - */ -@Injectable({ providedIn: "root" }) -export class VaultStateService { - private filterChangeSubject = new Subject(); - private addFolderSubject = new Subject(); - private editFolderSubject = new Subject(); - - /** - * The currently active vault filter. - */ - activeFilter: VaultFilter = new VaultFilter(); - - /** - * Observable stream of vault filter changes. - * Subscribe to this to react to filter changes from the navigation. - */ - readonly filterChange$ = this.filterChangeSubject.asObservable(); - - /** - * Observable stream of add folder requests. - * Subscribe to this to handle folder creation. - */ - readonly addFolder$ = this.addFolderSubject.asObservable(); - - /** - * Observable stream of edit folder requests. - * Subscribe to this to handle folder editing. - * Emits the folder ID to edit. - */ - readonly editFolder$ = this.editFolderSubject.asObservable(); - - constructor( - private i18nService: I18nService, - private searchBarService: SearchBarService, - ) {} - - /** - * Apply a new vault filter. - * This updates the search bar placeholder and notifies all subscribers. - */ - applyFilter(filter: VaultFilter): void { - // Store the active filter - this.activeFilter = filter; - - // Update search bar placeholder text based on the filter - this.searchBarService.setPlaceholderText( - this.i18nService.t(this.calculateSearchBarLocalizationString(filter)), - ); - - // Emit the filter change to subscribers - this.filterChangeSubject.next(filter); - } - - /** - * Request to add a new folder. - * This will notify subscribers to show the folder creation dialog. - */ - requestAddFolder(): void { - this.addFolderSubject.next(); - } - - /** - * Request to edit an existing folder. - * This will notify subscribers to show the folder edit dialog. - */ - requestEditFolder(folderId: string): void { - this.editFolderSubject.next(folderId); - } - - /** - * Calculate the appropriate search bar localization string based on the active filter. - */ - private calculateSearchBarLocalizationString(vaultFilter: VaultFilter): string { - if (vaultFilter.status === "favorites") { - return "searchFavorites"; - } - if (vaultFilter.status === "trash") { - return "searchTrash"; - } - if (vaultFilter.cipherType != null) { - return "searchType"; - } - if (vaultFilter.selectedFolderId != null && vaultFilter.selectedFolderId !== "none") { - return "searchFolder"; - } - if (vaultFilter.selectedCollectionId != null) { - return "searchCollection"; - } - if (vaultFilter.selectedOrganizationId != null) { - return "searchOrganization"; - } - if (vaultFilter.myVaultOnly) { - return "searchMyVault"; - } - return "searchVault"; - } -} diff --git a/apps/desktop/src/vault/app/vault-v3/nav/vault-nav.component.html b/apps/desktop/src/vault/app/vault-v3/nav/vault-nav.component.html deleted file mode 100644 index 3c8e5eb16da..00000000000 --- a/apps/desktop/src/vault/app/vault-v3/nav/vault-nav.component.html +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/apps/desktop/src/vault/app/vault-v3/nav/vault-nav.component.ts b/apps/desktop/src/vault/app/vault-v3/nav/vault-nav.component.ts deleted file mode 100644 index d30be43ecb8..00000000000 --- a/apps/desktop/src/vault/app/vault-v3/nav/vault-nav.component.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { ChangeDetectionStrategy, Component } from "@angular/core"; -import { NavigationModule } from "@bitwarden/components"; -import { I18nPipe } from "@bitwarden/ui-common"; -import { VaultStateService } from "../../../../services/vault-state.service"; -import { VaultFilterModule } from "../vault-filter/vault-filter.module"; - -@Component({ - selector: "app-vault-nav", - imports: [I18nPipe, NavigationModule, VaultFilterModule], - templateUrl: "./vault-nav.component.html", - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class VaultNavComponent { - constructor(protected vaultStateService: VaultStateService) {} -} diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.html index f83a2e1c91f..735188f479c 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.html +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.html @@ -1,84 +1,23 @@ - -
-

- -

-
-
    - -
  • - - - - -
      - - -
    -
  • -
    - - -
-
+@if (collection().children.length) { + + @for (childCollection of collection().children; track childCollection.node.id) { + + } + +} @else { + +} diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.ts index 015b301efdb..6a98b8c2f09 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.ts @@ -1,12 +1,40 @@ -import { Component } from "@angular/core"; +import { Component, input, computed } from "@angular/core"; -import { CollectionFilterComponent as BaseCollectionFilterComponent } from "@bitwarden/angular/vault/vault-filter/components/collection-filter.component"; +import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; +import { VaultFilter, CollectionFilter } from "@bitwarden/vault"; + +import { VAULT_FILTER_IMPORTS } from "../shared-filter-imports"; // 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-collection-filter", templateUrl: "collection-filter.component.html", - standalone: false, + standalone: true, + imports: [...VAULT_FILTER_IMPORTS], }) -export class CollectionFilterComponent extends BaseCollectionFilterComponent {} +export class CollectionFilterComponent { + protected readonly collection = input>(); + protected readonly activeFilter = input(); + + protected readonly displayName = computed(() => { + return this.collection().node.name; + }); + + protected readonly isActive = computed(() => { + return ( + this.collection().node.id === this.activeFilter()?.collectionId && + !!this.activeFilter()?.selectedCollectionNode + ); + }); + + protected applyFilter(event: Event) { + event.stopPropagation(); + + const filter = this.activeFilter(); + + if (filter) { + filter.selectedCollectionNode = this.collection(); + } + } +} diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.html index a2240b03ff5..b201913d258 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.html +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.html @@ -1,94 +1,23 @@ - -
-

- -

- -
-
    - -
  • - - - - - -
      - - -
    -
  • -
    - -
-
+@if (folder().children.length) { + + @for (childFolder of folder().children; track childFolder.node.id) { + + } + +} @else { + +} diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.ts index f340e4082b8..477325c69a3 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.ts @@ -1,12 +1,39 @@ -import { Component } from "@angular/core"; +import { Component, input, computed } from "@angular/core"; -import { FolderFilterComponent as BaseFolderFilterComponent } from "@bitwarden/angular/vault/vault-filter/components/folder-filter.component"; +import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; +import { VaultFilter, FolderFilter } from "@bitwarden/vault"; + +import { VAULT_FILTER_IMPORTS } from "../shared-filter-imports"; // 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-folder-filter", templateUrl: "folder-filter.component.html", - standalone: false, + standalone: true, + imports: [...VAULT_FILTER_IMPORTS], }) -export class FolderFilterComponent extends BaseFolderFilterComponent {} +export class FolderFilterComponent { + protected readonly folder = input>(); + protected readonly activeFilter = input(); + + protected readonly displayName = computed(() => { + return this.folder().node.name; + }); + + protected readonly isActive = computed(() => { + return ( + this.folder().node.id === this.activeFilter()?.folderId && + !!this.activeFilter()?.selectedFolderNode + ); + }); + + protected applyFilter(event: Event) { + event.stopPropagation(); + const filter = this.activeFilter(); + + if (filter) { + filter.selectedFolderNode = this.folder(); + } + } +} diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.html index 8c73891dc09..60fa048406a 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.html +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.html @@ -1,140 +1,38 @@ - - - -
- -   -

- -

-
-
    -
  • - - - - - - -
  • -
-
- -
- -   -

- -

-
-
    -
  • - - - -
  • -
  • - - - - - - -
  • -
-
-
-
-
+@if (show()) { + @for (organization of organizations().children ?? []; track organization.node.id) { + @if (getOrgCollections(organization.node.id)?.children?.length > 0) { + + @if (!hideCollections() && collections() != null) { + @for (c of getOrgCollections(organization.node.id)?.children ?? []; track c.node.id) { + + } + } + + } @else { + + } + @if (!organization.node.enabled) { + + + + } + } +} diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.ts index 99338ddbb7c..2c92c35a2b5 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.ts @@ -1,53 +1,102 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { Component } from "@angular/core"; +import { Component, computed, input, inject } from "@angular/core"; -import { OrganizationFilterComponent as BaseOrganizationFilterComponent } from "@bitwarden/angular/vault/vault-filter/components/organization-filter.component"; import { DisplayMode } from "@bitwarden/angular/vault/vault-filter/models/display-mode"; -import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { OrganizationId } from "@bitwarden/common/types/guid"; +import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; import { ToastService } from "@bitwarden/components"; +import { OrganizationFilter, VaultFilter, CollectionFilter } from "@bitwarden/vault"; + +import { VAULT_FILTER_IMPORTS } from "../shared-filter-imports"; + +import { CollectionFilterComponent } from "./collection-filter.component"; // 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", - standalone: false, + standalone: true, + imports: [...VAULT_FILTER_IMPORTS, CollectionFilterComponent], }) -export class OrganizationFilterComponent extends BaseOrganizationFilterComponent { - get show() { +export class OrganizationFilterComponent { + private toastService: ToastService = inject(ToastService); + private i18nService: I18nService = inject(I18nService); + + protected readonly hide = input(false); + protected readonly organizations = input>(); + protected readonly activeFilter = input(); + protected readonly activeOrganizationDataOwnership = input(false); + protected readonly activeSingleOrganizationPolicy = input(false); + protected readonly hideCollections = input(false); + protected readonly collections = input>(); + + protected readonly show = computed(() => { const hiddenDisplayModes: DisplayMode[] = [ "singleOrganizationAndOrganizatonDataOwnershipPolicies", ]; return ( - !this.hide && - this.organizations.length > 0 && - hiddenDisplayModes.indexOf(this.displayMode) === -1 + !this.hide() && + this.organizations()?.children.length > 0 && + hiddenDisplayModes.indexOf(this.displayMode()) === -1 ); - } + }); - constructor( - private i18nService: I18nService, - private platformUtilsService: PlatformUtilsService, - private toastService: ToastService, - ) { - super(); - } + protected readonly displayMode = computed(() => { + let displayMode: DisplayMode = "organizationMember"; + if (this.organizations() == null || this.organizations().children.length < 1) { + displayMode = "noOrganizations"; + } else if (this.activeOrganizationDataOwnership() && !this.activeSingleOrganizationPolicy()) { + displayMode = "organizationDataOwnershipPolicy"; + } else if (!this.activeOrganizationDataOwnership() && this.activeSingleOrganizationPolicy()) { + displayMode = "singleOrganizationPolicy"; + } else if (this.activeOrganizationDataOwnership() && this.activeSingleOrganizationPolicy()) { + displayMode = "singleOrganizationAndOrganizatonDataOwnershipPolicies"; + } - async applyOrganizationFilter(organization: Organization) { - if (organization.enabled) { - //proceed with default behaviour for enabled organizations - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - super.applyOrganizationFilter(organization); - } else { + return displayMode; + }); + + protected applyFilter(organization: TreeNode) { + if (!organization.node.enabled) { this.toastService.showToast({ variant: "error", title: null, message: this.i18nService.t("disabledOrganizationFilterError"), }); + return; + } + + const filter = this.activeFilter(); + + if (filter) { + filter.selectedOrganizationNode = organization; } } + + private readonly collectionsByOrganization = computed(() => { + const collections = this.collections(); + const map = new Map>(); + 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(collections.node, null); + headNode.children = filteredCollections; + map.set(org.node.id, headNode); + } + + return map; + }); + + protected getOrgCollections(organizationId: OrganizationId): TreeNode { + return this.collectionsByOrganization().get(organizationId) ?? null; + } } diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.html index 8b064778444..2f747c7a821 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.html +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.html @@ -1,68 +1,25 @@ - -

{{ "filters" | i18n }}

-
    -
  • - - - -
  • -
  • - - - -
  • -
  • - - - - @if (!(canArchive$ | async)) { - - } -
  • -
  • - - - -
  • -
-
+@if (show()) { + @if (!hideArchive()) { + + @if (!(canArchive$ | async)) { + + } + } + @if (!hideTrash()) { + + } +} diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.spec.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.spec.ts deleted file mode 100644 index ba785310a0a..00000000000 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.spec.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { ComponentFixture, TestBed } from "@angular/core/testing"; -import { mock } from "jest-mock-extended"; -import { of } from "rxjs"; - -import { PremiumBadgeComponent } from "@bitwarden/angular/billing/components/premium-badge"; -import { JslibModule } from "@bitwarden/angular/jslib.module"; -import { VaultFilter } from "@bitwarden/angular/vault/vault-filter/models/vault-filter.model"; -import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec"; -import { UserId } from "@bitwarden/common/types/guid"; -import { CipherArchiveService } from "@bitwarden/common/vault/abstractions/cipher-archive.service"; -import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; -import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; - -import { StatusFilterComponent } from "./status-filter.component"; - -describe("StatusFilterComponent", () => { - let component: StatusFilterComponent; - let fixture: ComponentFixture; - let cipherArchiveService: jest.Mocked; - let accountService: FakeAccountService; - - const mockUserId = Utils.newGuid() as UserId; - const event = new Event("click"); - - beforeEach(async () => { - accountService = mockAccountServiceWith(mockUserId); - cipherArchiveService = mock(); - - await TestBed.configureTestingModule({ - declarations: [StatusFilterComponent], - providers: [ - { provide: AccountService, useValue: accountService }, - { provide: CipherArchiveService, useValue: cipherArchiveService }, - { provide: PremiumUpgradePromptService, useValue: mock() }, - { - provide: BillingAccountProfileStateService, - useValue: mock(), - }, - { provide: I18nService, useValue: { t: (key: string) => key } }, - ], - imports: [JslibModule, PremiumBadgeComponent], - }).compileComponents(); - - fixture = TestBed.createComponent(StatusFilterComponent); - component = fixture.componentInstance; - component.activeFilter = new VaultFilter(); - fixture.detectChanges(); - }); - - describe("handleArchiveFilter", () => { - const applyFilter = jest.fn(); - let promptForPremiumSpy: jest.SpyInstance; - - beforeEach(() => { - applyFilter.mockClear(); - component["applyFilter"] = applyFilter; - - promptForPremiumSpy = jest.spyOn(component["premiumBadgeComponent"]()!, "promptForPremium"); - }); - - it("should apply archive filter when userCanArchive returns true", async () => { - cipherArchiveService.userCanArchive$.mockReturnValue(of(true)); - cipherArchiveService.archivedCiphers$.mockReturnValue(of([])); - - await component["handleArchiveFilter"](event); - - expect(applyFilter).toHaveBeenCalledWith("archive"); - expect(promptForPremiumSpy).not.toHaveBeenCalled(); - }); - - it("should apply archive filter when userCanArchive returns false but hasArchivedCiphers is true", async () => { - const mockCipher = new CipherView(); - mockCipher.id = "test-id"; - - cipherArchiveService.userCanArchive$.mockReturnValue(of(false)); - cipherArchiveService.archivedCiphers$.mockReturnValue(of([mockCipher])); - - await component["handleArchiveFilter"](event); - - expect(applyFilter).toHaveBeenCalledWith("archive"); - expect(promptForPremiumSpy).not.toHaveBeenCalled(); - }); - - it("should prompt for premium when userCanArchive returns false and hasArchivedCiphers is false", async () => { - cipherArchiveService.userCanArchive$.mockReturnValue(of(false)); - cipherArchiveService.archivedCiphers$.mockReturnValue(of([])); - - await component["handleArchiveFilter"](event); - - expect(applyFilter).not.toHaveBeenCalled(); - expect(promptForPremiumSpy).toHaveBeenCalled(); - }); - }); -}); diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts index 95ffd3f0212..22f52a69dc5 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts @@ -1,20 +1,60 @@ -import { Component, viewChild } from "@angular/core"; +import { Component, viewChild, input, inject, computed } from "@angular/core"; import { combineLatest, firstValueFrom, map, switchMap } from "rxjs"; import { PremiumBadgeComponent } from "@bitwarden/angular/billing/components/premium-badge"; -import { StatusFilterComponent as BaseStatusFilterComponent } from "@bitwarden/angular/vault/vault-filter/components/status-filter.component"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { CipherArchiveService } from "@bitwarden/common/vault/abstractions/cipher-archive.service"; +import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; +import { VaultFilter, CipherStatus, CipherTypeFilter } from "@bitwarden/vault"; + +import { VAULT_FILTER_IMPORTS } from "../shared-filter-imports"; // 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-status-filter", templateUrl: "status-filter.component.html", - standalone: false, + standalone: true, + imports: [...VAULT_FILTER_IMPORTS, PremiumBadgeComponent], }) -export class StatusFilterComponent extends BaseStatusFilterComponent { +export class StatusFilterComponent { + private accountService: AccountService = inject(AccountService); + private cipherArchiveService: CipherArchiveService = inject(CipherArchiveService); + + protected readonly hideTrash = input(false); + protected readonly hideArchive = input(false); + protected readonly activeFilter = input(); + protected readonly archiveFilter: CipherTypeFilter = { + id: "archive", + name: "archiveNoun", + type: "archive", + icon: "bwi-archive", + }; + protected readonly trashFilter: CipherTypeFilter = { + id: "trash", + name: "trash", + type: "trash", + icon: "bwi-trash", + }; + + protected readonly show = computed(() => { + return !(this.hideTrash() && this.hideArchive()); + }); + + protected applyFilter(filterType: CipherStatus) { + let filter: CipherTypeFilter = null; + if (filterType === "archive") { + filter = this.archiveFilter; + } else if (filterType === "trash") { + filter = this.trashFilter; + } + + if (filter) { + this.activeFilter().selectedCipherTypeNode = new TreeNode(filter, null); + } + } + private readonly premiumBadgeComponent = viewChild(PremiumBadgeComponent); private userId$ = this.accountService.activeAccount$.pipe(getUserId); @@ -28,13 +68,6 @@ export class StatusFilterComponent extends BaseStatusFilterComponent { ), ); - constructor( - private accountService: AccountService, - private cipherArchiveService: CipherArchiveService, - ) { - super(); - } - protected async handleArchiveFilter(event: Event) { const [canArchive, hasArchivedCiphers] = await firstValueFrom( combineLatest([this.canArchive$, this.hasArchivedCiphers$]), diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.html index f8a83e01266..fdaf74be74f 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.html +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.html @@ -1,39 +1,10 @@ -
-

- -

-
-
    - @for (typeFilter of typeFilters$ | async; track typeFilter) { -
  • - - - -
  • - } -
+@for (typeFilter of typeFilters$ | async; track typeFilter) { + +} diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.ts index fbab7ce4667..56c272ccfd7 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.ts @@ -1,34 +1,48 @@ -import { Component } from "@angular/core"; +import { Component, input, inject } from "@angular/core"; import { map, shareReplay } from "rxjs"; -import { TypeFilterComponent as BaseTypeFilterComponent } from "@bitwarden/angular/vault/vault-filter/components/type-filter.component"; +import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service"; -import { CIPHER_MENU_ITEMS } from "@bitwarden/common/vault/types/cipher-menu-items"; +import { VaultFilter, CipherTypeFilter } from "@bitwarden/vault"; + +import { VAULT_FILTER_IMPORTS } from "../shared-filter-imports"; // 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-type-filter", templateUrl: "type-filter.component.html", - standalone: false, + standalone: true, + imports: [...VAULT_FILTER_IMPORTS], }) -export class TypeFilterComponent extends BaseTypeFilterComponent { +export class TypeFilterComponent { + private restrictedItemTypesService: RestrictedItemTypesService = inject( + RestrictedItemTypesService, + ); + + protected readonly cipherTypes = input>(); + protected readonly activeFilter = input(); + + protected applyFilter(cipherType: TreeNode) { + const filter = this.activeFilter(); + + if (filter) { + filter.selectedCipherTypeNode = cipherType; + } + } + protected typeFilters$ = this.restrictedItemTypesService.restricted$.pipe( map((restrictedItemTypes) => // Filter out restricted item types from the typeFilters array - CIPHER_MENU_ITEMS.filter( - (typeFilter) => + this.cipherTypes().children.filter( + (type) => !restrictedItemTypes.some( (restrictedType) => restrictedType.allowViewOrgIds.length === 0 && - restrictedType.cipherType === typeFilter.type, + restrictedType.cipherType === type.node.type, ), ), ), shareReplay({ bufferSize: 1, refCount: true }), ); - - constructor(private restrictedItemTypesService: RestrictedItemTypesService) { - super(); - } } diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/shared-filter-imports.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/shared-filter-imports.ts new file mode 100644 index 00000000000..d2cd3d9cf66 --- /dev/null +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/shared-filter-imports.ts @@ -0,0 +1,9 @@ +import { CommonModule } from "@angular/common"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { NavigationModule } from "@bitwarden/components"; + +/** + * Common imports shared across all vault filter components. + */ +export const VAULT_FILTER_IMPORTS = [CommonModule, JslibModule, NavigationModule] as const; diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.html index 14e72f3bb9d..521deabae3b 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.html +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.html @@ -1,51 +1,30 @@ -
- -
- - - - - - - +@if (!isLoaded) { +
+ +
+} @else { + + + + + @if (!hideFolders()) { + + @for (folder of (folders$ | async)?.children ?? []; track folder.node.id) { + + } + + } + +} diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts index d7c5bafc3a4..709e9afd190 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts @@ -1,12 +1,109 @@ -import { Component } from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { Component, inject, OnInit, input, output } from "@angular/core"; +import { firstValueFrom, Observable, Subject, takeUntil } from "rxjs"; -import { VaultFilterComponent as BaseVaultFilterComponent } from "@bitwarden/angular/vault/vault-filter/components/vault-filter.component"; +import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { PolicyType } from "@bitwarden/common/admin-console/enums"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { getUserId } from "@bitwarden/common/auth/services/account.service"; +import { UserId } from "@bitwarden/common/types/guid"; +import { CipherArchiveService } from "@bitwarden/common/vault/abstractions/cipher-archive.service"; +import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; +import { NavigationModule } from "@bitwarden/components"; +import { I18nPipe } from "@bitwarden/ui-common"; +import { + OrganizationFilter, + CipherTypeFilter, + CollectionFilter, + FolderFilter, + VaultFilter, + VaultFilterServiceAbstraction as VaultFilterService, + RoutedVaultFilterBridgeService, +} from "@bitwarden/vault"; + +import { FolderFilterComponent } from "./filters/folder-filter.component"; +import { OrganizationFilterComponent } from "./filters/organization-filter.component"; +import { StatusFilterComponent } from "./filters/status-filter.component"; +import { TypeFilterComponent } from "./filters/type-filter.component"; // 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-vault-filter", templateUrl: "vault-filter.component.html", - standalone: false, + standalone: true, + imports: [ + I18nPipe, + NavigationModule, + CommonModule, + OrganizationFilterComponent, + StatusFilterComponent, + TypeFilterComponent, + FolderFilterComponent, + ], }) -export class VaultFilterComponent extends BaseVaultFilterComponent {} +export class VaultFilterComponent implements OnInit { + private routedVaultFilterBridgeService = inject(RoutedVaultFilterBridgeService); + private vaultFilterService: VaultFilterService = inject(VaultFilterService); + private accountService: AccountService = inject(AccountService); + private cipherArchiveService: CipherArchiveService = inject(CipherArchiveService); + private policyService: PolicyService = inject(PolicyService); + private componentIsDestroyed$ = new Subject(); + + protected activeFilter: VaultFilter; + protected readonly hideFolders = input(false); + protected readonly hideCollections = input(false); + protected readonly hideFavorites = input(false); + protected readonly hideTrash = input(false); + protected readonly hideOrganizations = input(false); + protected onFilterChange = output(); + + private activeUserId: UserId; + protected isLoaded = false; + protected showArchiveVaultFilter = false; + protected activeOrganizationDataOwnershipPolicy: boolean; + protected activeSingleOrganizationPolicy: boolean; + protected organizations$: Observable>; + protected collections$: Observable>; + protected folders$: Observable>; + protected cipherTypes$: Observable>; + + private async setActivePolicies() { + this.activeOrganizationDataOwnershipPolicy = await firstValueFrom( + this.policyService.policyAppliesToUser$( + PolicyType.OrganizationDataOwnership, + this.activeUserId, + ), + ); + this.activeSingleOrganizationPolicy = await firstValueFrom( + this.policyService.policyAppliesToUser$(PolicyType.SingleOrg, this.activeUserId), + ); + } + + async ngOnInit(): Promise { + this.activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); + this.organizations$ = this.vaultFilterService.organizationTree$; + if ( + this.organizations$ != null && + (await firstValueFrom(this.organizations$)).children.length > 0 + ) { + await this.setActivePolicies(); + } + this.cipherTypes$ = this.vaultFilterService.cipherTypeTree$; + this.folders$ = this.vaultFilterService.folderTree$; + this.collections$ = this.vaultFilterService.collectionTree$; + + this.showArchiveVaultFilter = await firstValueFrom( + this.cipherArchiveService.hasArchiveFlagEnabled$(), + ); + + // Subscribe to the active filter from the bridge service + this.routedVaultFilterBridgeService.activeFilter$ + .pipe(takeUntil(this.componentIsDestroyed$)) + .subscribe((filter) => { + this.activeFilter = filter; + }); + + this.isLoaded = true; + } +} diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.module.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.module.ts deleted file mode 100644 index 54a6d33ca6a..00000000000 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.module.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { CommonModule } from "@angular/common"; -import { NgModule } from "@angular/core"; - -import { PremiumBadgeComponent } from "@bitwarden/angular/billing/components/premium-badge"; -import { JslibModule } from "@bitwarden/angular/jslib.module"; -import { DeprecatedVaultFilterService as DeprecatedVaultFilterServiceAbstraction } from "@bitwarden/angular/vault/abstractions/deprecated-vault-filter.service"; -import { VaultFilterService } from "@bitwarden/angular/vault/vault-filter/services/vault-filter.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"; -import { TypeFilterComponent } from "./filters/type-filter.component"; -import { VaultFilterComponent } from "./vault-filter.component"; - -@NgModule({ - imports: [CommonModule, JslibModule, PremiumBadgeComponent], - declarations: [ - VaultFilterComponent, - CollectionFilterComponent, - FolderFilterComponent, - OrganizationFilterComponent, - StatusFilterComponent, - TypeFilterComponent, - ], - exports: [VaultFilterComponent], - providers: [ - { - provide: DeprecatedVaultFilterServiceAbstraction, - useClass: VaultFilterService, - }, - ], -}) -export class VaultFilterModule {} diff --git a/apps/desktop/src/vault/app/vault-v3/vault.component.ts b/apps/desktop/src/vault/app/vault-v3/vault.component.ts index 6cb5eb05379..cbc8c5c866d 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault.component.ts @@ -9,15 +9,21 @@ import { ViewContainerRef, } from "@angular/core"; import { ActivatedRoute, Router } from "@angular/router"; -import { firstValueFrom, Subject, takeUntil, switchMap, lastValueFrom, Observable } from "rxjs"; +import { + firstValueFrom, + Subject, + takeUntil, + switchMap, + lastValueFrom, + Observable, + from, +} from "rxjs"; import { filter, map, take } from "rxjs/operators"; import { CollectionService, CollectionView } from "@bitwarden/admin-console/common"; import { PremiumBadgeComponent } from "@bitwarden/angular/billing/components/premium-badge"; import { VaultViewPasswordHistoryService } from "@bitwarden/angular/services/view-password-history.service"; -import { VaultFilter } from "@bitwarden/angular/vault/vault-filter/models/vault-filter.model"; import { AuthRequestServiceAbstraction } from "@bitwarden/auth/common"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; @@ -28,7 +34,6 @@ import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { EventType } from "@bitwarden/common/enums"; import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; @@ -60,8 +65,6 @@ import { } from "@bitwarden/components"; import { I18nPipe } from "@bitwarden/ui-common"; import { - AddEditFolderDialogComponent, - AddEditFolderDialogResult, AttachmentDialogResult, AttachmentsV2Component, ChangeLoginPasswordService, @@ -78,17 +81,17 @@ import { PasswordRepromptService, CipherFormComponent, ArchiveCipherUtilitiesService, + VaultFilter, + RoutedVaultFilterBridgeService, + VaultFilterServiceAbstraction as VaultFilterService, } from "@bitwarden/vault"; import { SearchBarService } from "../../../app/layout/search/search-bar.service"; import { DesktopCredentialGenerationService } from "../../../services/desktop-cipher-form-generator.service"; import { DesktopPremiumUpgradePromptService } from "../../../services/desktop-premium-upgrade-prompt.service"; -import { VaultStateService } from "../../../services/vault-state.service"; import { invokeMenu, RendererMenuItem } from "../../../utils"; import { AssignCollectionsDesktopComponent } from "../vault/assign-collections"; import { ItemFooterComponent } from "../vault/item-footer.component"; -import { VaultFilterComponent } from "../vault/vault-filter/vault-filter.component"; -import { VaultFilterModule } from "../vault/vault-filter/vault-filter.module"; import { VaultItemsV2Component } from "../vault/vault-items-v2.component"; const BroadcasterSubscriptionId = "VaultComponent"; @@ -108,7 +111,6 @@ const BroadcasterSubscriptionId = "VaultComponent"; ItemModule, ButtonModule, PremiumBadgeComponent, - VaultFilterModule, VaultItemsV2Component, ], providers: [ @@ -135,17 +137,11 @@ const BroadcasterSubscriptionId = "VaultComponent"; }, ], }) -export class VaultComponent - implements OnInit, OnDestroy, CopyClickListener -{ +export class VaultComponent implements OnInit, OnDestroy, CopyClickListener { // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChild(VaultItemsV2Component, { static: true }) - vaultItemsComponent: VaultItemsV2Component | null = null; - // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals - // eslint-disable-next-line @angular-eslint/prefer-signals - @ViewChild(VaultFilterComponent, { static: true }) - vaultFilterComponent: VaultFilterComponent | null = null; + vaultItemsComponent: VaultItemsV2Component | null = null; // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChild("folderAddEdit", { read: ViewContainerRef, static: true }) @@ -195,6 +191,7 @@ export class VaultComponent private componentIsDestroyed$ = new Subject(); private allOrganizations: Organization[] = []; private allCollections: CollectionView[] = []; + private filteredCollections: CollectionView[] = []; constructor( private route: ActivatedRoute, @@ -210,7 +207,6 @@ export class VaultComponent private totpService: TotpService, private passwordRepromptService: PasswordRepromptService, private searchBarService: SearchBarService, - private apiService: ApiService, private dialogService: DialogService, private billingAccountProfileStateService: BillingAccountProfileStateService, private toastService: ToastService, @@ -221,12 +217,12 @@ export class VaultComponent private collectionService: CollectionService, private organizationService: OrganizationService, private folderService: FolderService, - private configService: ConfigService, private authRequestService: AuthRequestServiceAbstraction, private cipherArchiveService: CipherArchiveService, private policyService: PolicyService, private archiveCipherUtilitiesService: ArchiveCipherUtilitiesService, - private vaultStateService: VaultStateService, + private routedVaultFilterBridgeService: RoutedVaultFilterBridgeService, + private vaultFilterService: VaultFilterService, ) {} async ngOnInit() { @@ -242,26 +238,10 @@ export class VaultComponent this.userHasPremiumAccess = canAccessPremium; }); - // Subscribe to filter changes from VaultNavComponent - this.vaultStateService.filterChange$ + // Subscribe to filter changes from router params via the bridge service + this.routedVaultFilterBridgeService.activeFilter$ .pipe( - switchMap((vaultFilter: VaultFilter) => this.applyVaultFilter(vaultFilter)), - takeUntil(this.componentIsDestroyed$), - ) - .subscribe(); - - // Subscribe to add folder requests from VaultNavComponent - this.vaultStateService.addFolder$ - .pipe( - switchMap(() => this.addFolder()), - takeUntil(this.componentIsDestroyed$), - ) - .subscribe(); - - // Subscribe to edit folder requests from VaultNavComponent - this.vaultStateService.editFolder$ - .pipe( - switchMap((folderId: string) => this.editFolder(folderId)), + switchMap((vaultFilter: VaultFilter) => from(this.applyVaultFilter(vaultFilter))), takeUntil(this.componentIsDestroyed$), ) .subscribe(); @@ -293,15 +273,12 @@ export class VaultComponent break; case "syncCompleted": if (this.vaultItemsComponent) { - await this.vaultItemsComponent - .reload(this.activeFilter.buildFilter()) - .catch(() => {}); - } - if (this.vaultFilterComponent) { - await this.vaultFilterComponent - .reloadCollectionsAndFolders(this.activeFilter) - .catch(() => {}); - await this.vaultFilterComponent.reloadOrganizations().catch(() => {}); + // const filterFn = this.wrapFilterForCipherListView( + // this.activeFilter.buildFilter(), + // ); + // await this.vaultItemsComponent.reload(filterFn).catch(() => {}); + const filter = this.activeFilter.buildFilter(); + await this.vaultItemsComponent.reload(filter).catch(() => {}); } break; case "modalShown": @@ -403,6 +380,12 @@ export class VaultComponent .subscribe((collections) => { this.allCollections = collections; }); + + this.vaultFilterService.filteredCollections$ + .pipe(takeUntil(this.componentIsDestroyed$)) + .subscribe((collections) => { + this.filteredCollections = collections; + }); } ngOnDestroy() { @@ -429,19 +412,6 @@ export class VaultComponent this.addType = paramCipherAddType; await this.addCipher(this.addType).catch(() => {}); } - - const paramCipherType = toCipherType(params.type); - this.activeFilter = new VaultFilter({ - status: params.deleted ? "trash" : params.favorites ? "favorites" : "all", - cipherType: params.action === "add" || paramCipherType == null ? undefined : paramCipherType, - selectedFolderId: params.folderId, - selectedCollectionId: params.selectedCollectionId, - selectedOrganizationId: params.selectedOrganizationId, - myVaultOnly: params.myVaultOnly ?? false, - }); - if (this.vaultItemsComponent) { - await this.vaultItemsComponent.reload(this.activeFilter.buildFilter()).catch(() => {}); - } } /** @@ -465,9 +435,7 @@ export class VaultComponent this.cipherId = cipher.id; this.cipher = cipher; this.collections = - this.vaultFilterComponent?.collections?.fullList.filter((c) => - cipher.collectionIds.includes(c.id), - ) ?? null; + this.filteredCollections?.filter((c) => cipher.collectionIds.includes(c.id)) ?? null; this.action = "view"; await this.go().catch(() => {}); @@ -824,19 +792,45 @@ export class VaultComponent await this.go().catch(() => {}); } + /** + * Wraps a filter function to handle CipherListView objects. + * CipherListView has a different type structure where type can be a string or object. + * This wrapper converts it to CipherView-compatible structure before filtering. + */ + private wrapFilterForCipherListView( + filterFn: (cipher: CipherView) => boolean, + ): (cipher: CipherViewLike) => boolean { + return (cipher: CipherViewLike) => { + // For CipherListView, create a proxy object with the correct type property + if (CipherViewLikeUtils.isCipherListView(cipher)) { + const proxyCipher = { + ...cipher, + type: CipherViewLikeUtils.getType(cipher), + // Normalize undefined organizationId to null for filter compatibility + organizationId: cipher.organizationId ?? null, + // Explicitly include isDeleted and isArchived since they might be getters + isDeleted: CipherViewLikeUtils.isDeleted(cipher), + isArchived: CipherViewLikeUtils.isArchived(cipher), + }; + return filterFn(proxyCipher as any); + } + }; + } + async applyVaultFilter(vaultFilter: VaultFilter) { this.searchBarService.setPlaceholderText( this.i18nService.t(this.calculateSearchBarLocalizationString(vaultFilter)), ); this.activeFilter = vaultFilter; - await this.vaultItemsComponent - ?.reload( - this.activeFilter.buildFilter(), - vaultFilter.status === "trash", - vaultFilter.status === "archive", - ) - .catch(() => {}); - await this.go().catch(() => {}); + + const originalFilterFn = this.activeFilter.buildFilter(); + const wrappedFilterFn = this.wrapFilterForCipherListView(originalFilterFn); + + await this.vaultItemsComponent?.reload( + wrappedFilterFn, + vaultFilter.isDeleted, + vaultFilter.isArchived, + ); } private getAvailableCollections(cipher: CipherView): CollectionView[] { @@ -850,25 +844,25 @@ export class VaultComponent } private calculateSearchBarLocalizationString(vaultFilter: VaultFilter): string { - if (vaultFilter.status === "favorites") { + if (vaultFilter.isFavorites) { return "searchFavorites"; } - if (vaultFilter.status === "trash") { + if (vaultFilter.isDeleted) { return "searchTrash"; } if (vaultFilter.cipherType != null) { return "searchType"; } - if (vaultFilter.selectedFolderId != null && vaultFilter.selectedFolderId !== "none") { + if (vaultFilter.folderId != null && vaultFilter.folderId !== "none") { return "searchFolder"; } - if (vaultFilter.selectedCollectionId != null) { + if (vaultFilter.collectionId != null) { return "searchCollection"; } - if (vaultFilter.selectedOrganizationId != null) { + if (vaultFilter.organizationId != null) { return "searchOrganization"; } - if (vaultFilter.myVaultOnly) { + if (vaultFilter.isMyVaultSelected) { return "searchMyVault"; } return "searchVault"; @@ -889,23 +883,6 @@ export class VaultComponent if (!folderView) { return; } - - const dialogRef = AddEditFolderDialogComponent.open(this.dialogService, { - editFolderConfig: { - folder: { - ...folderView, - }, - }, - }); - - const result = await lastValueFrom(dialogRef.closed); - - if ( - result === AddEditFolderDialogResult.Deleted || - result === AddEditFolderDialogResult.Created - ) { - await this.vaultFilterComponent?.reloadCollectionsAndFolders(this.activeFilter); - } } /** Refresh the current cipher object */ @@ -992,22 +969,22 @@ export class VaultComponent } private prefillCipherFromFilter() { - if (this.activeFilter.selectedCollectionId != null && this.vaultFilterComponent != null) { - const collections = this.vaultFilterComponent.collections?.fullList.filter( - (c) => c.id === this.activeFilter.selectedCollectionId, + if (this.activeFilter.collectionId != null) { + const collections = this.filteredCollections?.filter( + (c) => c.id === this.activeFilter.collectionId, ); - if (collections.length > 0) { + if (collections?.length > 0) { this.addOrganizationId = collections[0].organizationId; - this.addCollectionIds = [this.activeFilter.selectedCollectionId]; + this.addCollectionIds = [this.activeFilter.collectionId]; } - } else if (this.activeFilter.selectedOrganizationId) { - this.addOrganizationId = this.activeFilter.selectedOrganizationId; + } else if (this.activeFilter.organizationId) { + this.addOrganizationId = this.activeFilter.organizationId; } else { // clear out organizationId when the user switches to a personal vault filter this.addOrganizationId = null; } - if (this.activeFilter.selectedFolderId && this.activeFilter.selectedFolder) { - this.folderId = this.activeFilter.selectedFolderId; + if (this.activeFilter.folderId && this.activeFilter.selectedFolderNode) { + this.folderId = this.activeFilter.folderId; } if (this.config == null) { From 6e74bf851159bbcf7bcc75d937e87427f832db32 Mon Sep 17 00:00:00 2001 From: Leslie Xiong Date: Thu, 11 Dec 2025 10:39:17 -0500 Subject: [PATCH 18/56] fixed filter route not being preserved when clicking on vault item --- apps/desktop/src/vault/app/vault-v3/vault.component.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/apps/desktop/src/vault/app/vault-v3/vault.component.ts b/apps/desktop/src/vault/app/vault-v3/vault.component.ts index cbc8c5c866d..8f1214c2338 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault.component.ts @@ -922,19 +922,13 @@ export class VaultComponent implements OnInit, OnDestroy, CopyClickListener { queryParams = { action: this.action, cipherId: this.cipherId, - favorites: this.favorites ? true : null, - type: this.type, - folderId: this.folderId, - collectionId: this.collectionId, - deleted: this.deleted ? true : null, - organizationId: this.organizationId, - myVaultOnly: this.myVaultOnly, }; } this.router .navigate([], { relativeTo: this.route, queryParams: queryParams, + queryParamsHandling: "merge", replaceUrl: true, }) .catch(() => {}); From 3792e17ec02159bb76f486b870301036f0f0cfe3 Mon Sep 17 00:00:00 2001 From: Leslie Xiong Date: Thu, 11 Dec 2025 12:09:32 -0500 Subject: [PATCH 19/56] added 'edit folder' button and functionality to folders --- .../filters/folder-filter.component.html | 27 ++++++++++++++++++- .../filters/folder-filter.component.ts | 10 +++++-- .../vault-filter/vault-filter.component.html | 6 ++++- .../vault-filter/vault-filter.component.ts | 27 ++++++++++++++++++- 4 files changed, 65 insertions(+), 5 deletions(-) diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.html index b201913d258..8656ec7fe26 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.html +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.html @@ -7,6 +7,18 @@ [appA11yTitle]="displayName()" (click)="applyFilter($event)" > + @if (folder()?.node.id) { + + } @for (childFolder of folder().children; track childFolder.node.id) { } @@ -19,5 +31,18 @@ [variant]="'tree'" [appA11yTitle]="displayName()" (click)="applyFilter($event)" - /> + > + @if (folder()?.node.id) { + + } +
} diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.ts index 477325c69a3..5efc0d079fe 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.ts @@ -1,6 +1,7 @@ -import { Component, input, computed } from "@angular/core"; +import { Component, input, computed, output } from "@angular/core"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; +import { IconButtonModule } from "@bitwarden/components"; import { VaultFilter, FolderFilter } from "@bitwarden/vault"; import { VAULT_FILTER_IMPORTS } from "../shared-filter-imports"; @@ -11,11 +12,12 @@ import { VAULT_FILTER_IMPORTS } from "../shared-filter-imports"; selector: "app-folder-filter", templateUrl: "folder-filter.component.html", standalone: true, - imports: [...VAULT_FILTER_IMPORTS], + imports: [...VAULT_FILTER_IMPORTS, IconButtonModule], }) export class FolderFilterComponent { protected readonly folder = input>(); protected readonly activeFilter = input(); + protected onEditFolder = output(); protected readonly displayName = computed(() => { return this.folder().node.name; @@ -36,4 +38,8 @@ export class FolderFilterComponent { filter.selectedFolderNode = this.folder(); } } + + protected editFolder(folder: FolderFilter) { + this.onEditFolder.emit(folder); + } } diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.html index 521deabae3b..2cf807cfb24 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.html +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.html @@ -22,7 +22,11 @@ @if (!hideFolders()) { @for (folder of (folders$ | async)?.children ?? []; track folder.node.id) { - + } } diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts index 709e9afd190..60f4ebedd26 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts @@ -8,8 +8,9 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { UserId } from "@bitwarden/common/types/guid"; import { CipherArchiveService } from "@bitwarden/common/vault/abstractions/cipher-archive.service"; +import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; -import { NavigationModule } from "@bitwarden/components"; +import { NavigationModule, DialogService } from "@bitwarden/components"; import { I18nPipe } from "@bitwarden/ui-common"; import { OrganizationFilter, @@ -19,6 +20,7 @@ import { VaultFilter, VaultFilterServiceAbstraction as VaultFilterService, RoutedVaultFilterBridgeService, + AddEditFolderDialogComponent, } from "@bitwarden/vault"; import { FolderFilterComponent } from "./filters/folder-filter.component"; @@ -47,7 +49,9 @@ export class VaultFilterComponent implements OnInit { private vaultFilterService: VaultFilterService = inject(VaultFilterService); private accountService: AccountService = inject(AccountService); private cipherArchiveService: CipherArchiveService = inject(CipherArchiveService); + private folderService: FolderService = inject(FolderService); private policyService: PolicyService = inject(PolicyService); + private dialogService: DialogService = inject(DialogService); private componentIsDestroyed$ = new Subject(); protected activeFilter: VaultFilter; @@ -106,4 +110,25 @@ export class VaultFilterComponent implements OnInit { this.isLoaded = true; } + + protected async editFolder(folder: FolderFilter) { + if (!this.activeUserId) { + return; + } + const folderView = await firstValueFrom( + this.folderService.getDecrypted$(folder.id, this.activeUserId), + ); + + if (!folderView) { + return; + } + + AddEditFolderDialogComponent.open(this.dialogService, { + editFolderConfig: { + folder: { + ...folderView, + }, + }, + }); + } } From 4502642f0a974cde09d5d79a5cd91f14ab4f1b78 Mon Sep 17 00:00:00 2001 From: Leslie Xiong Date: Thu, 11 Dec 2025 13:50:16 -0500 Subject: [PATCH 20/56] fixes from merging in main --- apps/desktop/src/app/app-routing.module.ts | 1 + .../vault/app/vault-v3/vault-filter/vault-filter.component.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/desktop/src/app/app-routing.module.ts b/apps/desktop/src/app/app-routing.module.ts index 42f9d7ae779..c53a3d32178 100644 --- a/apps/desktop/src/app/app-routing.module.ts +++ b/apps/desktop/src/app/app-routing.module.ts @@ -60,6 +60,7 @@ import { reactiveUnlockVaultGuard } from "../autofill/guards/reactive-vault-guar import { Fido2CreateComponent } from "../autofill/modal/credentials/fido2-create.component"; import { Fido2ExcludedCiphersComponent } from "../autofill/modal/credentials/fido2-excluded-ciphers.component"; import { Fido2VaultComponent } from "../autofill/modal/credentials/fido2-vault.component"; +import { DesktopPremiumUpgradePromptService } from "../services/desktop-premium-upgrade-prompt.service"; import { VaultV2Component } from "../vault/app/vault/vault-v2.component"; import { VaultComponent } from "../vault/app/vault-v3/vault.component"; diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts index 60f4ebedd26..7efd3b2f0b3 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts @@ -98,7 +98,7 @@ export class VaultFilterComponent implements OnInit { this.collections$ = this.vaultFilterService.collectionTree$; this.showArchiveVaultFilter = await firstValueFrom( - this.cipherArchiveService.hasArchiveFlagEnabled$(), + this.cipherArchiveService.hasArchiveFlagEnabled$, ); // Subscribe to the active filter from the bridge service From 030f4a66c5e0f9c64fb295f96bf6f7334960b2a1 Mon Sep 17 00:00:00 2001 From: Leslie Xiong Date: Mon, 15 Dec 2025 12:10:13 -0500 Subject: [PATCH 21/56] prettier --- .../extension-device-management-component.service.ts | 4 +--- .../services/browser-fido2-user-interface.service.ts | 4 +--- .../content/overlay-notifications-content.service.ts | 4 +--- .../inline-menu-field-qualification.service.ts | 4 +--- .../services/desktop-fido2-user-interface.service.ts | 4 +--- .../organization-user-reset-password.service.ts | 11 ++++------- .../webauthn-login/webauthn-login-admin.service.ts | 4 +--- .../services/emergency-access.service.ts | 11 ++++------- .../default-device-management-component.service.ts | 4 +--- ...default-login-approval-dialog-component.service.ts | 4 +--- .../encrypted-migrations-scheduler.service.ts | 4 +--- ...fault-new-device-verification-component.service.ts | 4 +--- ...ault-two-factor-auth-webauthn-component.service.ts | 4 +--- .../user-decryption-options.service.ts | 4 +--- ...ult-organization-management-preferences.service.ts | 4 +--- ...assword-reset-enrollment.service.implementation.ts | 4 +--- .../organization-sponsorship-api.service.ts | 4 +--- libs/common/src/platform/ipc/ipc-message.ts | 6 ++++-- .../services/fido2/fido2-authenticator.service.ts | 6 +++--- .../platform/services/fido2/fido2-client.service.ts | 6 +++--- libs/common/src/tools/state/secret-classifier.ts | 8 +++++--- libs/common/src/tools/state/secret-state.ts | 10 +++++++--- libs/common/src/tools/state/user-state-subject.ts | 10 +++++----- libs/components/src/dialog/dialog.service.ts | 7 ++++--- ...lt-user-asymmetric-key-regeneration-api.service.ts | 4 +--- ...efault-user-asymmetric-key-regeneration.service.ts | 4 +--- libs/state-internal/src/default-derived-state.ts | 8 +++++--- libs/state-internal/src/inline-derived-state.ts | 8 +++++--- libs/state-test-utils/src/fake-state.ts | 8 +++++--- .../core/src/policies/default-policy-evaluator.ts | 7 ++++--- .../policies/dynamic-password-policy-constraints.ts | 4 +--- .../src/strategies/catchall-generator-strategy.ts | 7 ++++--- .../src/strategies/eff-username-generator-strategy.ts | 7 ++++--- .../src/strategies/passphrase-generator-strategy.ts | 7 ++++--- .../src/strategies/password-generator-strategy.ts | 7 ++++--- .../src/strategies/subaddress-generator-strategy.ts | 7 ++++--- .../navigation/src/generator-navigation-evaluator.ts | 7 ++++--- 37 files changed, 100 insertions(+), 120 deletions(-) diff --git a/apps/browser/src/auth/services/extension-device-management-component.service.ts b/apps/browser/src/auth/services/extension-device-management-component.service.ts index 2585ba3198c..eb7ea4be37b 100644 --- a/apps/browser/src/auth/services/extension-device-management-component.service.ts +++ b/apps/browser/src/auth/services/extension-device-management-component.service.ts @@ -3,9 +3,7 @@ import { DeviceManagementComponentServiceAbstraction } from "@bitwarden/angular/ /** * Browser extension implementation of the device management component service */ -export class ExtensionDeviceManagementComponentService - implements DeviceManagementComponentServiceAbstraction -{ +export class ExtensionDeviceManagementComponentService implements DeviceManagementComponentServiceAbstraction { /** * Don't show header information in browser extension client */ diff --git a/apps/browser/src/autofill/fido2/services/browser-fido2-user-interface.service.ts b/apps/browser/src/autofill/fido2/services/browser-fido2-user-interface.service.ts index 5818bbf8d82..e0ab45e9f84 100644 --- a/apps/browser/src/autofill/fido2/services/browser-fido2-user-interface.service.ts +++ b/apps/browser/src/autofill/fido2/services/browser-fido2-user-interface.service.ts @@ -120,9 +120,7 @@ export type BrowserFido2ParentWindowReference = chrome.tabs.Tab; * Browser implementation of the {@link Fido2UserInterfaceService}. * The user interface is implemented as a popout and the service uses the browser's messaging API to communicate with it. */ -export class BrowserFido2UserInterfaceService - implements Fido2UserInterfaceServiceAbstraction -{ +export class BrowserFido2UserInterfaceService implements Fido2UserInterfaceServiceAbstraction { constructor(private authService: AuthService) {} async newSession( diff --git a/apps/browser/src/autofill/overlay/notifications/content/overlay-notifications-content.service.ts b/apps/browser/src/autofill/overlay/notifications/content/overlay-notifications-content.service.ts index 8dc08169468..ab3b8144426 100644 --- a/apps/browser/src/autofill/overlay/notifications/content/overlay-notifications-content.service.ts +++ b/apps/browser/src/autofill/overlay/notifications/content/overlay-notifications-content.service.ts @@ -15,9 +15,7 @@ import { OverlayNotificationsExtensionMessageHandlers, } from "../abstractions/overlay-notifications-content.service"; -export class OverlayNotificationsContentService - implements OverlayNotificationsContentServiceInterface -{ +export class OverlayNotificationsContentService implements OverlayNotificationsContentServiceInterface { private notificationBarRootElement: HTMLElement | null = null; private notificationBarElement: HTMLElement | null = null; private notificationBarIframeElement: HTMLIFrameElement | null = null; diff --git a/apps/browser/src/autofill/services/inline-menu-field-qualification.service.ts b/apps/browser/src/autofill/services/inline-menu-field-qualification.service.ts index f6afaae202f..65f9eee1ecb 100644 --- a/apps/browser/src/autofill/services/inline-menu-field-qualification.service.ts +++ b/apps/browser/src/autofill/services/inline-menu-field-qualification.service.ts @@ -16,9 +16,7 @@ import { } from "./autofill-constants"; import AutofillService from "./autofill.service"; -export class InlineMenuFieldQualificationService - implements InlineMenuFieldQualificationServiceInterface -{ +export class InlineMenuFieldQualificationService implements InlineMenuFieldQualificationServiceInterface { private searchFieldNamesSet = new Set(AutoFillConstants.SearchFieldNames); private excludedAutofillFieldTypesSet = new Set(AutoFillConstants.ExcludedAutofillLoginTypes); private usernameFieldTypes = new Set(["text", "email", "number", "tel"]); diff --git a/apps/desktop/src/autofill/services/desktop-fido2-user-interface.service.ts b/apps/desktop/src/autofill/services/desktop-fido2-user-interface.service.ts index 19946ab590c..cf29370840d 100644 --- a/apps/desktop/src/autofill/services/desktop-fido2-user-interface.service.ts +++ b/apps/desktop/src/autofill/services/desktop-fido2-user-interface.service.ts @@ -43,9 +43,7 @@ export type NativeWindowObject = { windowXy?: { x: number; y: number }; }; -export class DesktopFido2UserInterfaceService - implements Fido2UserInterfaceServiceAbstraction -{ +export class DesktopFido2UserInterfaceService implements Fido2UserInterfaceServiceAbstraction { constructor( private authService: AuthService, private cipherService: CipherService, diff --git a/apps/web/src/app/admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service.ts b/apps/web/src/app/admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service.ts index df5e7e8a25c..88797f86650 100644 --- a/apps/web/src/app/admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service.ts +++ b/apps/web/src/app/admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service.ts @@ -35,13 +35,10 @@ import { OrganizationUserResetPasswordEntry } from "./organization-user-reset-pa @Injectable({ providedIn: "root", }) -export class OrganizationUserResetPasswordService - implements - UserKeyRotationKeyRecoveryProvider< - OrganizationUserResetPasswordWithIdRequest, - OrganizationUserResetPasswordEntry - > -{ +export class OrganizationUserResetPasswordService implements UserKeyRotationKeyRecoveryProvider< + OrganizationUserResetPasswordWithIdRequest, + OrganizationUserResetPasswordEntry +> { constructor( private keyService: KeyService, private encryptService: EncryptService, diff --git a/apps/web/src/app/auth/core/services/webauthn-login/webauthn-login-admin.service.ts b/apps/web/src/app/auth/core/services/webauthn-login/webauthn-login-admin.service.ts index 7765d01f75c..3fc57e1a22c 100644 --- a/apps/web/src/app/auth/core/services/webauthn-login/webauthn-login-admin.service.ts +++ b/apps/web/src/app/auth/core/services/webauthn-login/webauthn-login-admin.service.ts @@ -39,9 +39,7 @@ import { WebAuthnLoginAdminApiService } from "./webauthn-login-admin-api.service /** * Service for managing WebAuthnLogin credentials. */ -export class WebauthnLoginAdminService - implements UserKeyRotationDataProvider -{ +export class WebauthnLoginAdminService implements UserKeyRotationDataProvider { static readonly MaxCredentialCount = 5; private navigatorCredentials: CredentialsContainer; diff --git a/apps/web/src/app/auth/emergency-access/services/emergency-access.service.ts b/apps/web/src/app/auth/emergency-access/services/emergency-access.service.ts index b91bc932e83..80b1b27116b 100644 --- a/apps/web/src/app/auth/emergency-access/services/emergency-access.service.ts +++ b/apps/web/src/app/auth/emergency-access/services/emergency-access.service.ts @@ -45,13 +45,10 @@ import { EmergencyAccessGranteeDetailsResponse } from "../response/emergency-acc import { EmergencyAccessApiService } from "./emergency-access-api.service"; @Injectable() -export class EmergencyAccessService - implements - UserKeyRotationKeyRecoveryProvider< - EmergencyAccessWithIdRequest, - GranteeEmergencyAccessWithPublicKey - > -{ +export class EmergencyAccessService implements UserKeyRotationKeyRecoveryProvider< + EmergencyAccessWithIdRequest, + GranteeEmergencyAccessWithPublicKey +> { constructor( private emergencyAccessApiService: EmergencyAccessApiService, private apiService: ApiService, diff --git a/libs/angular/src/auth/device-management/default-device-management-component.service.ts b/libs/angular/src/auth/device-management/default-device-management-component.service.ts index 5089ba259a5..169ee6ce7b6 100644 --- a/libs/angular/src/auth/device-management/default-device-management-component.service.ts +++ b/libs/angular/src/auth/device-management/default-device-management-component.service.ts @@ -3,9 +3,7 @@ import { DeviceManagementComponentServiceAbstraction } from "./device-management /** * Default implementation of the device management component service */ -export class DefaultDeviceManagementComponentService - implements DeviceManagementComponentServiceAbstraction -{ +export class DefaultDeviceManagementComponentService implements DeviceManagementComponentServiceAbstraction { /** * Show header information in web client */ diff --git a/libs/angular/src/auth/login-approval/default-login-approval-dialog-component.service.ts b/libs/angular/src/auth/login-approval/default-login-approval-dialog-component.service.ts index 5fefd3c3abb..4a9a37fd0de 100644 --- a/libs/angular/src/auth/login-approval/default-login-approval-dialog-component.service.ts +++ b/libs/angular/src/auth/login-approval/default-login-approval-dialog-component.service.ts @@ -3,9 +3,7 @@ import { LoginApprovalDialogComponentServiceAbstraction } from "./login-approval /** * Default implementation of the LoginApprovalDialogComponentServiceAbstraction. */ -export class DefaultLoginApprovalDialogComponentService - implements LoginApprovalDialogComponentServiceAbstraction -{ +export class DefaultLoginApprovalDialogComponentService implements LoginApprovalDialogComponentServiceAbstraction { /** * No-op implementation of the showLoginRequestedAlertIfWindowNotVisible method. * @returns diff --git a/libs/angular/src/key-management/encrypted-migration/encrypted-migrations-scheduler.service.ts b/libs/angular/src/key-management/encrypted-migration/encrypted-migrations-scheduler.service.ts index aea838ef4c6..dd248a582d3 100644 --- a/libs/angular/src/key-management/encrypted-migration/encrypted-migrations-scheduler.service.ts +++ b/libs/angular/src/key-management/encrypted-migration/encrypted-migrations-scheduler.service.ts @@ -45,9 +45,7 @@ const VAULT_ROUTES = ["/vault", "/tabs/vault", "/tabs/current"]; * if it is required by showing a UI prompt. It is only one means of triggering migrations, in case the user stays unlocked for a while, * or regularly logs in without a master-password, when the migrations do require a master-password to run. */ -export class DefaultEncryptedMigrationsSchedulerService - implements EncryptedMigrationsSchedulerService -{ +export class DefaultEncryptedMigrationsSchedulerService implements EncryptedMigrationsSchedulerService { isMigrating = false; url$: Observable; diff --git a/libs/auth/src/angular/new-device-verification/default-new-device-verification-component.service.ts b/libs/auth/src/angular/new-device-verification/default-new-device-verification-component.service.ts index 88ea652bc4b..4e7731a412b 100644 --- a/libs/auth/src/angular/new-device-verification/default-new-device-verification-component.service.ts +++ b/libs/auth/src/angular/new-device-verification/default-new-device-verification-component.service.ts @@ -1,8 +1,6 @@ import { NewDeviceVerificationComponentService } from "./new-device-verification-component.service"; -export class DefaultNewDeviceVerificationComponentService - implements NewDeviceVerificationComponentService -{ +export class DefaultNewDeviceVerificationComponentService implements NewDeviceVerificationComponentService { showBackButton() { return true; } diff --git a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-webauthn/default-two-factor-auth-webauthn-component.service.ts b/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-webauthn/default-two-factor-auth-webauthn-component.service.ts index 3d3578c656e..545c57f2946 100644 --- a/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-webauthn/default-two-factor-auth-webauthn-component.service.ts +++ b/libs/auth/src/angular/two-factor-auth/child-components/two-factor-auth-webauthn/default-two-factor-auth-webauthn-component.service.ts @@ -1,8 +1,6 @@ import { TwoFactorAuthWebAuthnComponentService } from "./two-factor-auth-webauthn-component.service"; -export class DefaultTwoFactorAuthWebAuthnComponentService - implements TwoFactorAuthWebAuthnComponentService -{ +export class DefaultTwoFactorAuthWebAuthnComponentService implements TwoFactorAuthWebAuthnComponentService { /** * Default implementation is to not open in a new tab. */ diff --git a/libs/auth/src/common/services/user-decryption-options/user-decryption-options.service.ts b/libs/auth/src/common/services/user-decryption-options/user-decryption-options.service.ts index a0075d1987b..09d73f0224d 100644 --- a/libs/auth/src/common/services/user-decryption-options/user-decryption-options.service.ts +++ b/libs/auth/src/common/services/user-decryption-options/user-decryption-options.service.ts @@ -19,9 +19,7 @@ export const USER_DECRYPTION_OPTIONS = new UserKeyDefinition { diff --git a/libs/common/src/admin-console/services/organization-management-preferences/default-organization-management-preferences.service.ts b/libs/common/src/admin-console/services/organization-management-preferences/default-organization-management-preferences.service.ts index e257b691638..55af4b0bf29 100644 --- a/libs/common/src/admin-console/services/organization-management-preferences/default-organization-management-preferences.service.ts +++ b/libs/common/src/admin-console/services/organization-management-preferences/default-organization-management-preferences.service.ts @@ -27,9 +27,7 @@ function buildKeyDefinition(key: string): UserKeyDefinition { export const AUTO_CONFIRM_FINGERPRINTS = buildKeyDefinition("autoConfirmFingerPrints"); -export class DefaultOrganizationManagementPreferencesService - implements OrganizationManagementPreferencesService -{ +export class DefaultOrganizationManagementPreferencesService implements OrganizationManagementPreferencesService { constructor(private stateProvider: StateProvider) {} autoConfirmFingerPrints = this.buildOrganizationManagementPreference( diff --git a/libs/common/src/auth/services/password-reset-enrollment.service.implementation.ts b/libs/common/src/auth/services/password-reset-enrollment.service.implementation.ts index 55644009f16..a1464ed9c9b 100644 --- a/libs/common/src/auth/services/password-reset-enrollment.service.implementation.ts +++ b/libs/common/src/auth/services/password-reset-enrollment.service.implementation.ts @@ -22,9 +22,7 @@ import { UserKey } from "../../types/key"; import { AccountService } from "../abstractions/account.service"; import { PasswordResetEnrollmentServiceAbstraction } from "../abstractions/password-reset-enrollment.service.abstraction"; -export class PasswordResetEnrollmentServiceImplementation - implements PasswordResetEnrollmentServiceAbstraction -{ +export class PasswordResetEnrollmentServiceImplementation implements PasswordResetEnrollmentServiceAbstraction { constructor( protected organizationApiService: OrganizationApiServiceAbstraction, protected accountService: AccountService, diff --git a/libs/common/src/billing/services/organization/organization-sponsorship-api.service.ts b/libs/common/src/billing/services/organization/organization-sponsorship-api.service.ts index de0ff302737..dea3979ac2d 100644 --- a/libs/common/src/billing/services/organization/organization-sponsorship-api.service.ts +++ b/libs/common/src/billing/services/organization/organization-sponsorship-api.service.ts @@ -4,9 +4,7 @@ import { PlatformUtilsService } from "../../../platform/abstractions/platform-ut import { OrganizationSponsorshipApiServiceAbstraction } from "../../abstractions/organizations/organization-sponsorship-api.service.abstraction"; import { OrganizationSponsorshipInvitesResponse } from "../../models/response/organization-sponsorship-invites.response"; -export class OrganizationSponsorshipApiService - implements OrganizationSponsorshipApiServiceAbstraction -{ +export class OrganizationSponsorshipApiService implements OrganizationSponsorshipApiServiceAbstraction { constructor( private apiService: ApiService, private platformUtilsService: PlatformUtilsService, diff --git a/libs/common/src/platform/ipc/ipc-message.ts b/libs/common/src/platform/ipc/ipc-message.ts index c3ac6360597..ede68e170c5 100644 --- a/libs/common/src/platform/ipc/ipc-message.ts +++ b/libs/common/src/platform/ipc/ipc-message.ts @@ -5,8 +5,10 @@ export interface IpcMessage { message: SerializedOutgoingMessage; } -export interface SerializedOutgoingMessage - extends Omit { +export interface SerializedOutgoingMessage extends Omit< + OutgoingMessage, + typeof Symbol.dispose | "free" | "payload" +> { payload: number[]; } diff --git a/libs/common/src/platform/services/fido2/fido2-authenticator.service.ts b/libs/common/src/platform/services/fido2/fido2-authenticator.service.ts index e560a77cc2e..d1081e9f7b2 100644 --- a/libs/common/src/platform/services/fido2/fido2-authenticator.service.ts +++ b/libs/common/src/platform/services/fido2/fido2-authenticator.service.ts @@ -46,9 +46,9 @@ const KeyUsages: KeyUsage[] = ["sign"]; * * It is highly recommended that the W3C specification is used a reference when reading this code. */ -export class Fido2AuthenticatorService - implements Fido2AuthenticatorServiceAbstraction -{ +export class Fido2AuthenticatorService< + ParentWindowReference, +> implements Fido2AuthenticatorServiceAbstraction { constructor( private cipherService: CipherService, private userInterface: Fido2UserInterfaceService, diff --git a/libs/common/src/platform/services/fido2/fido2-client.service.ts b/libs/common/src/platform/services/fido2/fido2-client.service.ts index 08c0a265100..503ffef8241 100644 --- a/libs/common/src/platform/services/fido2/fido2-client.service.ts +++ b/libs/common/src/platform/services/fido2/fido2-client.service.ts @@ -47,9 +47,9 @@ import { guidToRawFormat } from "./guid-utils"; * * It is highly recommended that the W3C specification is used a reference when reading this code. */ -export class Fido2ClientService - implements Fido2ClientServiceAbstraction -{ +export class Fido2ClientService< + ParentWindowReference, +> implements Fido2ClientServiceAbstraction { private timeoutAbortController: AbortController; private readonly TIMEOUTS = { NO_VERIFICATION: { diff --git a/libs/common/src/tools/state/secret-classifier.ts b/libs/common/src/tools/state/secret-classifier.ts index e961ffcd20e..ddb4bc2c63a 100644 --- a/libs/common/src/tools/state/secret-classifier.ts +++ b/libs/common/src/tools/state/secret-classifier.ts @@ -14,9 +14,11 @@ import { Classifier } from "./classifier"; * Data that cannot be serialized by JSON.stringify() should * be excluded. */ -export class SecretClassifier - implements Classifier<Plaintext, Disclosed, Secret> -{ +export class SecretClassifier<Plaintext extends object, Disclosed, Secret> implements Classifier< + Plaintext, + Disclosed, + Secret +> { private constructor( disclosed: readonly (keyof Jsonify<Disclosed> & keyof Jsonify<Plaintext>)[], excluded: readonly (keyof Plaintext)[], diff --git a/libs/common/src/tools/state/secret-state.ts b/libs/common/src/tools/state/secret-state.ts index 91f45a51211..0aa808d2cb7 100644 --- a/libs/common/src/tools/state/secret-state.ts +++ b/libs/common/src/tools/state/secret-state.ts @@ -25,9 +25,13 @@ const ONE_MINUTE = 1000 * 60; * * DO NOT USE THIS for synchronized data. */ -export class SecretState<Outer, Id, Plaintext extends object, Disclosed, Secret> - implements SingleUserState<Outer> -{ +export class SecretState< + Outer, + Id, + Plaintext extends object, + Disclosed, + Secret, +> implements SingleUserState<Outer> { // The constructor is private to avoid creating a circular dependency when // wiring the derived and secret states together. private constructor( diff --git a/libs/common/src/tools/state/user-state-subject.ts b/libs/common/src/tools/state/user-state-subject.ts index e6b66d8f699..f9cdf87ea04 100644 --- a/libs/common/src/tools/state/user-state-subject.ts +++ b/libs/common/src/tools/state/user-state-subject.ts @@ -79,11 +79,11 @@ const DEFAULT_FRAME_SIZE = 32; * @template Dependencies use-specific dependencies provided by the user. */ export class UserStateSubject< - State extends object, - Secret = State, - Disclosed = Record<string, never>, - Dependencies = null, - > + State extends object, + Secret = State, + Disclosed = Record<string, never>, + Dependencies = null, +> extends Observable<State> implements SubjectLike<State> { diff --git a/libs/components/src/dialog/dialog.service.ts b/libs/components/src/dialog/dialog.service.ts index 1fc452418e1..804b650beab 100644 --- a/libs/components/src/dialog/dialog.service.ts +++ b/libs/components/src/dialog/dialog.service.ts @@ -43,9 +43,10 @@ class CustomBlockScrollStrategy implements ScrollStrategy { detach() {} } -export abstract class DialogRef<R = unknown, C = unknown> - implements Pick<CdkDialogRef<R, C>, "close" | "closed" | "disableClose" | "componentInstance"> -{ +export abstract class DialogRef<R = unknown, C = unknown> implements Pick< + CdkDialogRef<R, C>, + "close" | "closed" | "disableClose" | "componentInstance" +> { abstract readonly isDrawer?: boolean; // --- From CdkDialogRef --- diff --git a/libs/key-management/src/user-asymmetric-key-regeneration/services/default-user-asymmetric-key-regeneration-api.service.ts b/libs/key-management/src/user-asymmetric-key-regeneration/services/default-user-asymmetric-key-regeneration-api.service.ts index a79bcfd0ef3..37410b9fffb 100644 --- a/libs/key-management/src/user-asymmetric-key-regeneration/services/default-user-asymmetric-key-regeneration-api.service.ts +++ b/libs/key-management/src/user-asymmetric-key-regeneration/services/default-user-asymmetric-key-regeneration-api.service.ts @@ -4,9 +4,7 @@ import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-st import { UserAsymmetricKeysRegenerationApiService } from "../abstractions/user-asymmetric-key-regeneration-api.service"; import { KeyRegenerationRequest } from "../models/requests/key-regeneration.request"; -export class DefaultUserAsymmetricKeysRegenerationApiService - implements UserAsymmetricKeysRegenerationApiService -{ +export class DefaultUserAsymmetricKeysRegenerationApiService implements UserAsymmetricKeysRegenerationApiService { constructor(private apiService: ApiService) {} async regenerateUserAsymmetricKeys( diff --git a/libs/key-management/src/user-asymmetric-key-regeneration/services/default-user-asymmetric-key-regeneration.service.ts b/libs/key-management/src/user-asymmetric-key-regeneration/services/default-user-asymmetric-key-regeneration.service.ts index 48fe3a1686f..36bf9c8a421 100644 --- a/libs/key-management/src/user-asymmetric-key-regeneration/services/default-user-asymmetric-key-regeneration.service.ts +++ b/libs/key-management/src/user-asymmetric-key-regeneration/services/default-user-asymmetric-key-regeneration.service.ts @@ -15,9 +15,7 @@ import { KeyService } from "../../abstractions/key.service"; import { UserAsymmetricKeysRegenerationApiService } from "../abstractions/user-asymmetric-key-regeneration-api.service"; import { UserAsymmetricKeysRegenerationService } from "../abstractions/user-asymmetric-key-regeneration.service"; -export class DefaultUserAsymmetricKeysRegenerationService - implements UserAsymmetricKeysRegenerationService -{ +export class DefaultUserAsymmetricKeysRegenerationService implements UserAsymmetricKeysRegenerationService { constructor( private keyService: KeyService, private cipherService: CipherService, diff --git a/libs/state-internal/src/default-derived-state.ts b/libs/state-internal/src/default-derived-state.ts index ce84be93f92..60355dc033b 100644 --- a/libs/state-internal/src/default-derived-state.ts +++ b/libs/state-internal/src/default-derived-state.ts @@ -5,9 +5,11 @@ import { DeriveDefinition, DerivedState, DerivedStateDependencies } from "@bitwa /** * Default derived state */ -export class DefaultDerivedState<TFrom, TTo, TDeps extends DerivedStateDependencies> - implements DerivedState<TTo> -{ +export class DefaultDerivedState< + TFrom, + TTo, + TDeps extends DerivedStateDependencies, +> implements DerivedState<TTo> { private readonly storageKey: string; private forcedValueSubject = new Subject<TTo>(); diff --git a/libs/state-internal/src/inline-derived-state.ts b/libs/state-internal/src/inline-derived-state.ts index 2c03443d42c..cdbc329e050 100644 --- a/libs/state-internal/src/inline-derived-state.ts +++ b/libs/state-internal/src/inline-derived-state.ts @@ -17,9 +17,11 @@ export class InlineDerivedStateProvider implements DerivedStateProvider { } } -export class InlineDerivedState<TFrom, TTo, TDeps extends DerivedStateDependencies> - implements DerivedState<TTo> -{ +export class InlineDerivedState< + TFrom, + TTo, + TDeps extends DerivedStateDependencies, +> implements DerivedState<TTo> { constructor( parentState$: Observable<TFrom>, deriveDefinition: DeriveDefinition<TFrom, TTo, TDeps>, diff --git a/libs/state-test-utils/src/fake-state.ts b/libs/state-test-utils/src/fake-state.ts index 25aabcd9933..21cb5e7aa73 100644 --- a/libs/state-test-utils/src/fake-state.ts +++ b/libs/state-test-utils/src/fake-state.ts @@ -236,9 +236,11 @@ export class FakeActiveUserState<T> implements ActiveUserState<T> { } } -export class FakeDerivedState<TFrom, TTo, TDeps extends DerivedStateDependencies> - implements DerivedState<TTo> -{ +export class FakeDerivedState< + TFrom, + TTo, + TDeps extends DerivedStateDependencies, +> implements DerivedState<TTo> { // eslint-disable-next-line rxjs/no-exposed-subjects -- exposed for testing setup stateSubject = new ReplaySubject<TTo>(1); diff --git a/libs/tools/generator/core/src/policies/default-policy-evaluator.ts b/libs/tools/generator/core/src/policies/default-policy-evaluator.ts index 384b3bc1aeb..2d2ce48aec8 100644 --- a/libs/tools/generator/core/src/policies/default-policy-evaluator.ts +++ b/libs/tools/generator/core/src/policies/default-policy-evaluator.ts @@ -2,9 +2,10 @@ import { PolicyEvaluator } from "../abstractions"; import { NoPolicy } from "../types"; /** A policy evaluator that does not apply any policy */ -export class DefaultPolicyEvaluator<PolicyTarget> - implements PolicyEvaluator<NoPolicy, PolicyTarget> -{ +export class DefaultPolicyEvaluator<PolicyTarget> implements PolicyEvaluator< + NoPolicy, + PolicyTarget +> { /** {@link PolicyEvaluator.policy} */ get policy() { return {}; diff --git a/libs/tools/generator/core/src/policies/dynamic-password-policy-constraints.ts b/libs/tools/generator/core/src/policies/dynamic-password-policy-constraints.ts index b1e582637e4..32a6099d6cc 100644 --- a/libs/tools/generator/core/src/policies/dynamic-password-policy-constraints.ts +++ b/libs/tools/generator/core/src/policies/dynamic-password-policy-constraints.ts @@ -13,9 +13,7 @@ import { atLeast, atLeastSum, maybe, readonlyTrueWhen, AtLeastOne, Zero } from " import { PasswordPolicyConstraints } from "./password-policy-constraints"; /** Creates state constraints by blending policy and password settings. */ -export class DynamicPasswordPolicyConstraints - implements DynamicStateConstraints<PasswordGeneratorSettings> -{ +export class DynamicPasswordPolicyConstraints implements DynamicStateConstraints<PasswordGeneratorSettings> { /** Instantiates the object. * @param policy the password policy to enforce. This cannot be * `null` or `undefined`. diff --git a/libs/tools/generator/core/src/strategies/catchall-generator-strategy.ts b/libs/tools/generator/core/src/strategies/catchall-generator-strategy.ts index 36365fc338f..a093aa164f1 100644 --- a/libs/tools/generator/core/src/strategies/catchall-generator-strategy.ts +++ b/libs/tools/generator/core/src/strategies/catchall-generator-strategy.ts @@ -13,9 +13,10 @@ import { observe$PerUserId, sharedStateByUserId } from "../util"; import { CATCHALL_SETTINGS } from "./storage"; /** Strategy for creating usernames using a catchall email address */ -export class CatchallGeneratorStrategy - implements GeneratorStrategy<CatchallGenerationOptions, NoPolicy> -{ +export class CatchallGeneratorStrategy implements GeneratorStrategy< + CatchallGenerationOptions, + NoPolicy +> { /** Instantiates the generation strategy * @param usernameService generates a catchall address for a domain */ diff --git a/libs/tools/generator/core/src/strategies/eff-username-generator-strategy.ts b/libs/tools/generator/core/src/strategies/eff-username-generator-strategy.ts index ebeacef81e8..8c75d2d2a34 100644 --- a/libs/tools/generator/core/src/strategies/eff-username-generator-strategy.ts +++ b/libs/tools/generator/core/src/strategies/eff-username-generator-strategy.ts @@ -16,9 +16,10 @@ const UsernameDigits = Object.freeze({ }); /** Strategy for creating usernames from the EFF wordlist */ -export class EffUsernameGeneratorStrategy - implements GeneratorStrategy<EffUsernameGenerationOptions, NoPolicy> -{ +export class EffUsernameGeneratorStrategy implements GeneratorStrategy< + EffUsernameGenerationOptions, + NoPolicy +> { /** Instantiates the generation strategy * @param usernameService generates a username from EFF word list */ diff --git a/libs/tools/generator/core/src/strategies/passphrase-generator-strategy.ts b/libs/tools/generator/core/src/strategies/passphrase-generator-strategy.ts index 374df84a5bd..be2984e09e1 100644 --- a/libs/tools/generator/core/src/strategies/passphrase-generator-strategy.ts +++ b/libs/tools/generator/core/src/strategies/passphrase-generator-strategy.ts @@ -14,9 +14,10 @@ import { observe$PerUserId, optionsToEffWordListRequest, sharedStateByUserId } f import { PASSPHRASE_SETTINGS } from "./storage"; /** Generates passphrases composed of random words */ -export class PassphraseGeneratorStrategy - implements GeneratorStrategy<PassphraseGenerationOptions, PassphraseGeneratorPolicy> -{ +export class PassphraseGeneratorStrategy implements GeneratorStrategy< + PassphraseGenerationOptions, + PassphraseGeneratorPolicy +> { /** instantiates the password generator strategy. * @param legacy generates the passphrase * @param stateProvider provides durable state diff --git a/libs/tools/generator/core/src/strategies/password-generator-strategy.ts b/libs/tools/generator/core/src/strategies/password-generator-strategy.ts index 1a5070901c2..ab8a04cf79b 100644 --- a/libs/tools/generator/core/src/strategies/password-generator-strategy.ts +++ b/libs/tools/generator/core/src/strategies/password-generator-strategy.ts @@ -12,9 +12,10 @@ import { observe$PerUserId, optionsToRandomAsciiRequest, sharedStateByUserId } f import { PASSWORD_SETTINGS } from "./storage"; /** Generates passwords composed of random characters */ -export class PasswordGeneratorStrategy - implements GeneratorStrategy<PasswordGenerationOptions, PasswordGeneratorPolicy> -{ +export class PasswordGeneratorStrategy implements GeneratorStrategy< + PasswordGenerationOptions, + PasswordGeneratorPolicy +> { /** instantiates the password generator strategy. * @param legacy generates the password */ diff --git a/libs/tools/generator/core/src/strategies/subaddress-generator-strategy.ts b/libs/tools/generator/core/src/strategies/subaddress-generator-strategy.ts index 86df7f1c667..f0c7c482060 100644 --- a/libs/tools/generator/core/src/strategies/subaddress-generator-strategy.ts +++ b/libs/tools/generator/core/src/strategies/subaddress-generator-strategy.ts @@ -17,9 +17,10 @@ import { SUBADDRESS_SETTINGS } from "./storage"; * For example, if the email address is `jd+xyz@domain.io`, * the subaddress is `xyz`. */ -export class SubaddressGeneratorStrategy - implements GeneratorStrategy<SubaddressGenerationOptions, NoPolicy> -{ +export class SubaddressGeneratorStrategy implements GeneratorStrategy< + SubaddressGenerationOptions, + NoPolicy +> { /** Instantiates the generation strategy * @param usernameService generates an email subaddress from an email address */ diff --git a/libs/tools/generator/extensions/navigation/src/generator-navigation-evaluator.ts b/libs/tools/generator/extensions/navigation/src/generator-navigation-evaluator.ts index 5446c1f26ad..e5b1ab87817 100644 --- a/libs/tools/generator/extensions/navigation/src/generator-navigation-evaluator.ts +++ b/libs/tools/generator/extensions/navigation/src/generator-navigation-evaluator.ts @@ -8,9 +8,10 @@ import { GeneratorNavigationPolicy } from "./generator-navigation-policy"; /** Enforces policy for generator navigation options. */ -export class GeneratorNavigationEvaluator - implements PolicyEvaluator<GeneratorNavigationPolicy, GeneratorNavigation> -{ +export class GeneratorNavigationEvaluator implements PolicyEvaluator< + GeneratorNavigationPolicy, + GeneratorNavigation +> { /** Instantiates the evaluator. * @param policy The policy applied by the evaluator. When this conflicts with * the defaults, the policy takes precedence. From ac1f1df4f70f3292f93a30a828ec6e435da57ee8 Mon Sep 17 00:00:00 2001 From: Leslie Xiong <lxiong@livefront.com> Date: Mon, 15 Dec 2025 12:49:56 -0500 Subject: [PATCH 22/56] more prettier --- .../generator/core/src/engine/rpc/create-forwarding-address.ts | 3 +-- libs/tools/generator/core/src/engine/rpc/get-account-id.ts | 3 +-- libs/tools/generator/core/src/strategies/options-classifier.ts | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/libs/tools/generator/core/src/engine/rpc/create-forwarding-address.ts b/libs/tools/generator/core/src/engine/rpc/create-forwarding-address.ts index 4d4a5ba2ded..3321d48b345 100644 --- a/libs/tools/generator/core/src/engine/rpc/create-forwarding-address.ts +++ b/libs/tools/generator/core/src/engine/rpc/create-forwarding-address.ts @@ -7,8 +7,7 @@ import { ForwarderContext } from "../forwarder-context"; export class CreateForwardingAddressRpc< Settings extends ApiSettings, Req extends IntegrationRequest = IntegrationRequest, -> implements JsonRpc<Req, string> -{ +> implements JsonRpc<Req, string> { constructor( readonly requestor: ForwarderConfiguration<Settings>, readonly context: ForwarderContext<Settings>, diff --git a/libs/tools/generator/core/src/engine/rpc/get-account-id.ts b/libs/tools/generator/core/src/engine/rpc/get-account-id.ts index 751220fc216..19550967835 100644 --- a/libs/tools/generator/core/src/engine/rpc/get-account-id.ts +++ b/libs/tools/generator/core/src/engine/rpc/get-account-id.ts @@ -9,8 +9,7 @@ import { ForwarderContext } from "../forwarder-context"; export class GetAccountIdRpc< Settings extends ApiSettings, Req extends IntegrationRequest = IntegrationRequest, -> implements JsonRpc<Req, string> -{ +> implements JsonRpc<Req, string> { constructor( readonly requestor: ForwarderConfiguration<Settings>, readonly context: ForwarderContext<Settings>, diff --git a/libs/tools/generator/core/src/strategies/options-classifier.ts b/libs/tools/generator/core/src/strategies/options-classifier.ts index 672b5e5cc65..9ed98d71c96 100644 --- a/libs/tools/generator/core/src/strategies/options-classifier.ts +++ b/libs/tools/generator/core/src/strategies/options-classifier.ts @@ -10,8 +10,7 @@ import { Classifier } from "@bitwarden/common/tools/state/classifier"; export class OptionsClassifier< Settings, Options extends IntegrationRequest & Settings = IntegrationRequest & Settings, -> implements Classifier<Options, Record<string, never>, Settings> -{ +> implements Classifier<Options, Record<string, never>, Settings> { /** Partitions `secret` into its disclosed properties and secret properties. * @param value The object to partition * @returns an object that classifies secrets. From 9383a788ed0c1e9025220f7ecdbdb797d5260e8e Mon Sep 17 00:00:00 2001 From: Leslie Xiong <lxiong@livefront.com> Date: Mon, 15 Dec 2025 12:51:00 -0500 Subject: [PATCH 23/56] moved providers from 'app-routing' to components --- apps/desktop/src/app/app-routing.module.ts | 20 ------------------- .../app/layout/desktop-layout.component.ts | 14 +++++++++++++ .../vault-filter/vault-filter.component.ts | 9 +++++++++ 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/apps/desktop/src/app/app-routing.module.ts b/apps/desktop/src/app/app-routing.module.ts index c53a3d32178..6077afa8c12 100644 --- a/apps/desktop/src/app/app-routing.module.ts +++ b/apps/desktop/src/app/app-routing.module.ts @@ -41,26 +41,18 @@ import { NewDeviceVerificationComponent, } from "@bitwarden/auth/angular"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; import { AnonLayoutWrapperComponent, AnonLayoutWrapperData } from "@bitwarden/components"; import { LockComponent, ConfirmKeyConnectorDomainComponent, RemovePasswordComponent, } from "@bitwarden/key-management-ui"; -import { - VaultFilterServiceAbstraction, - VaultFilterService, - RoutedVaultFilterBridgeService, - RoutedVaultFilterService, -} from "@bitwarden/vault"; import { maxAccountsGuardFn } from "../auth/guards/max-accounts.guard"; import { reactiveUnlockVaultGuard } from "../autofill/guards/reactive-vault-guard"; import { Fido2CreateComponent } from "../autofill/modal/credentials/fido2-create.component"; import { Fido2ExcludedCiphersComponent } from "../autofill/modal/credentials/fido2-excluded-ciphers.component"; import { Fido2VaultComponent } from "../autofill/modal/credentials/fido2-vault.component"; -import { DesktopPremiumUpgradePromptService } from "../services/desktop-premium-upgrade-prompt.service"; import { VaultV2Component } from "../vault/app/vault/vault-v2.component"; import { VaultComponent } from "../vault/app/vault-v3/vault.component"; @@ -361,18 +353,6 @@ const routes: Routes = [ path: "", component: DesktopLayoutComponent, canActivate: [authGuard], - providers: [ - RoutedVaultFilterService, - RoutedVaultFilterBridgeService, - { - provide: VaultFilterServiceAbstraction, - useClass: VaultFilterService, - }, - { - provide: PremiumUpgradePromptService, - useClass: DesktopPremiumUpgradePromptService, - }, - ], children: [ { path: "new-vault", diff --git a/apps/desktop/src/app/layout/desktop-layout.component.ts b/apps/desktop/src/app/layout/desktop-layout.component.ts index 9d94a21007a..29b29a31441 100644 --- a/apps/desktop/src/app/layout/desktop-layout.component.ts +++ b/apps/desktop/src/app/layout/desktop-layout.component.ts @@ -4,6 +4,12 @@ import { RouterModule } from "@angular/router"; import { PasswordManagerLogo } from "@bitwarden/assets/svg"; import { LayoutComponent, NavigationModule } from "@bitwarden/components"; import { I18nPipe } from "@bitwarden/ui-common"; +import { + RoutedVaultFilterBridgeService, + VaultFilterServiceAbstraction, + VaultFilterService, + RoutedVaultFilterService, +} from "@bitwarden/vault"; import { VaultFilterComponent } from "../../vault/app/vault-v3/vault-filter/vault-filter.component"; import { SendFiltersNavComponent } from "../tools/send-v2/send-filters-nav.component"; @@ -23,6 +29,14 @@ import { DesktopSideNavComponent } from "./desktop-side-nav.component"; VaultFilterComponent, SendFiltersNavComponent, ], + providers: [ + RoutedVaultFilterService, + RoutedVaultFilterBridgeService, + { + provide: VaultFilterServiceAbstraction, + useClass: VaultFilterService, + }, + ], templateUrl: "./desktop-layout.component.html", }) export class DesktopLayoutComponent { diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts index 7efd3b2f0b3..8884ec0b21c 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts @@ -9,6 +9,7 @@ import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { UserId } from "@bitwarden/common/types/guid"; import { CipherArchiveService } from "@bitwarden/common/vault/abstractions/cipher-archive.service"; 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 { I18nPipe } from "@bitwarden/ui-common"; @@ -23,6 +24,8 @@ import { AddEditFolderDialogComponent, } from "@bitwarden/vault"; +import { DesktopPremiumUpgradePromptService } from "../../../../services/desktop-premium-upgrade-prompt.service"; + import { FolderFilterComponent } from "./filters/folder-filter.component"; import { OrganizationFilterComponent } from "./filters/organization-filter.component"; import { StatusFilterComponent } from "./filters/status-filter.component"; @@ -43,6 +46,12 @@ import { TypeFilterComponent } from "./filters/type-filter.component"; TypeFilterComponent, FolderFilterComponent, ], + providers: [ + { + provide: PremiumUpgradePromptService, + useClass: DesktopPremiumUpgradePromptService, + }, + ], }) export class VaultFilterComponent implements OnInit { private routedVaultFilterBridgeService = inject(RoutedVaultFilterBridgeService); From 74347bf5317c2122343d61e22089b7c7fca88491 Mon Sep 17 00:00:00 2001 From: Leslie Xiong <lxiong@livefront.com> Date: Mon, 15 Dec 2025 13:52:44 -0500 Subject: [PATCH 24/56] - changes to use static strings --- .../filters/collection-filter.component.html | 6 +++--- .../filters/folder-filter.component.html | 16 ++++++++-------- .../filters/organization-filter.component.html | 4 ++-- .../vault-filter/vault-filter.component.html | 2 +- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.html index 735188f479c..4f2366af7da 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.html +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.html @@ -3,7 +3,7 @@ [icon]="collection().node.icon" [class.active]="isActive()" [text]="displayName()" - [variant]="'tree'" + variant="tree" [appA11yTitle]="displayName()" (click)="applyFilter($event)" > @@ -14,9 +14,9 @@ } @else { <bit-nav-item [icon]="collection().node.icon" - [class.active]="isActive()" + [forceActiveStyles]="isActive()" [text]="collection().node.name" - [variant]="'tree'" + variant="tree" [appA11yTitle]="collection().node.name" (click)="applyFilter($event)" /> diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.html index 8656ec7fe26..0c6f86df53f 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.html +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.html @@ -3,7 +3,7 @@ [icon]="folder().node.icon" [class.active]="isActive()" [text]="displayName()" - [variant]="'tree'" + variant="tree" [appA11yTitle]="displayName()" (click)="applyFilter($event)" > @@ -12,10 +12,10 @@ type="button" slot="end" class="edit-button" - [bitIconButton]="'bwi-pencil'" - [buttonType]="'nav-contrast'" + bitIconButton="bwi-pencil" + buttonType="nav-contrast" size="small" - label="'editFolder' | i18n" + [label]="'editFolder' | i18n" (click)="editFolder(folder().node)" ></button> } @@ -28,7 +28,7 @@ [icon]="folder().node.icon" [class.active]="isActive()" [text]="displayName()" - [variant]="'tree'" + variant="tree" [appA11yTitle]="displayName()" (click)="applyFilter($event)" > @@ -37,10 +37,10 @@ type="button" slot="end" class="edit-button" - [bitIconButton]="'bwi-pencil'" - [buttonType]="'nav-contrast'" + bitIconButton="bwi-pencil" + buttonType="nav-contrast" size="small" - label="'editFolder' | i18n" + [label]="'editFolder' | i18n" (click)="editFolder(folder().node)" ></button> } diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.html index 60fa048406a..8dc0bf20bef 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.html +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.html @@ -5,7 +5,7 @@ [icon]="organization.node.icon" [class.active]="organization.node.id === activeFilter()?.organizationId" [text]="organization.node.name" - [variant]="'tree'" + variant="tree" [appA11yTitle]="organization.node.name" (click)="applyFilter(organization)" > @@ -20,7 +20,7 @@ [icon]="organization.node.icon" [class.active]="organization.node.id === activeFilter()?.organizationId" [text]="organization.node.name" - [variant]="'tree'" + variant="tree" [appA11yTitle]="organization.node.name" (click)="applyFilter(organization)" /> diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.html index 2cf807cfb24..50bc380ac99 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.html +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.html @@ -20,7 +20,7 @@ [activeFilter]="activeFilter" /> @if (!hideFolders()) { - <bit-nav-group [icon]="'bwi-folder'" [text]="'folders' | i18n" [variant]="'tree'"> + <bit-nav-group icon="bwi-folder" [text]="'folders' | i18n" variant="tree"> @for (folder of (folders$ | async)?.children ?? []; track folder.node.id) { <app-folder-filter [activeFilter]="activeFilter" From b50ed1dfbf636ee7d028121e933aab5b48e8987e Mon Sep 17 00:00:00 2001 From: Leslie Xiong <lxiong@livefront.com> Date: Mon, 15 Dec 2025 13:58:02 -0500 Subject: [PATCH 25/56] changed `[class.active]` to `[forceActiveStyles]` for `bit-nav-item`s --- .../vault-filter/filters/folder-filter.component.html | 2 +- .../vault-filter/filters/organization-filter.component.html | 2 +- .../vault-filter/filters/status-filter.component.html | 4 ++-- .../vault-v3/vault-filter/filters/type-filter.component.html | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.html index 0c6f86df53f..0867c509c2b 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.html +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.html @@ -26,7 +26,7 @@ } @else { <bit-nav-item [icon]="folder().node.icon" - [class.active]="isActive()" + [forceActiveStyles]="isActive()" [text]="displayName()" variant="tree" [appA11yTitle]="displayName()" diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.html index 8dc0bf20bef..9aec09fe44a 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.html +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.html @@ -18,7 +18,7 @@ } @else { <bit-nav-item [icon]="organization.node.icon" - [class.active]="organization.node.id === activeFilter()?.organizationId" + [forceActiveStyles]="organization.node.id === activeFilter()?.organizationId" [text]="organization.node.name" variant="tree" [appA11yTitle]="organization.node.name" diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.html index 2f747c7a821..59f75c01371 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.html +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.html @@ -2,7 +2,7 @@ @if (!hideArchive()) { <bit-nav-item [icon]="archiveFilter.icon" - [class.active]="activeFilter()?.isArchived" + [forceActiveStyles]="activeFilter()?.isArchived" (click)="handleArchiveFilter($event)" [text]="archiveFilter.name | i18n" [attr.aria-pressed]="activeFilter()?.isArchived" @@ -15,7 +15,7 @@ @if (!hideTrash()) { <bit-nav-item [icon]="trashFilter.icon" - [class.active]="activeFilter()?.isDeleted" + [forceActiveStyles]="activeFilter()?.isDeleted" (click)="applyFilter('trash')" [text]="trashFilter.name | i18n" [attr.aria-pressed]="activeFilter()?.isDeleted" diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.html index fdaf74be74f..b5e8e6663f1 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.html +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.html @@ -1,7 +1,7 @@ @for (typeFilter of typeFilters$ | async; track typeFilter) { <bit-nav-item [icon]="typeFilter.node.icon" - [class.active]="activeFilter()?.cipherType === typeFilter.node.type" + [forceActiveStyles]="activeFilter()?.cipherType === typeFilter.node.type" (click)="applyFilter(typeFilter)" [text]="typeFilter.node.name" [attr.aria-pressed]="activeFilter()?.cipherType === typeFilter.node.type" From d05859242e9b0c01a42265df79211c80bc8af91b Mon Sep 17 00:00:00 2001 From: Leslie Xiong <lxiong@livefront.com> Date: Mon, 15 Dec 2025 14:02:08 -0500 Subject: [PATCH 26/56] omitted `standalone` --- .../vault-v3/vault-filter/filters/collection-filter.component.ts | 1 - .../app/vault-v3/vault-filter/filters/folder-filter.component.ts | 1 - .../vault-filter/filters/organization-filter.component.ts | 1 - .../app/vault-v3/vault-filter/filters/status-filter.component.ts | 1 - .../app/vault-v3/vault-filter/filters/type-filter.component.ts | 1 - .../vault/app/vault-v3/vault-filter/vault-filter.component.ts | 1 - 6 files changed, 6 deletions(-) diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.ts index 6a98b8c2f09..1ea3ae27252 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.ts @@ -10,7 +10,6 @@ import { VAULT_FILTER_IMPORTS } from "../shared-filter-imports"; @Component({ selector: "app-collection-filter", templateUrl: "collection-filter.component.html", - standalone: true, imports: [...VAULT_FILTER_IMPORTS], }) export class CollectionFilterComponent { diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.ts index 5efc0d079fe..f2858f0e902 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.ts @@ -11,7 +11,6 @@ import { VAULT_FILTER_IMPORTS } from "../shared-filter-imports"; @Component({ selector: "app-folder-filter", templateUrl: "folder-filter.component.html", - standalone: true, imports: [...VAULT_FILTER_IMPORTS, IconButtonModule], }) export class FolderFilterComponent { diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.ts index 2c92c35a2b5..fef9c7d1b51 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.ts @@ -16,7 +16,6 @@ import { CollectionFilterComponent } from "./collection-filter.component"; @Component({ selector: "app-organization-filter", templateUrl: "organization-filter.component.html", - standalone: true, imports: [...VAULT_FILTER_IMPORTS, CollectionFilterComponent], }) export class OrganizationFilterComponent { diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts index 22f52a69dc5..f4fce207453 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts @@ -15,7 +15,6 @@ import { VAULT_FILTER_IMPORTS } from "../shared-filter-imports"; @Component({ selector: "app-status-filter", templateUrl: "status-filter.component.html", - standalone: true, imports: [...VAULT_FILTER_IMPORTS, PremiumBadgeComponent], }) export class StatusFilterComponent { diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.ts index 56c272ccfd7..3625642bf94 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.ts @@ -12,7 +12,6 @@ import { VAULT_FILTER_IMPORTS } from "../shared-filter-imports"; @Component({ selector: "app-type-filter", templateUrl: "type-filter.component.html", - standalone: true, imports: [...VAULT_FILTER_IMPORTS], }) export class TypeFilterComponent { diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts index 8884ec0b21c..13267924f31 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts @@ -36,7 +36,6 @@ import { TypeFilterComponent } from "./filters/type-filter.component"; @Component({ selector: "app-vault-filter", templateUrl: "vault-filter.component.html", - standalone: true, imports: [ I18nPipe, NavigationModule, From ee5dfa80b9f962bcf793f8a02132f26ae4ef496a Mon Sep 17 00:00:00 2001 From: Leslie Xiong <lxiong@livefront.com> Date: Mon, 15 Dec 2025 14:26:01 -0500 Subject: [PATCH 27/56] used explicit imports --- .../vault-filter/filters/collection-filter.component.ts | 6 +++--- .../vault-filter/filters/folder-filter.component.ts | 7 +++---- .../filters/organization-filter.component.ts | 7 +++---- .../vault-filter/filters/status-filter.component.ts | 7 ++++--- .../vault-filter/filters/type-filter.component.ts | 7 ++++--- .../app/vault-v3/vault-filter/shared-filter-imports.ts | 9 --------- 6 files changed, 17 insertions(+), 26 deletions(-) delete mode 100644 apps/desktop/src/vault/app/vault-v3/vault-filter/shared-filter-imports.ts diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.ts index 1ea3ae27252..b2c8aa53e0a 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.ts @@ -1,16 +1,16 @@ import { Component, input, computed } from "@angular/core"; +import { JslibModule } from "@bitwarden/angular/jslib.module"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; +import { NavigationModule } from "@bitwarden/components"; import { VaultFilter, CollectionFilter } from "@bitwarden/vault"; -import { VAULT_FILTER_IMPORTS } from "../shared-filter-imports"; - // 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-collection-filter", templateUrl: "collection-filter.component.html", - imports: [...VAULT_FILTER_IMPORTS], + imports: [JslibModule, NavigationModule], }) export class CollectionFilterComponent { protected readonly collection = input<TreeNode<CollectionFilter>>(); diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.ts index f2858f0e902..83389629b82 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.ts @@ -1,17 +1,16 @@ import { Component, input, computed, output } from "@angular/core"; +import { JslibModule } from "@bitwarden/angular/jslib.module"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; -import { IconButtonModule } from "@bitwarden/components"; +import { IconButtonModule, NavigationModule } from "@bitwarden/components"; import { VaultFilter, FolderFilter } from "@bitwarden/vault"; -import { VAULT_FILTER_IMPORTS } from "../shared-filter-imports"; - // 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-folder-filter", templateUrl: "folder-filter.component.html", - imports: [...VAULT_FILTER_IMPORTS, IconButtonModule], + imports: [JslibModule, NavigationModule, IconButtonModule], }) export class FolderFilterComponent { protected readonly folder = input<TreeNode<FolderFilter>>(); diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.ts index fef9c7d1b51..ed7ee8dde4f 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.ts @@ -1,14 +1,13 @@ import { Component, computed, input, inject } from "@angular/core"; +import { JslibModule } from "@bitwarden/angular/jslib.module"; 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 } from "@bitwarden/components"; +import { ToastService, NavigationModule } from "@bitwarden/components"; import { OrganizationFilter, VaultFilter, CollectionFilter } from "@bitwarden/vault"; -import { VAULT_FILTER_IMPORTS } from "../shared-filter-imports"; - import { CollectionFilterComponent } from "./collection-filter.component"; // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush @@ -16,7 +15,7 @@ import { CollectionFilterComponent } from "./collection-filter.component"; @Component({ selector: "app-organization-filter", templateUrl: "organization-filter.component.html", - imports: [...VAULT_FILTER_IMPORTS, CollectionFilterComponent], + imports: [JslibModule, NavigationModule, CollectionFilterComponent], }) export class OrganizationFilterComponent { private toastService: ToastService = inject(ToastService); diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts index f4fce207453..7bda547a079 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts @@ -1,21 +1,22 @@ +import { CommonModule } from "@angular/common"; import { Component, viewChild, input, inject, computed } from "@angular/core"; import { combineLatest, firstValueFrom, map, switchMap } from "rxjs"; import { PremiumBadgeComponent } from "@bitwarden/angular/billing/components/premium-badge"; +import { JslibModule } from "@bitwarden/angular/jslib.module"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { CipherArchiveService } from "@bitwarden/common/vault/abstractions/cipher-archive.service"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; +import { NavigationModule } from "@bitwarden/components"; import { VaultFilter, CipherStatus, CipherTypeFilter } from "@bitwarden/vault"; -import { VAULT_FILTER_IMPORTS } from "../shared-filter-imports"; - // 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-status-filter", templateUrl: "status-filter.component.html", - imports: [...VAULT_FILTER_IMPORTS, PremiumBadgeComponent], + imports: [CommonModule, JslibModule, NavigationModule, PremiumBadgeComponent], }) export class StatusFilterComponent { private accountService: AccountService = inject(AccountService); diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.ts index 3625642bf94..aa3059627f6 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.ts @@ -1,18 +1,19 @@ +import { CommonModule } from "@angular/common"; import { Component, input, inject } from "@angular/core"; import { map, shareReplay } from "rxjs"; +import { JslibModule } from "@bitwarden/angular/jslib.module"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service"; +import { NavigationModule } from "@bitwarden/components"; import { VaultFilter, CipherTypeFilter } from "@bitwarden/vault"; -import { VAULT_FILTER_IMPORTS } from "../shared-filter-imports"; - // 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-type-filter", templateUrl: "type-filter.component.html", - imports: [...VAULT_FILTER_IMPORTS], + imports: [CommonModule, JslibModule, NavigationModule], }) export class TypeFilterComponent { private restrictedItemTypesService: RestrictedItemTypesService = inject( diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/shared-filter-imports.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/shared-filter-imports.ts deleted file mode 100644 index d2cd3d9cf66..00000000000 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/shared-filter-imports.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { CommonModule } from "@angular/common"; - -import { JslibModule } from "@bitwarden/angular/jslib.module"; -import { NavigationModule } from "@bitwarden/components"; - -/** - * Common imports shared across all vault filter components. - */ -export const VAULT_FILTER_IMPORTS = [CommonModule, JslibModule, NavigationModule] as const; From f2ba5e4a38ffe27f63af6f19f941210e1ef01c35 Mon Sep 17 00:00:00 2001 From: Leslie Xiong <lxiong@livefront.com> Date: Tue, 16 Dec 2025 10:49:35 -0500 Subject: [PATCH 28/56] replaced `JslibModule` imports with `A11yTitleDirective` --- .../vault-filter/filters/collection-filter.component.ts | 5 ++--- .../vault-v3/vault-filter/filters/folder-filter.component.ts | 5 ++--- .../vault-filter/filters/organization-filter.component.ts | 5 ++--- .../vault-v3/vault-filter/filters/status-filter.component.ts | 5 ++--- .../vault-v3/vault-filter/filters/type-filter.component.ts | 5 ++--- 5 files changed, 10 insertions(+), 15 deletions(-) diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.ts index b2c8aa53e0a..e23d215aef1 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.ts @@ -1,8 +1,7 @@ import { Component, input, computed } from "@angular/core"; -import { JslibModule } from "@bitwarden/angular/jslib.module"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; -import { NavigationModule } from "@bitwarden/components"; +import { NavigationModule, A11yTitleDirective } from "@bitwarden/components"; import { VaultFilter, CollectionFilter } from "@bitwarden/vault"; // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush @@ -10,7 +9,7 @@ import { VaultFilter, CollectionFilter } from "@bitwarden/vault"; @Component({ selector: "app-collection-filter", templateUrl: "collection-filter.component.html", - imports: [JslibModule, NavigationModule], + imports: [A11yTitleDirective, NavigationModule], }) export class CollectionFilterComponent { protected readonly collection = input<TreeNode<CollectionFilter>>(); diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.ts index 83389629b82..efcbaaeab72 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.ts @@ -1,8 +1,7 @@ import { Component, input, computed, output } from "@angular/core"; -import { JslibModule } from "@bitwarden/angular/jslib.module"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; -import { IconButtonModule, NavigationModule } from "@bitwarden/components"; +import { IconButtonModule, NavigationModule, A11yTitleDirective } from "@bitwarden/components"; import { VaultFilter, FolderFilter } from "@bitwarden/vault"; // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush @@ -10,7 +9,7 @@ import { VaultFilter, FolderFilter } from "@bitwarden/vault"; @Component({ selector: "app-folder-filter", templateUrl: "folder-filter.component.html", - imports: [JslibModule, NavigationModule, IconButtonModule], + imports: [A11yTitleDirective, NavigationModule, IconButtonModule], }) export class FolderFilterComponent { protected readonly folder = input<TreeNode<FolderFilter>>(); diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.ts index ed7ee8dde4f..d79e583edee 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.ts @@ -1,11 +1,10 @@ import { Component, computed, input, inject } from "@angular/core"; -import { JslibModule } from "@bitwarden/angular/jslib.module"; 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 } from "@bitwarden/components"; +import { ToastService, NavigationModule, A11yTitleDirective } from "@bitwarden/components"; import { OrganizationFilter, VaultFilter, CollectionFilter } from "@bitwarden/vault"; import { CollectionFilterComponent } from "./collection-filter.component"; @@ -15,7 +14,7 @@ import { CollectionFilterComponent } from "./collection-filter.component"; @Component({ selector: "app-organization-filter", templateUrl: "organization-filter.component.html", - imports: [JslibModule, NavigationModule, CollectionFilterComponent], + imports: [A11yTitleDirective, NavigationModule, CollectionFilterComponent], }) export class OrganizationFilterComponent { private toastService: ToastService = inject(ToastService); diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts index 7bda547a079..417c618d62a 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts @@ -3,12 +3,11 @@ import { Component, viewChild, input, inject, computed } from "@angular/core"; import { combineLatest, firstValueFrom, map, switchMap } from "rxjs"; import { PremiumBadgeComponent } from "@bitwarden/angular/billing/components/premium-badge"; -import { JslibModule } from "@bitwarden/angular/jslib.module"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { CipherArchiveService } from "@bitwarden/common/vault/abstractions/cipher-archive.service"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; -import { NavigationModule } from "@bitwarden/components"; +import { NavigationModule, A11yTitleDirective } from "@bitwarden/components"; import { VaultFilter, CipherStatus, CipherTypeFilter } from "@bitwarden/vault"; // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush @@ -16,7 +15,7 @@ import { VaultFilter, CipherStatus, CipherTypeFilter } from "@bitwarden/vault"; @Component({ selector: "app-status-filter", templateUrl: "status-filter.component.html", - imports: [CommonModule, JslibModule, NavigationModule, PremiumBadgeComponent], + imports: [CommonModule, A11yTitleDirective, NavigationModule, PremiumBadgeComponent], }) export class StatusFilterComponent { private accountService: AccountService = inject(AccountService); diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.ts index aa3059627f6..5262d617336 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.ts @@ -2,10 +2,9 @@ import { CommonModule } from "@angular/common"; import { Component, input, inject } from "@angular/core"; import { map, shareReplay } from "rxjs"; -import { JslibModule } from "@bitwarden/angular/jslib.module"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service"; -import { NavigationModule } from "@bitwarden/components"; +import { NavigationModule, A11yTitleDirective } from "@bitwarden/components"; import { VaultFilter, CipherTypeFilter } from "@bitwarden/vault"; // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush @@ -13,7 +12,7 @@ import { VaultFilter, CipherTypeFilter } from "@bitwarden/vault"; @Component({ selector: "app-type-filter", templateUrl: "type-filter.component.html", - imports: [CommonModule, JslibModule, NavigationModule], + imports: [CommonModule, A11yTitleDirective, NavigationModule], }) export class TypeFilterComponent { private restrictedItemTypesService: RestrictedItemTypesService = inject( From b5fdc95e5e81274c35f06498ddb0f394397ed679 Mon Sep 17 00:00:00 2001 From: Leslie Xiong <lxiong@livefront.com> Date: Tue, 16 Dec 2025 11:30:10 -0500 Subject: [PATCH 29/56] moved services from `DesktopLayoutComponent` to `ServicesModule` --- .../app/layout/desktop-layout.component.ts | 14 ------- .../src/app/services/services.module.ts | 40 +++++++++++++++++-- 2 files changed, 37 insertions(+), 17 deletions(-) diff --git a/apps/desktop/src/app/layout/desktop-layout.component.ts b/apps/desktop/src/app/layout/desktop-layout.component.ts index 29b29a31441..9d94a21007a 100644 --- a/apps/desktop/src/app/layout/desktop-layout.component.ts +++ b/apps/desktop/src/app/layout/desktop-layout.component.ts @@ -4,12 +4,6 @@ import { RouterModule } from "@angular/router"; import { PasswordManagerLogo } from "@bitwarden/assets/svg"; import { LayoutComponent, NavigationModule } from "@bitwarden/components"; import { I18nPipe } from "@bitwarden/ui-common"; -import { - RoutedVaultFilterBridgeService, - VaultFilterServiceAbstraction, - VaultFilterService, - RoutedVaultFilterService, -} from "@bitwarden/vault"; import { VaultFilterComponent } from "../../vault/app/vault-v3/vault-filter/vault-filter.component"; import { SendFiltersNavComponent } from "../tools/send-v2/send-filters-nav.component"; @@ -29,14 +23,6 @@ import { DesktopSideNavComponent } from "./desktop-side-nav.component"; VaultFilterComponent, SendFiltersNavComponent, ], - providers: [ - RoutedVaultFilterService, - RoutedVaultFilterBridgeService, - { - provide: VaultFilterServiceAbstraction, - useClass: VaultFilterService, - }, - ], templateUrl: "./desktop-layout.component.html", }) export class DesktopLayoutComponent { diff --git a/apps/desktop/src/app/services/services.module.ts b/apps/desktop/src/app/services/services.module.ts index 59021a556e4..85b6ef96bef 100644 --- a/apps/desktop/src/app/services/services.module.ts +++ b/apps/desktop/src/app/services/services.module.ts @@ -1,10 +1,10 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { APP_INITIALIZER, NgModule } from "@angular/core"; -import { Router } from "@angular/router"; +import { ActivatedRoute, Router } from "@angular/router"; import { Subject, merge } from "rxjs"; -import { OrganizationUserApiService } from "@bitwarden/admin-console/common"; +import { CollectionService, OrganizationUserApiService } from "@bitwarden/admin-console/common"; import { LoginApprovalDialogComponentServiceAbstraction } from "@bitwarden/angular/auth/login-approval"; import { SetInitialPasswordService } from "@bitwarden/angular/auth/password-management/set-initial-password/set-initial-password.service.abstraction"; import { SafeProvider, safeProvider } from "@bitwarden/angular/platform/utils/safe-provider"; @@ -37,6 +37,7 @@ import { } from "@bitwarden/auth/common"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; +import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService as PolicyServiceAbstraction, InternalPolicyService, @@ -102,6 +103,7 @@ import { SystemService } from "@bitwarden/common/platform/services/system.servic import { GlobalStateProvider, StateProvider } from "@bitwarden/common/platform/state"; import { SyncService } from "@bitwarden/common/platform/sync"; import { CipherService as CipherServiceAbstraction } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { DialogService, ToastService } from "@bitwarden/components"; import { GeneratorServicesModule } from "@bitwarden/generator-components"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy"; @@ -117,7 +119,14 @@ import { SessionTimeoutSettingsComponentService, } from "@bitwarden/key-management-ui"; import { SerializedMemoryStorageService } from "@bitwarden/storage-core"; -import { DefaultSshImportPromptService, SshImportPromptService } from "@bitwarden/vault"; +import { + DefaultSshImportPromptService, + SshImportPromptService, + VaultFilterServiceAbstraction, + VaultFilterService, + RoutedVaultFilterService, + RoutedVaultFilterBridgeService, +} from "@bitwarden/vault"; import { DesktopLoginApprovalDialogComponentService } from "../../auth/login/desktop-login-approval-dialog-component.service"; import { DesktopLoginComponentService } from "../../auth/login/desktop-login-component.service"; @@ -506,6 +515,31 @@ const safeProviders: SafeProvider[] = [ useClass: SessionTimeoutSettingsComponentService, deps: [I18nServiceAbstraction, SessionTimeoutTypeService, PolicyServiceAbstraction], }), + safeProvider({ + provide: VaultFilterServiceAbstraction, + useClass: VaultFilterService, + deps: [ + OrganizationService, + FolderService, + CipherServiceAbstraction, + PolicyServiceAbstraction, + I18nServiceAbstraction, + StateProvider, + CollectionService, + AccountServiceAbstraction, + ConfigService, + ], + }), + safeProvider({ + provide: RoutedVaultFilterService, + useClass: RoutedVaultFilterService, + deps: [ActivatedRoute], + }), + safeProvider({ + provide: RoutedVaultFilterBridgeService, + useClass: RoutedVaultFilterBridgeService, + deps: [Router, RoutedVaultFilterService, VaultFilterServiceAbstraction], + }), ]; @NgModule({ From 552a085d6fe3655feac6c75eda57303666cd71f4 Mon Sep 17 00:00:00 2001 From: Leslie Xiong <lxiong@livefront.com> Date: Tue, 16 Dec 2025 11:50:19 -0500 Subject: [PATCH 30/56] used `displayName()` for collection filter --- .../vault-filter/filters/collection-filter.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.html index 4f2366af7da..ccb6eca3a0f 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.html +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.html @@ -15,9 +15,9 @@ <bit-nav-item [icon]="collection().node.icon" [forceActiveStyles]="isActive()" - [text]="collection().node.name" + [text]="displayName()" variant="tree" - [appA11yTitle]="collection().node.name" + [appA11yTitle]="displayName()" (click)="applyFilter($event)" /> } From 92846c7ffd59609336240d0f8cb5792b614bf7e2 Mon Sep 17 00:00:00 2001 From: Leslie Xiong <lxiong@livefront.com> Date: Tue, 16 Dec 2025 11:51:33 -0500 Subject: [PATCH 31/56] added `required` to `premiumBadgeComponent` --- .../vault-v3/vault-filter/filters/status-filter.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts index 417c618d62a..c045c9ba20d 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts @@ -54,7 +54,7 @@ export class StatusFilterComponent { } } - private readonly premiumBadgeComponent = viewChild(PremiumBadgeComponent); + private readonly premiumBadgeComponent = viewChild.required(PremiumBadgeComponent); private userId$ = this.accountService.activeAccount$.pipe(getUserId); protected canArchive$ = this.userId$.pipe( From c25e53260ab3ba7084b77feabaa0571f7206da6f Mon Sep 17 00:00:00 2001 From: Leslie Xiong <lxiong@livefront.com> Date: Tue, 16 Dec 2025 11:55:11 -0500 Subject: [PATCH 32/56] removed uncustomizable 'hide' signals --- .../vault-filter/vault-filter.component.html | 28 +++++++------------ .../vault-filter/vault-filter.component.ts | 7 +---- 2 files changed, 11 insertions(+), 24 deletions(-) diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.html index 50bc380ac99..3eff45102e8 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.html +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.html @@ -5,30 +5,22 @@ } @else { <bit-nav-group icon="bwi-vault" [text]="'vault' | i18n" route="new-vault"> <app-organization-filter - [hide]="hideOrganizations()" [activeFilter]="activeFilter" [organizations]="organizations$ | async" [activeOrganizationDataOwnership]="activeOrganizationDataOwnershipPolicy" [activeSingleOrganizationPolicy]="activeSingleOrganizationPolicy" - [hideCollections]="hideCollections()" [collections]="collections$ | async" /> <app-type-filter [activeFilter]="activeFilter" [cipherTypes]="cipherTypes$ | async" /> - <app-status-filter - [hideTrash]="hideTrash()" - [hideArchive]="!showArchiveVaultFilter" - [activeFilter]="activeFilter" - /> - @if (!hideFolders()) { - <bit-nav-group icon="bwi-folder" [text]="'folders' | i18n" variant="tree"> - @for (folder of (folders$ | async)?.children ?? []; track folder.node.id) { - <app-folder-filter - [activeFilter]="activeFilter" - [folder]="folder" - (onEditFolder)="editFolder($event)" - /> - } - </bit-nav-group> - } + <app-status-filter [hideArchive]="!showArchiveVaultFilter" [activeFilter]="activeFilter" /> + <bit-nav-group icon="bwi-folder" [text]="'folders' | i18n" variant="tree"> + @for (folder of (folders$ | async)?.children ?? []; track folder.node.id) { + <app-folder-filter + [activeFilter]="activeFilter" + [folder]="folder" + (onEditFolder)="editFolder($event)" + /> + } + </bit-nav-group> </bit-nav-group> } diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts index 13267924f31..a0d3980d9f9 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts @@ -1,5 +1,5 @@ import { CommonModule } from "@angular/common"; -import { Component, inject, OnInit, input, output } from "@angular/core"; +import { Component, inject, OnInit, output } from "@angular/core"; import { firstValueFrom, Observable, Subject, takeUntil } from "rxjs"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; @@ -63,11 +63,6 @@ export class VaultFilterComponent implements OnInit { private componentIsDestroyed$ = new Subject<boolean>(); protected activeFilter: VaultFilter; - protected readonly hideFolders = input(false); - protected readonly hideCollections = input(false); - protected readonly hideFavorites = input(false); - protected readonly hideTrash = input(false); - protected readonly hideOrganizations = input(false); protected onFilterChange = output<VaultFilter>(); private activeUserId: UserId; From b648d989b76f31fce201173183afc9f490f9a90b Mon Sep 17 00:00:00 2001 From: Leslie Xiong <lxiong@livefront.com> Date: Tue, 16 Dec 2025 12:00:47 -0500 Subject: [PATCH 33/56] added necessary `I18nPipe` imports to 'folder' and 'status' filter components --- .../vault-v3/vault-filter/filters/folder-filter.component.ts | 3 ++- .../vault-v3/vault-filter/filters/status-filter.component.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.ts index efcbaaeab72..0f24fe7aecf 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.ts @@ -2,6 +2,7 @@ import { Component, input, computed, output } from "@angular/core"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; import { IconButtonModule, NavigationModule, A11yTitleDirective } from "@bitwarden/components"; +import { I18nPipe } from "@bitwarden/ui-common"; import { VaultFilter, FolderFilter } from "@bitwarden/vault"; // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush @@ -9,7 +10,7 @@ import { VaultFilter, FolderFilter } from "@bitwarden/vault"; @Component({ selector: "app-folder-filter", templateUrl: "folder-filter.component.html", - imports: [A11yTitleDirective, NavigationModule, IconButtonModule], + imports: [A11yTitleDirective, NavigationModule, IconButtonModule, I18nPipe], }) export class FolderFilterComponent { protected readonly folder = input<TreeNode<FolderFilter>>(); diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts index c045c9ba20d..cf79a8cad97 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts @@ -8,6 +8,7 @@ import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { CipherArchiveService } from "@bitwarden/common/vault/abstractions/cipher-archive.service"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; import { NavigationModule, A11yTitleDirective } from "@bitwarden/components"; +import { I18nPipe } from "@bitwarden/ui-common"; import { VaultFilter, CipherStatus, CipherTypeFilter } from "@bitwarden/vault"; // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush @@ -15,7 +16,7 @@ import { VaultFilter, CipherStatus, CipherTypeFilter } from "@bitwarden/vault"; @Component({ selector: "app-status-filter", templateUrl: "status-filter.component.html", - imports: [CommonModule, A11yTitleDirective, NavigationModule, PremiumBadgeComponent], + imports: [CommonModule, A11yTitleDirective, NavigationModule, PremiumBadgeComponent, I18nPipe], }) export class StatusFilterComponent { private accountService: AccountService = inject(AccountService); From e03d1bfee168a215cd6e648e3f48681d7c6303d6 Mon Sep 17 00:00:00 2001 From: Leslie Xiong <lxiong@livefront.com> Date: Tue, 16 Dec 2025 12:45:10 -0500 Subject: [PATCH 34/56] added `I18nPipe` to organization filter --- .../vault-filter/filters/organization-filter.component.html | 4 ++-- .../vault-filter/filters/organization-filter.component.ts | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.html index 9aec09fe44a..74ba38ebb8c 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.html +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.html @@ -29,8 +29,8 @@ <span class="tw-ml-auto"> <i class="bwi bwi-fw bwi-exclamation-triangle text-danger mr-auto" - attr.aria-label="{{ 'organizationIsDisabled' | i18n }}" - appA11yTitle="{{ 'organizationIsDisabled' | i18n }}" + [attr.aria-label]="'organizationIsDisabled' | i18n" + [appA11yTitle]="'organizationIsDisabled' | i18n" ></i> </span> } diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.ts index d79e583edee..d0761c3fe2d 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.ts @@ -5,6 +5,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic 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"; @@ -14,7 +15,7 @@ import { CollectionFilterComponent } from "./collection-filter.component"; @Component({ selector: "app-organization-filter", templateUrl: "organization-filter.component.html", - imports: [A11yTitleDirective, NavigationModule, CollectionFilterComponent], + imports: [A11yTitleDirective, NavigationModule, CollectionFilterComponent, I18nPipe], }) export class OrganizationFilterComponent { private toastService: ToastService = inject(ToastService); From 470b0c8f38817095ec25ed411a1b7d7e5f71cf91 Mon Sep 17 00:00:00 2001 From: Leslie Xiong <lxiong@livefront.com> Date: Tue, 16 Dec 2025 12:58:27 -0500 Subject: [PATCH 35/56] fixed 'Favorites' not applying `forceActiveStyles` --- .../vault-v3/vault-filter/filters/type-filter.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.html index b5e8e6663f1..a552ee2d719 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.html +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.html @@ -1,10 +1,10 @@ @for (typeFilter of typeFilters$ | async; track typeFilter) { <bit-nav-item [icon]="typeFilter.node.icon" - [forceActiveStyles]="activeFilter()?.cipherType === typeFilter.node.type" + [forceActiveStyles]="activeFilter()?.selectedCipherTypeNode.node.type === typeFilter.node.type" (click)="applyFilter(typeFilter)" [text]="typeFilter.node.name" - [attr.aria-pressed]="activeFilter()?.cipherType === typeFilter.node.type" + [attr.aria-pressed]="activeFilter()?.selectedCipherTypeNode.node.type === typeFilter.node.type" [appA11yTitle]="typeFilter.node.name" /> } From 9f5dbb5e43db7a78b1b771328ca5d5d0b8b4b082 Mon Sep 17 00:00:00 2001 From: Leslie Xiong <lxiong@livefront.com> Date: Tue, 16 Dec 2025 13:41:03 -0500 Subject: [PATCH 36/56] created `DesktopRoutedVaultFilterBridgeService` to handle navigation for all vault filters --- ...ktop-routed-vault-filter-bridge.service.ts | 39 +++++++++++++++++++ .../src/app/services/services.module.ts | 6 +-- .../vault-filter/vault-filter.component.ts | 4 +- .../src/vault/app/vault-v3/vault.component.ts | 4 +- 4 files changed, 46 insertions(+), 7 deletions(-) create mode 100644 apps/desktop/src/app/services/desktop-routed-vault-filter-bridge.service.ts diff --git a/apps/desktop/src/app/services/desktop-routed-vault-filter-bridge.service.ts b/apps/desktop/src/app/services/desktop-routed-vault-filter-bridge.service.ts new file mode 100644 index 00000000000..4c1a825d7d2 --- /dev/null +++ b/apps/desktop/src/app/services/desktop-routed-vault-filter-bridge.service.ts @@ -0,0 +1,39 @@ +import { Injectable } from "@angular/core"; +import { Router } from "@angular/router"; + +import { + RoutedVaultFilterService, + RoutedVaultFilterBridgeService, + RoutedVaultFilterModel, + VaultFilterServiceAbstraction as VaultFilterService, +} from "@bitwarden/vault"; + +/** + * Desktop-specific extension of RoutedVaultFilterBridgeService that ensures + * vault filter navigation always goes to the /new-vault route. + */ +@Injectable() +export class DesktopRoutedVaultFilterBridgeService extends RoutedVaultFilterBridgeService { + private static readonly VAULT_ROUTE = "/new-vault"; + private readonly desktopRouter: Router; + private readonly desktopRoutedVaultFilterService: RoutedVaultFilterService; + + constructor( + router: Router, + routedVaultFilterService: RoutedVaultFilterService, + vaultFilterService: VaultFilterService, + ) { + super(router, routedVaultFilterService, vaultFilterService); + this.desktopRouter = router; + this.desktopRoutedVaultFilterService = routedVaultFilterService; + } + + override navigate(filter: RoutedVaultFilterModel) { + const extras = this.desktopRoutedVaultFilterService.createRoute(filter)[1]; + const vaultCommands = [DesktopRoutedVaultFilterBridgeService.VAULT_ROUTE]; + + // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. + // eslint-disable-next-line @typescript-eslint/no-floating-promises + this.desktopRouter.navigate(vaultCommands, extras); + } +} diff --git a/apps/desktop/src/app/services/services.module.ts b/apps/desktop/src/app/services/services.module.ts index 85b6ef96bef..e123ea33553 100644 --- a/apps/desktop/src/app/services/services.module.ts +++ b/apps/desktop/src/app/services/services.module.ts @@ -125,7 +125,6 @@ import { VaultFilterServiceAbstraction, VaultFilterService, RoutedVaultFilterService, - RoutedVaultFilterBridgeService, } from "@bitwarden/vault"; import { DesktopLoginApprovalDialogComponentService } from "../../auth/login/desktop-login-approval-dialog-component.service"; @@ -161,6 +160,7 @@ import { NativeMessagingService } from "../../services/native-messaging.service" import { SearchBarService } from "../layout/search/search-bar.service"; import { DesktopFileDownloadService } from "./desktop-file-download.service"; +import { DesktopRoutedVaultFilterBridgeService } from "./desktop-routed-vault-filter-bridge.service"; import { InitService } from "./init.service"; import { NativeMessagingManifestService } from "./native-messaging-manifest.service"; import { DesktopSetInitialPasswordService } from "./set-initial-password/desktop-set-initial-password.service"; @@ -536,8 +536,8 @@ const safeProviders: SafeProvider[] = [ deps: [ActivatedRoute], }), safeProvider({ - provide: RoutedVaultFilterBridgeService, - useClass: RoutedVaultFilterBridgeService, + provide: DesktopRoutedVaultFilterBridgeService, + useClass: DesktopRoutedVaultFilterBridgeService, deps: [Router, RoutedVaultFilterService, VaultFilterServiceAbstraction], }), ]; diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts index a0d3980d9f9..96c50b23582 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts @@ -20,10 +20,10 @@ import { FolderFilter, VaultFilter, VaultFilterServiceAbstraction as VaultFilterService, - RoutedVaultFilterBridgeService, AddEditFolderDialogComponent, } from "@bitwarden/vault"; +import { DesktopRoutedVaultFilterBridgeService } from "../../../../app/services/desktop-routed-vault-filter-bridge.service"; import { DesktopPremiumUpgradePromptService } from "../../../../services/desktop-premium-upgrade-prompt.service"; import { FolderFilterComponent } from "./filters/folder-filter.component"; @@ -53,7 +53,7 @@ import { TypeFilterComponent } from "./filters/type-filter.component"; ], }) export class VaultFilterComponent implements OnInit { - private routedVaultFilterBridgeService = inject(RoutedVaultFilterBridgeService); + private routedVaultFilterBridgeService = inject(DesktopRoutedVaultFilterBridgeService); private vaultFilterService: VaultFilterService = inject(VaultFilterService); private accountService: AccountService = inject(AccountService); private cipherArchiveService: CipherArchiveService = inject(CipherArchiveService); diff --git a/apps/desktop/src/vault/app/vault-v3/vault.component.ts b/apps/desktop/src/vault/app/vault-v3/vault.component.ts index d92c3080b07..8eabb52aba1 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault.component.ts @@ -82,11 +82,11 @@ import { CipherFormComponent, ArchiveCipherUtilitiesService, VaultFilter, - RoutedVaultFilterBridgeService, VaultFilterServiceAbstraction as VaultFilterService, } from "@bitwarden/vault"; import { SearchBarService } from "../../../app/layout/search/search-bar.service"; +import { DesktopRoutedVaultFilterBridgeService } from "../../../app/services/desktop-routed-vault-filter-bridge.service"; import { DesktopCredentialGenerationService } from "../../../services/desktop-cipher-form-generator.service"; import { DesktopPremiumUpgradePromptService } from "../../../services/desktop-premium-upgrade-prompt.service"; import { invokeMenu, RendererMenuItem } from "../../../utils"; @@ -221,7 +221,7 @@ export class VaultComponent implements OnInit, OnDestroy, CopyClickListener { private cipherArchiveService: CipherArchiveService, private policyService: PolicyService, private archiveCipherUtilitiesService: ArchiveCipherUtilitiesService, - private routedVaultFilterBridgeService: RoutedVaultFilterBridgeService, + private routedVaultFilterBridgeService: DesktopRoutedVaultFilterBridgeService, private vaultFilterService: VaultFilterService, ) {} From 383cb0663c027b5502fed2e2917717d2dcfb0dfa Mon Sep 17 00:00:00 2001 From: Leslie Xiong <lxiong@livefront.com> Date: Tue, 16 Dec 2025 14:28:18 -0500 Subject: [PATCH 37/56] moved 'collections' outside of 'organizations' --- .../organization-filter.component.html | 33 ++++------------- .../filters/organization-filter.component.ts | 37 ++----------------- .../vault-filter/vault-filter.component.html | 28 +++++++++++--- .../vault-filter/vault-filter.component.ts | 15 ++++++-- 4 files changed, 45 insertions(+), 68 deletions(-) diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.html index 74ba38ebb8c..5de23af8e4e 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.html +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.html @@ -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 diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.ts index d0761c3fe2d..fb88db007f4 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.ts @@ -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; - } } diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.html index 3eff45102e8..a782fe6c30e 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.html +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.html @@ -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)" /> diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts index 96c50b23582..3cb75e26a98 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts @@ -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; From d9da68de40c3175236bfc0d40f1b47fa539517dc Mon Sep 17 00:00:00 2001 From: Leslie Xiong <lxiong@livefront.com> Date: Tue, 16 Dec 2025 14:40:23 -0500 Subject: [PATCH 38/56] fixed broken imports --- .../organizations/exposed-passwords-report.component.ts | 9 ++++++--- .../organizations/reused-passwords-report.component.ts | 9 ++++++--- .../organizations/unsecured-websites-report.component.ts | 9 ++++++--- .../organizations/weak-passwords-report.component.ts | 9 ++++++--- .../vault-item-dialog.component.spec.ts | 3 +-- .../vault-item-dialog/vault-item-dialog.component.ts | 4 ++-- .../admin-console-cipher-form-config.service.spec.ts | 3 +-- .../services/admin-console-cipher-form-config.service.ts | 9 ++++++--- 8 files changed, 34 insertions(+), 21 deletions(-) diff --git a/apps/web/src/app/dirt/reports/pages/organizations/exposed-passwords-report.component.ts b/apps/web/src/app/dirt/reports/pages/organizations/exposed-passwords-report.component.ts index f83614557bd..1d3d8d71f5a 100644 --- a/apps/web/src/app/dirt/reports/pages/organizations/exposed-passwords-report.component.ts +++ b/apps/web/src/app/dirt/reports/pages/organizations/exposed-passwords-report.component.ts @@ -17,14 +17,17 @@ import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.serv import { Cipher } from "@bitwarden/common/vault/models/domain/cipher"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { DialogService } from "@bitwarden/components"; -import { PasswordRepromptService, CipherFormConfigService } from "@bitwarden/vault"; +import { + PasswordRepromptService, + CipherFormConfigService, + RoutedVaultFilterBridgeService, + RoutedVaultFilterService, +} from "@bitwarden/vault"; import { HeaderModule } from "../../../../layouts/header/header.module"; import { SharedModule } from "../../../../shared"; import { OrganizationBadgeModule } from "../../../../vault/individual-vault/organization-badge/organization-badge.module"; import { PipesModule } from "../../../../vault/individual-vault/pipes/pipes.module"; -import { RoutedVaultFilterBridgeService } from "../../../../vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service"; -import { RoutedVaultFilterService } from "../../../../vault/individual-vault/vault-filter/services/routed-vault-filter.service"; import { AdminConsoleCipherFormConfigService } from "../../../../vault/org-vault/services/admin-console-cipher-form-config.service"; import { ExposedPasswordsReportComponent as BaseExposedPasswordsReportComponent } from "../exposed-passwords-report.component"; diff --git a/apps/web/src/app/dirt/reports/pages/organizations/reused-passwords-report.component.ts b/apps/web/src/app/dirt/reports/pages/organizations/reused-passwords-report.component.ts index 3944e2edfcb..599774d5515 100644 --- a/apps/web/src/app/dirt/reports/pages/organizations/reused-passwords-report.component.ts +++ b/apps/web/src/app/dirt/reports/pages/organizations/reused-passwords-report.component.ts @@ -16,14 +16,17 @@ import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.serv import { Cipher } from "@bitwarden/common/vault/models/domain/cipher"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { DialogService } from "@bitwarden/components"; -import { CipherFormConfigService, PasswordRepromptService } from "@bitwarden/vault"; +import { + CipherFormConfigService, + PasswordRepromptService, + RoutedVaultFilterBridgeService, + RoutedVaultFilterService, +} from "@bitwarden/vault"; import { HeaderModule } from "../../../../layouts/header/header.module"; import { SharedModule } from "../../../../shared"; import { OrganizationBadgeModule } from "../../../../vault/individual-vault/organization-badge/organization-badge.module"; import { PipesModule } from "../../../../vault/individual-vault/pipes/pipes.module"; -import { RoutedVaultFilterBridgeService } from "../../../../vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service"; -import { RoutedVaultFilterService } from "../../../../vault/individual-vault/vault-filter/services/routed-vault-filter.service"; import { AdminConsoleCipherFormConfigService } from "../../../../vault/org-vault/services/admin-console-cipher-form-config.service"; import { ReusedPasswordsReportComponent as BaseReusedPasswordsReportComponent } from "../reused-passwords-report.component"; diff --git a/apps/web/src/app/dirt/reports/pages/organizations/unsecured-websites-report.component.ts b/apps/web/src/app/dirt/reports/pages/organizations/unsecured-websites-report.component.ts index d49baa5d465..6bf741b86eb 100644 --- a/apps/web/src/app/dirt/reports/pages/organizations/unsecured-websites-report.component.ts +++ b/apps/web/src/app/dirt/reports/pages/organizations/unsecured-websites-report.component.ts @@ -16,14 +16,17 @@ import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.serv import { Cipher } from "@bitwarden/common/vault/models/domain/cipher"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { DialogService } from "@bitwarden/components"; -import { CipherFormConfigService, PasswordRepromptService } from "@bitwarden/vault"; +import { + CipherFormConfigService, + PasswordRepromptService, + RoutedVaultFilterBridgeService, + RoutedVaultFilterService, +} from "@bitwarden/vault"; import { HeaderModule } from "../../../../layouts/header/header.module"; import { SharedModule } from "../../../../shared"; import { OrganizationBadgeModule } from "../../../../vault/individual-vault/organization-badge/organization-badge.module"; import { PipesModule } from "../../../../vault/individual-vault/pipes/pipes.module"; -import { RoutedVaultFilterBridgeService } from "../../../../vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service"; -import { RoutedVaultFilterService } from "../../../../vault/individual-vault/vault-filter/services/routed-vault-filter.service"; import { AdminConsoleCipherFormConfigService } from "../../../../vault/org-vault/services/admin-console-cipher-form-config.service"; import { UnsecuredWebsitesReportComponent as BaseUnsecuredWebsitesReportComponent } from "../unsecured-websites-report.component"; diff --git a/apps/web/src/app/dirt/reports/pages/organizations/weak-passwords-report.component.ts b/apps/web/src/app/dirt/reports/pages/organizations/weak-passwords-report.component.ts index 5158416dd28..6780b65931c 100644 --- a/apps/web/src/app/dirt/reports/pages/organizations/weak-passwords-report.component.ts +++ b/apps/web/src/app/dirt/reports/pages/organizations/weak-passwords-report.component.ts @@ -17,14 +17,17 @@ import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.serv import { Cipher } from "@bitwarden/common/vault/models/domain/cipher"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { DialogService } from "@bitwarden/components"; -import { CipherFormConfigService, PasswordRepromptService } from "@bitwarden/vault"; +import { + CipherFormConfigService, + PasswordRepromptService, + RoutedVaultFilterBridgeService, + RoutedVaultFilterService, +} from "@bitwarden/vault"; import { HeaderModule } from "../../../../layouts/header/header.module"; import { SharedModule } from "../../../../shared"; import { OrganizationBadgeModule } from "../../../../vault/individual-vault/organization-badge/organization-badge.module"; import { PipesModule } from "../../../../vault/individual-vault/pipes/pipes.module"; -import { RoutedVaultFilterBridgeService } from "../../../../vault/individual-vault/vault-filter/services/routed-vault-filter-bridge.service"; -import { RoutedVaultFilterService } from "../../../../vault/individual-vault/vault-filter/services/routed-vault-filter.service"; import { AdminConsoleCipherFormConfigService } from "../../../../vault/org-vault/services/admin-console-cipher-form-config.service"; import { WeakPasswordsReportComponent as BaseWeakPasswordsReportComponent } from "../weak-passwords-report.component"; diff --git a/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.spec.ts b/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.spec.ts index 6716cde629a..57b2d7a5fad 100644 --- a/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.spec.ts +++ b/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.spec.ts @@ -17,8 +17,7 @@ import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.serv import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service"; import { DialogRef, DIALOG_DATA, DialogService, ToastService } from "@bitwarden/components"; - -import { RoutedVaultFilterService } from "../../individual-vault/vault-filter/services/routed-vault-filter.service"; +import { RoutedVaultFilterService } from "@bitwarden/vault"; import { VaultItemDialogComponent } from "./vault-item-dialog.component"; diff --git a/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts b/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts index 8508596a67b..1a2721800e8 100644 --- a/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts +++ b/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts @@ -62,11 +62,11 @@ import { CipherViewComponent, DecryptionFailureDialogComponent, DefaultChangeLoginPasswordService, + RoutedVaultFilterService, + RoutedVaultFilterModel, } from "@bitwarden/vault"; import { SharedModule } from "../../../shared/shared.module"; -import { RoutedVaultFilterService } from "../../individual-vault/vault-filter/services/routed-vault-filter.service"; -import { RoutedVaultFilterModel } from "../../individual-vault/vault-filter/shared/models/routed-vault-filter.model"; import { WebCipherFormGenerationService } from "../../services/web-cipher-form-generation.service"; import { WebVaultPremiumUpgradePromptService } from "../../services/web-premium-upgrade-prompt.service"; diff --git a/apps/web/src/app/vault/org-vault/services/admin-console-cipher-form-config.service.spec.ts b/apps/web/src/app/vault/org-vault/services/admin-console-cipher-form-config.service.spec.ts index c6a7c9c830d..b365e06e6f8 100644 --- a/apps/web/src/app/vault/org-vault/services/admin-console-cipher-form-config.service.spec.ts +++ b/apps/web/src/app/vault/org-vault/services/admin-console-cipher-form-config.service.spec.ts @@ -11,8 +11,7 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv import { mockAccountServiceWith } from "@bitwarden/common/spec"; import { CipherId, UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; - -import { RoutedVaultFilterService } from "../../individual-vault/vault-filter/services/routed-vault-filter.service"; +import { RoutedVaultFilterService } from "@bitwarden/vault"; import { AdminConsoleCipherFormConfigService } from "./admin-console-cipher-form-config.service"; diff --git a/apps/web/src/app/vault/org-vault/services/admin-console-cipher-form-config.service.ts b/apps/web/src/app/vault/org-vault/services/admin-console-cipher-form-config.service.ts index 939729568e9..01a2f23f4e1 100644 --- a/apps/web/src/app/vault/org-vault/services/admin-console-cipher-form-config.service.ts +++ b/apps/web/src/app/vault/org-vault/services/admin-console-cipher-form-config.service.ts @@ -16,9 +16,12 @@ import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.servi import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherData } from "@bitwarden/common/vault/models/data/cipher.data"; import { Cipher } from "@bitwarden/common/vault/models/domain/cipher"; -import { CipherFormConfig, CipherFormConfigService, CipherFormMode } from "@bitwarden/vault"; - -import { RoutedVaultFilterService } from "../../individual-vault/vault-filter/services/routed-vault-filter.service"; +import { + CipherFormConfig, + CipherFormConfigService, + CipherFormMode, + RoutedVaultFilterService, +} from "@bitwarden/vault"; /** Admin Console implementation of the `CipherFormConfigService`. */ @Injectable() From ef74e8c3ef63b4d5d51d64d79f03bf9a78a9ec1e Mon Sep 17 00:00:00 2001 From: Leslie Xiong <lxiong@livefront.com> Date: Tue, 16 Dec 2025 15:15:19 -0500 Subject: [PATCH 39/56] cleaned up unnecessary `hideTrash` --- .../filters/status-filter.component.html | 42 +++++++++---------- .../filters/status-filter.component.ts | 7 +--- 2 files changed, 20 insertions(+), 29 deletions(-) diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.html index 59f75c01371..aef9a4d41b4 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.html +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.html @@ -1,25 +1,21 @@ -@if (show()) { - @if (!hideArchive()) { - <bit-nav-item - [icon]="archiveFilter.icon" - [forceActiveStyles]="activeFilter()?.isArchived" - (click)="handleArchiveFilter($event)" - [text]="archiveFilter.name | i18n" - [attr.aria-pressed]="activeFilter()?.isArchived" - [appA11yTitle]="archiveFilter.name | i18n" - /> - @if (!(canArchive$ | async)) { - <app-premium-badge /> - } - } - @if (!hideTrash()) { - <bit-nav-item - [icon]="trashFilter.icon" - [forceActiveStyles]="activeFilter()?.isDeleted" - (click)="applyFilter('trash')" - [text]="trashFilter.name | i18n" - [attr.aria-pressed]="activeFilter()?.isDeleted" - [appA11yTitle]="trashFilter.name | i18n" - /> +@if (!hideArchive()) { + <bit-nav-item + [icon]="archiveFilter.icon" + [forceActiveStyles]="activeFilter()?.isArchived" + (click)="handleArchiveFilter($event)" + [text]="archiveFilter.name | i18n" + [attr.aria-pressed]="activeFilter()?.isArchived" + [appA11yTitle]="archiveFilter.name | i18n" + /> + @if (!(canArchive$ | async)) { + <app-premium-badge /> } } +<bit-nav-item + [icon]="trashFilter.icon" + [forceActiveStyles]="activeFilter()?.isDeleted" + (click)="applyFilter('trash')" + [text]="trashFilter.name | i18n" + [attr.aria-pressed]="activeFilter()?.isDeleted" + [appA11yTitle]="trashFilter.name | i18n" +/> diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts index cf79a8cad97..543bd550036 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts @@ -1,5 +1,5 @@ import { CommonModule } from "@angular/common"; -import { Component, viewChild, input, inject, computed } from "@angular/core"; +import { Component, viewChild, input, inject } from "@angular/core"; import { combineLatest, firstValueFrom, map, switchMap } from "rxjs"; import { PremiumBadgeComponent } from "@bitwarden/angular/billing/components/premium-badge"; @@ -22,7 +22,6 @@ export class StatusFilterComponent { private accountService: AccountService = inject(AccountService); private cipherArchiveService: CipherArchiveService = inject(CipherArchiveService); - protected readonly hideTrash = input(false); protected readonly hideArchive = input(false); protected readonly activeFilter = input<VaultFilter>(); protected readonly archiveFilter: CipherTypeFilter = { @@ -38,10 +37,6 @@ export class StatusFilterComponent { icon: "bwi-trash", }; - protected readonly show = computed(() => { - return !(this.hideTrash() && this.hideArchive()); - }); - protected applyFilter(filterType: CipherStatus) { let filter: CipherTypeFilter = null; if (filterType === "archive") { From 1fbb480f7dd4b562ba8015ceeda4fb4cc3e31264 Mon Sep 17 00:00:00 2001 From: Leslie Xiong <lxiong@livefront.com> Date: Tue, 16 Dec 2025 15:57:42 -0500 Subject: [PATCH 40/56] added 'All items' filter --- .../vault-filter/filters/type-filter.component.html | 10 +++++++++- .../vault-filter/filters/type-filter.component.ts | 13 +++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.html index a552ee2d719..309b953acb5 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.html +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.html @@ -1,8 +1,16 @@ +<bit-nav-item + icon="bwi-filter" + [forceActiveStyles]="activeFilter()?.selectedCipherTypeNode?.node?.id === 'AllItems'" + (click)="applyAllItemsFilter()" + [text]="'allItems' | i18n" + [attr.aria-pressed]="activeFilter()?.selectedCipherTypeNode?.node?.id === 'AllItems'" + [appA11yTitle]="'allItems' | i18n" +/> @for (typeFilter of typeFilters$ | async; track typeFilter) { <bit-nav-item [icon]="typeFilter.node.icon" [forceActiveStyles]="activeFilter()?.selectedCipherTypeNode.node.type === typeFilter.node.type" - (click)="applyFilter(typeFilter)" + (click)="applyTypeFilter(typeFilter)" [text]="typeFilter.node.name" [attr.aria-pressed]="activeFilter()?.selectedCipherTypeNode.node.type === typeFilter.node.type" [appA11yTitle]="typeFilter.node.name" diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.ts index 5262d617336..9060d78bbba 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.ts @@ -5,6 +5,7 @@ import { map, shareReplay } from "rxjs"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service"; import { NavigationModule, A11yTitleDirective } from "@bitwarden/components"; +import { I18nPipe } from "@bitwarden/ui-common"; import { VaultFilter, CipherTypeFilter } from "@bitwarden/vault"; // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush @@ -12,7 +13,7 @@ import { VaultFilter, CipherTypeFilter } from "@bitwarden/vault"; @Component({ selector: "app-type-filter", templateUrl: "type-filter.component.html", - imports: [CommonModule, A11yTitleDirective, NavigationModule], + imports: [CommonModule, A11yTitleDirective, NavigationModule, I18nPipe], }) export class TypeFilterComponent { private restrictedItemTypesService: RestrictedItemTypesService = inject( @@ -22,7 +23,7 @@ export class TypeFilterComponent { protected readonly cipherTypes = input<TreeNode<CipherTypeFilter>>(); protected readonly activeFilter = input<VaultFilter>(); - protected applyFilter(cipherType: TreeNode<CipherTypeFilter>) { + protected applyTypeFilter(cipherType: TreeNode<CipherTypeFilter>) { const filter = this.activeFilter(); if (filter) { @@ -30,6 +31,14 @@ export class TypeFilterComponent { } } + protected applyAllItemsFilter() { + const filter = this.activeFilter(); + + if (filter) { + filter.selectedCipherTypeNode = this.cipherTypes(); + } + } + protected typeFilters$ = this.restrictedItemTypesService.restricted$.pipe( map((restrictedItemTypes) => // Filter out restricted item types from the typeFilters array From 742bcaa535e023da0aae85c9b1b3e649d2225bb9 Mon Sep 17 00:00:00 2001 From: Leslie Xiong <lxiong@livefront.com> Date: Thu, 18 Dec 2025 09:45:58 -0500 Subject: [PATCH 41/56] fixed import --- .../collections/vault-header/vault-header.component.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/apps/web/src/app/admin-console/organizations/collections/vault-header/vault-header.component.ts b/apps/web/src/app/admin-console/organizations/collections/vault-header/vault-header.component.ts index 30582063ab2..20b31488649 100644 --- a/apps/web/src/app/admin-console/organizations/collections/vault-header/vault-header.component.ts +++ b/apps/web/src/app/admin-console/organizations/collections/vault-header/vault-header.component.ts @@ -27,14 +27,10 @@ import { SearchModule, SimpleDialogOptions, } from "@bitwarden/components"; -import { NewCipherMenuComponent } from "@bitwarden/vault"; +import { NewCipherMenuComponent, All, RoutedVaultFilterModel } from "@bitwarden/vault"; import { HeaderModule } from "../../../../layouts/header/header.module"; import { SharedModule } from "../../../../shared"; -import { - All, - RoutedVaultFilterModel, -} from "../../../../vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model"; import { CollectionDialogTabType } from "../../shared/components/collection-dialog"; // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush From 75c7e9c09714864b9045f93673e9fe4345c355d6 Mon Sep 17 00:00:00 2001 From: Leslie Xiong <lxiong@livefront.com> Date: Thu, 18 Dec 2025 15:59:35 -0500 Subject: [PATCH 42/56] added `void` --- .../services/desktop-routed-vault-filter-bridge.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/desktop/src/app/services/desktop-routed-vault-filter-bridge.service.ts b/apps/desktop/src/app/services/desktop-routed-vault-filter-bridge.service.ts index 4c1a825d7d2..e7b216b7503 100644 --- a/apps/desktop/src/app/services/desktop-routed-vault-filter-bridge.service.ts +++ b/apps/desktop/src/app/services/desktop-routed-vault-filter-bridge.service.ts @@ -33,7 +33,7 @@ export class DesktopRoutedVaultFilterBridgeService extends RoutedVaultFilterBrid const vaultCommands = [DesktopRoutedVaultFilterBridgeService.VAULT_ROUTE]; // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.desktopRouter.navigate(vaultCommands, extras); + + void this.desktopRouter.navigate(vaultCommands, extras); } } From 50d021c1b8fcaeff8194c5a83053527cf41957e7 Mon Sep 17 00:00:00 2001 From: Leslie Xiong <lxiong@livefront.com> Date: Thu, 18 Dec 2025 16:00:29 -0500 Subject: [PATCH 43/56] removed whitespace --- .../app/services/desktop-routed-vault-filter-bridge.service.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/desktop/src/app/services/desktop-routed-vault-filter-bridge.service.ts b/apps/desktop/src/app/services/desktop-routed-vault-filter-bridge.service.ts index e7b216b7503..2eadb6462ca 100644 --- a/apps/desktop/src/app/services/desktop-routed-vault-filter-bridge.service.ts +++ b/apps/desktop/src/app/services/desktop-routed-vault-filter-bridge.service.ts @@ -33,7 +33,6 @@ export class DesktopRoutedVaultFilterBridgeService extends RoutedVaultFilterBrid const vaultCommands = [DesktopRoutedVaultFilterBridgeService.VAULT_ROUTE]; // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - void this.desktopRouter.navigate(vaultCommands, extras); } } From d433fa22d58bdc87092423d88c9ff48d1e889ff5 Mon Sep 17 00:00:00 2001 From: Leslie Xiong <lxiong@livefront.com> Date: Fri, 19 Dec 2025 12:12:51 -0500 Subject: [PATCH 44/56] - created 'All vaults' group nav item to reset 'My vault' and 'Organization's - changed 'All items' to a group nav item - fixed issue where 'My vault' items were disappearing on page reload --- .../organization-filter.component.html | 45 +++++++++++-------- .../filters/organization-filter.component.ts | 12 ++++- .../filters/type-filter.component.html | 34 ++++++++------ .../filters/type-filter.component.ts | 5 ++- .../vault-filter/vault-filter.component.ts | 1 - .../src/vault/app/vault-v3/vault.component.ts | 3 +- 6 files changed, 62 insertions(+), 38 deletions(-) diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.html index 5de23af8e4e..79f89a39260 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.html +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.html @@ -1,21 +1,30 @@ @if (show()) { - @for (organization of organizations().children ?? []; track organization.node.id) { - <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 - class="bwi bwi-fw bwi-exclamation-triangle text-danger mr-auto" - [attr.aria-label]="'organizationIsDisabled' | i18n" - [appA11yTitle]="'organizationIsDisabled' | i18n" - ></i> - </span> + <bit-nav-group + icon="bwi-filter" + (click)="applyAllVaultsFilter()" + [text]="'allVaults' | i18n" + [attr.aria-pressed]="activeFilter()?.organizationId === null" + [appA11yTitle]="'allVaults' | i18n" + variant="tree" + [open]="true" + > + @for (organization of organizations().children ?? []; track organization.node.id) { + <bit-nav-item + [icon]="organization.node.icon" + [forceActiveStyles]="organization.node.id === activeFilter()?.organizationId" + [text]="organization.node.name" + [appA11yTitle]="organization.node.name" + (click)="applyFilter($event, organization)" + /> + @if (!organization.node.enabled) { + <span class="tw-ml-auto"> + <i + class="bwi bwi-fw bwi-exclamation-triangle text-danger mr-auto" + [attr.aria-label]="'organizationIsDisabled' | i18n" + [appA11yTitle]="'organizationIsDisabled' | i18n" + ></i> + </span> + } } - } + </bit-nav-group> } diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.ts index fb88db007f4..fa91816577a 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.ts @@ -51,7 +51,8 @@ export class OrganizationFilterComponent { return displayMode; }); - protected applyFilter(organization: TreeNode<OrganizationFilter>) { + protected applyFilter(event: Event, organization: TreeNode<OrganizationFilter>) { + event.stopPropagation(); if (!organization.node.enabled) { this.toastService.showToast({ variant: "error", @@ -68,4 +69,13 @@ export class OrganizationFilterComponent { filter.selectedOrganizationNode = organization; } } + + protected applyAllVaultsFilter() { + this.vaultFilterService.clearOrganizationFilter(); + const filter = this.activeFilter(); + + if (filter) { + filter.selectedOrganizationNode = null; + } + } } diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.html index 309b953acb5..1c9e58e0656 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.html +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.html @@ -1,18 +1,24 @@ -<bit-nav-item +<bit-nav-group icon="bwi-filter" - [forceActiveStyles]="activeFilter()?.selectedCipherTypeNode?.node?.id === 'AllItems'" - (click)="applyAllItemsFilter()" + (click)="applyAllItemsFilter($event)" [text]="'allItems' | i18n" [attr.aria-pressed]="activeFilter()?.selectedCipherTypeNode?.node?.id === 'AllItems'" [appA11yTitle]="'allItems' | i18n" -/> -@for (typeFilter of typeFilters$ | async; track typeFilter) { - <bit-nav-item - [icon]="typeFilter.node.icon" - [forceActiveStyles]="activeFilter()?.selectedCipherTypeNode.node.type === typeFilter.node.type" - (click)="applyTypeFilter(typeFilter)" - [text]="typeFilter.node.name" - [attr.aria-pressed]="activeFilter()?.selectedCipherTypeNode.node.type === typeFilter.node.type" - [appA11yTitle]="typeFilter.node.name" - /> -} + variant="tree" + [open]="true" +> + @for (typeFilter of typeFilters$ | async; track typeFilter) { + <bit-nav-item + [icon]="typeFilter.node.icon" + [forceActiveStyles]=" + activeFilter()?.selectedCipherTypeNode.node.type === typeFilter.node.type + " + (click)="applyTypeFilter($event, typeFilter)" + [text]="typeFilter.node.name" + [attr.aria-pressed]=" + activeFilter()?.selectedCipherTypeNode.node.type === typeFilter.node.type + " + [appA11yTitle]="typeFilter.node.name" + /> + } +</bit-nav-group> diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.ts index 9060d78bbba..40755b25253 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.ts @@ -23,7 +23,8 @@ export class TypeFilterComponent { protected readonly cipherTypes = input<TreeNode<CipherTypeFilter>>(); protected readonly activeFilter = input<VaultFilter>(); - protected applyTypeFilter(cipherType: TreeNode<CipherTypeFilter>) { + protected applyTypeFilter(event: Event, cipherType: TreeNode<CipherTypeFilter>) { + event.stopPropagation(); const filter = this.activeFilter(); if (filter) { @@ -31,7 +32,7 @@ export class TypeFilterComponent { } } - protected applyAllItemsFilter() { + protected applyAllItemsFilter(event: Event) { const filter = this.activeFilter(); if (filter) { diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts index 3cb75e26a98..3d6a481911d 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts @@ -111,7 +111,6 @@ export class VaultFilterComponent implements OnInit { this.cipherArchiveService.hasArchiveFlagEnabled$, ); - // Subscribe to the active filter from the bridge service this.routedVaultFilterBridgeService.activeFilter$ .pipe(takeUntil(this.componentIsDestroyed$)) .subscribe((filter) => { diff --git a/apps/desktop/src/vault/app/vault-v3/vault.component.ts b/apps/desktop/src/vault/app/vault-v3/vault.component.ts index 8eabb52aba1..81761a4a88c 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault.component.ts @@ -273,8 +273,7 @@ export class VaultComponent implements OnInit, OnDestroy, CopyClickListener { break; case "syncCompleted": if (this.vaultItemsComponent) { - const filter = this.activeFilter.buildFilter(); - await this.vaultItemsComponent.reload(filter).catch(() => {}); + await this.vaultItemsComponent.refresh().catch(() => {}); } break; case "modalShown": From 2367ccbac8e8f6625532e0c8f97d731356a8af3e Mon Sep 17 00:00:00 2001 From: Leslie Xiong <lxiong@livefront.com> Date: Fri, 19 Dec 2025 12:54:34 -0500 Subject: [PATCH 45/56] fixed tests --- libs/vault/src/models/filter-function.spec.ts | 15 ++++++----- .../src/models/vault-filter.model.spec.ts | 26 ++++++++++++------- .../src/services/vault-filter.service.spec.ts | 21 ++++++++++----- 3 files changed, 39 insertions(+), 23 deletions(-) diff --git a/libs/vault/src/models/filter-function.spec.ts b/libs/vault/src/models/filter-function.spec.ts index 5eaa38e6ef1..36bc6e683b5 100644 --- a/libs/vault/src/models/filter-function.spec.ts +++ b/libs/vault/src/models/filter-function.spec.ts @@ -2,6 +2,7 @@ // @ts-strict-ignore // eslint-disable-next-line no-restricted-imports import { Unassigned } from "@bitwarden/admin-console/common"; +import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid"; import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; @@ -128,8 +129,8 @@ describe("createFilter", () => { it("should return true when filter matches collection id", () => { const filterFunction = createFilterFunction({ - collectionId: "collectionId", - organizationId: "organizationId", + collectionId: "collectionId" as CollectionId, + organizationId: "organizationId" as OrganizationId, }); const result = filterFunction(cipher); @@ -139,8 +140,8 @@ describe("createFilter", () => { it("should return false when filter does not match collection id", () => { const filterFunction = createFilterFunction({ - collectionId: "nonMatchingCollectionId", - organizationId: "organizationId", + collectionId: "nonMatchingCollectionId" as CollectionId, + organizationId: "organizationId" as OrganizationId, }); const result = filterFunction(cipher); @@ -150,7 +151,7 @@ describe("createFilter", () => { it("should return false when filter does not match organization id", () => { const filterFunction = createFilterFunction({ - organizationId: "nonMatchingOrganizationId", + organizationId: "nonMatchingOrganizationId" as OrganizationId, }); const result = filterFunction(cipher); @@ -187,7 +188,9 @@ describe("createFilter", () => { }); it("should return true when filter matches organization id", () => { - const filterFunction = createFilterFunction({ organizationId: "organizationId" }); + const filterFunction = createFilterFunction({ + organizationId: "organizationId" as OrganizationId, + }); const result = filterFunction(cipher); diff --git a/libs/vault/src/models/vault-filter.model.spec.ts b/libs/vault/src/models/vault-filter.model.spec.ts index 35e5b668929..82296f8c836 100644 --- a/libs/vault/src/models/vault-filter.model.spec.ts +++ b/libs/vault/src/models/vault-filter.model.spec.ts @@ -3,7 +3,7 @@ // eslint-disable-next-line no-restricted-imports import { CollectionView } from "@bitwarden/admin-console/common"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; -import { CollectionId } from "@bitwarden/common/types/guid"; +import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid"; import { CipherType } from "@bitwarden/common/vault/enums"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; @@ -179,8 +179,8 @@ describe("VaultFilter", () => { it("should return true when filter matches collection id", () => { const filterFunction = createFilterFunction({ selectedCollectionNode: createCollectionFilterNode({ - id: "collectionId", - organizationId: "organizationId", + id: "collectionId" as CollectionId, + organizationId: "organizationId" as OrganizationId, }), }); @@ -192,8 +192,8 @@ describe("VaultFilter", () => { it("should return false when filter does not match collection id", () => { const filterFunction = createFilterFunction({ selectedCollectionNode: createCollectionFilterNode({ - id: "nonMatchingCollectionId", - organizationId: "organizationId", + id: "nonMatchingCollectionId" as CollectionId, + organizationId: "organizationId" as OrganizationId, }), }); @@ -205,7 +205,7 @@ describe("VaultFilter", () => { it("should return false when filter does not match organization id", () => { const filterFunction = createFilterFunction({ selectedOrganizationNode: createOrganizationFilterNode({ - id: "nonMatchingOrganizationId", + id: "nonMatchingOrganizationId" as OrganizationId, }), }); @@ -216,7 +216,9 @@ describe("VaultFilter", () => { it("should return false when filtering for my vault only", () => { const filterFunction = createFilterFunction({ - selectedOrganizationNode: createOrganizationFilterNode({ id: "MyVault" }), + selectedOrganizationNode: createOrganizationFilterNode({ + id: "MyVault" as OrganizationId, + }), }); const result = filterFunction(cipher); @@ -252,7 +254,9 @@ describe("VaultFilter", () => { it("should return true when filter matches organization id", () => { const filterFunction = createFilterFunction({ - selectedOrganizationNode: createOrganizationFilterNode({ id: "organizationId" }), + selectedOrganizationNode: createOrganizationFilterNode({ + id: "organizationId" as OrganizationId, + }), }); const result = filterFunction(cipher); @@ -277,7 +281,9 @@ describe("VaultFilter", () => { it("should return true when filtering for my vault only", () => { const cipher = createCipher({ organizationId: null }); const filterFunction = createFilterFunction({ - selectedOrganizationNode: createOrganizationFilterNode({ id: "MyVault" }), + selectedOrganizationNode: createOrganizationFilterNode({ + id: "MyVault" as OrganizationId, + }), }); const result = filterFunction(cipher); @@ -316,7 +322,7 @@ function createCollectionFilterNode( const collection = new CollectionView({ name: options.name ?? "Test Name", id: options.id ?? null, - organizationId: options.organizationId ?? "Org Id", + organizationId: options.organizationId ?? ("Org Id" as OrganizationId), }) as CollectionFilter; return new TreeNode<CollectionFilter>(collection, {} as TreeNode<CollectionFilter>); } diff --git a/libs/vault/src/services/vault-filter.service.spec.ts b/libs/vault/src/services/vault-filter.service.spec.ts index da66ad33a81..968966e9238 100644 --- a/libs/vault/src/services/vault-filter.service.spec.ts +++ b/libs/vault/src/services/vault-filter.service.spec.ts @@ -32,7 +32,7 @@ import { COLLAPSED_GROUPINGS } from "@bitwarden/common/vault/services/key-state/ import { VaultFilterService } from "./vault-filter.service"; jest.mock("@bitwarden/angular/vault/vault-filter/services/vault-filter.service", () => ({ - sortDefaultCollections: jest.fn(() => []), + sortDefaultCollections: jest.fn((): CollectionView[] => []), })); describe("vault filter service", () => { @@ -128,7 +128,10 @@ describe("vault filter service", () => { describe("organizations", () => { beforeEach(() => { - const storedOrgs = [createOrganization("1", "org1"), createOrganization("2", "org2")]; + const storedOrgs = [ + createOrganization("1" as OrganizationId, "org1"), + createOrganization("2" as OrganizationId, "org2"), + ]; organizations.next(storedOrgs); organizationDataOwnershipPolicy.next(false); singleOrgPolicy.next(false); @@ -176,7 +179,9 @@ describe("vault filter service", () => { describe("filtered folders with organization", () => { beforeEach(() => { // Org must be updated before folderService else the subscription uses the null org default value - vaultFilterService.setOrganizationFilter(createOrganization("org test id", "Test Org")); + vaultFilterService.setOrganizationFilter( + createOrganization("org test id" as OrganizationId, "Test Org"), + ); }); it("returns folders filtered by current organization", async () => { const storedCiphers = [ @@ -226,7 +231,9 @@ describe("vault filter service", () => { describe("collections", () => { describe("filtered collections", () => { it("returns collections filtered by current organization", async () => { - vaultFilterService.setOrganizationFilter(createOrganization("org test id", "Test Org")); + vaultFilterService.setOrganizationFilter( + createOrganization("org test id" as OrganizationId, "Test Org"), + ); const storedCollections = [ createCollectionView("1", "collection 1", "org test id"), @@ -317,8 +324,8 @@ describe("vault filter service", () => { it("calls sortDefaultCollections with the correct args", async () => { const storedOrgs = [ - createOrganization("id-defaultOrg1", "org1"), - createOrganization("id-defaultOrg2", "org2"), + createOrganization("id-defaultOrg1" as OrganizationId, "org1"), + createOrganization("id-defaultOrg2" as OrganizationId, "org2"), ]; organizations.next(storedOrgs); @@ -354,7 +361,7 @@ describe("vault filter service", () => { }); }); - function createOrganization(id: string, name: string) { + function createOrganization(id: OrganizationId, name: string) { const org = new Organization(); org.id = id; org.name = name; From 3cb1a557591a0a6202c2935c92c30f5f630511a4 Mon Sep 17 00:00:00 2001 From: Leslie Xiong <lxiong@livefront.com> Date: Fri, 19 Dec 2025 13:45:01 -0500 Subject: [PATCH 46/56] removed `else if (this.premiumBadgeComponent())` --- .../vault-v3/vault-filter/filters/status-filter.component.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts index 543bd550036..bf51321da17 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts @@ -70,9 +70,7 @@ export class StatusFilterComponent { if (canArchive || hasArchivedCiphers) { this.applyFilter("archive"); - } else if (this.premiumBadgeComponent()) { - // The `premiumBadgeComponent` should always be defined here, adding the - // if to satisfy TypeScript. + } else { await this.premiumBadgeComponent().promptForPremium(event); } } From 74410836af8b7cf22cf22ad61db02c78510bcd49 Mon Sep 17 00:00:00 2001 From: Leslie Xiong <lxiong@livefront.com> Date: Fri, 19 Dec 2025 13:59:58 -0500 Subject: [PATCH 47/56] fixed test errors in `desktop-layout.component` --- .../layout/desktop-layout.component.spec.ts | 43 ++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/apps/desktop/src/app/layout/desktop-layout.component.spec.ts b/apps/desktop/src/app/layout/desktop-layout.component.spec.ts index 74cddd02495..393a46f22a4 100644 --- a/apps/desktop/src/app/layout/desktop-layout.component.spec.ts +++ b/apps/desktop/src/app/layout/desktop-layout.component.spec.ts @@ -3,9 +3,18 @@ import { ComponentFixture, TestBed } from "@angular/core/testing"; import { RouterModule } from "@angular/router"; import { mock } from "jest-mock-extended"; +import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { NavigationModule } from "@bitwarden/components"; +import { CipherArchiveService } from "@bitwarden/common/vault/abstractions/cipher-archive.service"; +import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; +import { DialogService, NavigationModule } from "@bitwarden/components"; +import { + RoutedVaultFilterService, + VaultFilterServiceAbstraction as VaultFilterService, +} from "@bitwarden/vault"; +import { DesktopRoutedVaultFilterBridgeService } from "../services/desktop-routed-vault-filter-bridge.service"; import { SendFiltersNavComponent } from "../tools/send-v2/send-filters-nav.component"; import { DesktopLayoutComponent } from "./desktop-layout.component"; @@ -44,6 +53,38 @@ describe("DesktopLayoutComponent", () => { provide: I18nService, useValue: mock<I18nService>(), }, + { + provide: DesktopRoutedVaultFilterBridgeService, + useValue: mock<DesktopRoutedVaultFilterBridgeService>(), + }, + { + provide: RoutedVaultFilterService, + useValue: mock<RoutedVaultFilterService>(), + }, + { + provide: VaultFilterService, + useValue: mock<VaultFilterService>(), + }, + { + provide: AccountService, + useValue: mock<AccountService>(), + }, + { + provide: CipherArchiveService, + useValue: mock<CipherArchiveService>(), + }, + { + provide: FolderService, + useValue: mock<FolderService>(), + }, + { + provide: PolicyService, + useValue: mock<PolicyService>(), + }, + { + provide: DialogService, + useValue: mock<DialogService>(), + }, ], }) .overrideComponent(DesktopLayoutComponent, { From bbf492a2b7133e668fc8b695c081bcf738b2278e Mon Sep 17 00:00:00 2001 From: Leslie Xiong <lxiong@livefront.com> Date: Mon, 5 Jan 2026 11:25:06 -0500 Subject: [PATCH 48/56] fixed imports --- .../vault/components/vault-items/vault-items.component.spec.ts | 3 +-- .../app/vault/components/vault-items/vault-items.component.ts | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/web/src/app/vault/components/vault-items/vault-items.component.spec.ts b/apps/web/src/app/vault/components/vault-items/vault-items.component.spec.ts index c1c25c625da..9e43a16ba92 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-items.component.spec.ts +++ b/apps/web/src/app/vault/components/vault-items/vault-items.component.spec.ts @@ -11,8 +11,7 @@ import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/res import { CipherViewLike } from "@bitwarden/common/vault/utils/cipher-view-like-utils"; import { MenuModule, TableModule } from "@bitwarden/components"; import { I18nPipe } from "@bitwarden/ui-common"; -import { RoutedVaultFilterService } from "@bitwarden/web-vault/app/vault/individual-vault/vault-filter/services/routed-vault-filter.service"; -import { RoutedVaultFilterModel } from "@bitwarden/web-vault/app/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model"; +import { RoutedVaultFilterService, RoutedVaultFilterModel } from "@bitwarden/vault"; import { VaultItem } from "./vault-item"; import { VaultItemsComponent } from "./vault-items.component"; diff --git a/apps/web/src/app/vault/components/vault-items/vault-items.component.ts b/apps/web/src/app/vault/components/vault-items/vault-items.component.ts index a51009a1e5b..7ce24a5a0b4 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-items.component.ts +++ b/apps/web/src/app/vault/components/vault-items/vault-items.component.ts @@ -27,7 +27,7 @@ import { } from "@bitwarden/common/vault/utils/cipher-view-like-utils"; import { SortDirection, TableDataSource } from "@bitwarden/components"; import { OrganizationId } from "@bitwarden/sdk-internal"; -import { RoutedVaultFilterService } from "@bitwarden/web-vault/app/vault/individual-vault/vault-filter/services/routed-vault-filter.service"; +import { RoutedVaultFilterService } from "@bitwarden/vault"; import { GroupView } from "../../../admin-console/organizations/core"; From 6eeae302428addbdecc777c2bf0b5a370cf941ea Mon Sep 17 00:00:00 2001 From: Leslie Xiong <lxiong@livefront.com> Date: Mon, 5 Jan 2026 11:47:44 -0500 Subject: [PATCH 49/56] mocked vault filters nav component --- .../layout/desktop-layout.component.spec.ts | 62 ++++++------------- 1 file changed, 18 insertions(+), 44 deletions(-) diff --git a/apps/desktop/src/app/layout/desktop-layout.component.spec.ts b/apps/desktop/src/app/layout/desktop-layout.component.spec.ts index 356b6203f61..7b80dc94565 100644 --- a/apps/desktop/src/app/layout/desktop-layout.component.spec.ts +++ b/apps/desktop/src/app/layout/desktop-layout.component.spec.ts @@ -3,20 +3,12 @@ import { ComponentFixture, TestBed } from "@angular/core/testing"; import { RouterModule } from "@angular/router"; import { mock } from "jest-mock-extended"; -import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; -import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { FakeGlobalStateProvider } from "@bitwarden/common/spec"; -import { CipherArchiveService } from "@bitwarden/common/vault/abstractions/cipher-archive.service"; -import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; -import { DialogService, NavigationModule } from "@bitwarden/components"; +import { NavigationModule } from "@bitwarden/components"; import { GlobalStateProvider } from "@bitwarden/state"; -import { - RoutedVaultFilterService, - VaultFilterServiceAbstraction as VaultFilterService, -} from "@bitwarden/vault"; -import { DesktopRoutedVaultFilterBridgeService } from "../services/desktop-routed-vault-filter-bridge.service"; +import { VaultFilterComponent } from "../../vault/app/vault-v3/vault-filter/vault-filter.component"; import { SendFiltersNavComponent } from "../tools/send-v2/send-filters-nav.component"; import { DesktopLayoutComponent } from "./desktop-layout.component"; @@ -29,6 +21,13 @@ import { DesktopLayoutComponent } from "./desktop-layout.component"; }) class MockSendFiltersNavComponent {} +@Component({ + selector: "app-vault-filter", + template: "", + changeDetection: ChangeDetectionStrategy.OnPush, +}) +class MockVaultFiltersNavComponent {} + Object.defineProperty(window, "matchMedia", { writable: true, value: jest.fn().mockImplementation((query) => ({ @@ -57,38 +56,6 @@ describe("DesktopLayoutComponent", () => { provide: I18nService, useValue: mock<I18nService>(), }, - { - provide: DesktopRoutedVaultFilterBridgeService, - useValue: mock<DesktopRoutedVaultFilterBridgeService>(), - }, - { - provide: RoutedVaultFilterService, - useValue: mock<RoutedVaultFilterService>(), - }, - { - provide: VaultFilterService, - useValue: mock<VaultFilterService>(), - }, - { - provide: AccountService, - useValue: mock<AccountService>(), - }, - { - provide: CipherArchiveService, - useValue: mock<CipherArchiveService>(), - }, - { - provide: FolderService, - useValue: mock<FolderService>(), - }, - { - provide: PolicyService, - useValue: mock<PolicyService>(), - }, - { - provide: DialogService, - useValue: mock<DialogService>(), - }, { provide: GlobalStateProvider, useValue: fakeGlobalStateProvider, @@ -96,8 +63,8 @@ describe("DesktopLayoutComponent", () => { ], }) .overrideComponent(DesktopLayoutComponent, { - remove: { imports: [SendFiltersNavComponent] }, - add: { imports: [MockSendFiltersNavComponent] }, + remove: { imports: [SendFiltersNavComponent, VaultFilterComponent] }, + add: { imports: [MockSendFiltersNavComponent, MockVaultFiltersNavComponent] }, }) .compileComponents(); @@ -130,4 +97,11 @@ describe("DesktopLayoutComponent", () => { expect(sendFiltersNav).toBeTruthy(); }); + + it("renders vault filters navigation component", () => { + const compiled = fixture.nativeElement; + const vaultFiltersNav = compiled.querySelector("app-vault-filter"); + + expect(vaultFiltersNav).toBeTruthy(); + }); }); From ac02dda60fca8fbcfd014717c93fc8db1d1769ed Mon Sep 17 00:00:00 2001 From: Leslie Xiong <lxiong@livefront.com> Date: Mon, 5 Jan 2026 11:57:05 -0500 Subject: [PATCH 50/56] fixed import in `vault-items.stories` --- .../src/app/vault/components/vault-items/vault-items.stories.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/src/app/vault/components/vault-items/vault-items.stories.ts b/apps/web/src/app/vault/components/vault-items/vault-items.stories.ts index 9c56df0db59..a46ef6018da 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-items.stories.ts +++ b/apps/web/src/app/vault/components/vault-items/vault-items.stories.ts @@ -41,7 +41,7 @@ import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/res import { CipherViewLike } from "@bitwarden/common/vault/utils/cipher-view-like-utils"; import { LayoutComponent, StorybookGlobalStateProvider } from "@bitwarden/components"; import { GlobalStateProvider } from "@bitwarden/state"; -import { RoutedVaultFilterService } from "@bitwarden/web-vault/app/vault/individual-vault/vault-filter/services/routed-vault-filter.service"; +import { RoutedVaultFilterService } from "@bitwarden/vault"; import { GroupView } from "../../../admin-console/organizations/core"; import { PreloadedEnglishI18nModule } from "../../../core/tests"; From 4ae43048cd8fc3a54e9e6602a958c9b1e0e1018e Mon Sep 17 00:00:00 2001 From: Leslie Xiong <lxiong@livefront.com> Date: Mon, 5 Jan 2026 14:04:00 -0500 Subject: [PATCH 51/56] applied `forceActiveStyles` and `disableToggleOnClick` to `bit-nav-group` components --- .../vault-filter/filters/collection-filter.component.html | 3 ++- .../vault-filter/filters/folder-filter.component.html | 2 ++ .../vault-filter/filters/organization-filter.component.html | 4 +++- .../vault-v3/vault-filter/filters/type-filter.component.html | 2 ++ .../app/vault-v3/vault-filter/vault-filter.component.html | 4 ++++ 5 files changed, 13 insertions(+), 2 deletions(-) diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.html index ccb6eca3a0f..2ee78adcfb0 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.html +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.html @@ -1,11 +1,12 @@ @if (collection().children.length) { <bit-nav-group [icon]="collection().node.icon" - [class.active]="isActive()" [text]="displayName()" variant="tree" [appA11yTitle]="displayName()" (click)="applyFilter($event)" + [forceActiveStyles]="isActive()" + [disableToggleOnClick]="true" > @for (childCollection of collection().children; track childCollection.node.id) { <app-collection-filter [collection]="childCollection" [activeFilter]="activeFilter()" /> diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.html index 0867c509c2b..f063167c48f 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.html +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.html @@ -6,6 +6,8 @@ variant="tree" [appA11yTitle]="displayName()" (click)="applyFilter($event)" + [forceActiveStyles]="isActive()" + [disableToggleOnClick]="true" > @if (folder()?.node.id) { <button diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.html index 79f89a39260..e4e11b82a8a 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.html +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.html @@ -3,10 +3,12 @@ icon="bwi-filter" (click)="applyAllVaultsFilter()" [text]="'allVaults' | i18n" - [attr.aria-pressed]="activeFilter()?.organizationId === null" + [attr.aria-pressed]="!activeFilter()?.selectedOrganizationNode" [appA11yTitle]="'allVaults' | i18n" variant="tree" [open]="true" + [forceActiveStyles]="!activeFilter()?.selectedOrganizationNode" + [disableToggleOnClick]="true" > @for (organization of organizations().children ?? []; track organization.node.id) { <bit-nav-item diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.html index 1c9e58e0656..c9807e62066 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.html +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.html @@ -6,6 +6,8 @@ [appA11yTitle]="'allItems' | i18n" variant="tree" [open]="true" + [forceActiveStyles]="activeFilter()?.selectedCipherTypeNode?.node?.id === 'AllItems'" + [disableToggleOnClick]="true" > @for (typeFilter of typeFilters$ | async; track typeFilter) { <bit-nav-item diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.html index a782fe6c30e..fb3a67eeb46 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.html +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.html @@ -18,6 +18,8 @@ [text]="'collections' | i18n" variant="tree" [appA11yTitle]="'collections' | i18n" + [forceActiveStyles]="activeFilter()?.selectedCollectionNode" + [disableToggleOnClick]="true" > @for (collection of (collections$ | async)?.children ?? []; track collection.node.id) { <app-collection-filter [activeFilter]="activeFilter()" [collection]="collection" /> @@ -29,6 +31,8 @@ [text]="'folders' | i18n" variant="tree" [appA11yTitle]="'folders' | i18n" + [forceActiveStyles]="activeFilter()?.selectedFolderNode" + [disableToggleOnClick]="true" > @for (folder of (folders$ | async)?.children ?? []; track folder.node.id) { <app-folder-filter From 26b2c5a03e3837b583f46ca594029bd877d33ca7 Mon Sep 17 00:00:00 2001 From: Leslie Xiong <lxiong@livefront.com> Date: Tue, 6 Jan 2026 17:12:25 -0500 Subject: [PATCH 52/56] PR followup: - removed `DesktopRoutedVaultFilterBridgeService` - added 'baseRoute' injectable in `RoutedVaultFilterService` - removed `folderAddEditModalRef` from `VaultComponent` --- ...ktop-routed-vault-filter-bridge.service.ts | 38 ------------------- .../src/app/services/services.module.ts | 11 ++++-- .../vault-filter/vault-filter.component.ts | 4 +- .../src/vault/app/vault-v3/vault.component.ts | 18 ++------- .../services/routed-vault-filter.service.ts | 11 +++++- 5 files changed, 22 insertions(+), 60 deletions(-) delete mode 100644 apps/desktop/src/app/services/desktop-routed-vault-filter-bridge.service.ts diff --git a/apps/desktop/src/app/services/desktop-routed-vault-filter-bridge.service.ts b/apps/desktop/src/app/services/desktop-routed-vault-filter-bridge.service.ts deleted file mode 100644 index 2eadb6462ca..00000000000 --- a/apps/desktop/src/app/services/desktop-routed-vault-filter-bridge.service.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Injectable } from "@angular/core"; -import { Router } from "@angular/router"; - -import { - RoutedVaultFilterService, - RoutedVaultFilterBridgeService, - RoutedVaultFilterModel, - VaultFilterServiceAbstraction as VaultFilterService, -} from "@bitwarden/vault"; - -/** - * Desktop-specific extension of RoutedVaultFilterBridgeService that ensures - * vault filter navigation always goes to the /new-vault route. - */ -@Injectable() -export class DesktopRoutedVaultFilterBridgeService extends RoutedVaultFilterBridgeService { - private static readonly VAULT_ROUTE = "/new-vault"; - private readonly desktopRouter: Router; - private readonly desktopRoutedVaultFilterService: RoutedVaultFilterService; - - constructor( - router: Router, - routedVaultFilterService: RoutedVaultFilterService, - vaultFilterService: VaultFilterService, - ) { - super(router, routedVaultFilterService, vaultFilterService); - this.desktopRouter = router; - this.desktopRoutedVaultFilterService = routedVaultFilterService; - } - - override navigate(filter: RoutedVaultFilterModel) { - const extras = this.desktopRoutedVaultFilterService.createRoute(filter)[1]; - const vaultCommands = [DesktopRoutedVaultFilterBridgeService.VAULT_ROUTE]; - - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - void this.desktopRouter.navigate(vaultCommands, extras); - } -} diff --git a/apps/desktop/src/app/services/services.module.ts b/apps/desktop/src/app/services/services.module.ts index 895f62bba2e..59a3e3b289e 100644 --- a/apps/desktop/src/app/services/services.module.ts +++ b/apps/desktop/src/app/services/services.module.ts @@ -126,6 +126,8 @@ import { VaultFilterServiceAbstraction, VaultFilterService, RoutedVaultFilterService, + RoutedVaultFilterBridgeService, + VAULT_FILTER_BASE_ROUTE, } from "@bitwarden/vault"; import { DesktopLoginApprovalDialogComponentService } from "../../auth/login/desktop-login-approval-dialog-component.service"; @@ -161,7 +163,6 @@ import { NativeMessagingService } from "../../services/native-messaging.service" import { SearchBarService } from "../layout/search/search-bar.service"; import { DesktopFileDownloadService } from "./desktop-file-download.service"; -import { DesktopRoutedVaultFilterBridgeService } from "./desktop-routed-vault-filter-bridge.service"; import { InitService } from "./init.service"; import { NativeMessagingManifestService } from "./native-messaging-manifest.service"; import { DesktopSetInitialPasswordService } from "./set-initial-password/desktop-set-initial-password.service"; @@ -533,14 +534,18 @@ const safeProviders: SafeProvider[] = [ ConfigService, ], }), + safeProvider({ + provide: VAULT_FILTER_BASE_ROUTE, + useValue: "/new-vault", + }), safeProvider({ provide: RoutedVaultFilterService, useClass: RoutedVaultFilterService, deps: [ActivatedRoute], }), safeProvider({ - provide: DesktopRoutedVaultFilterBridgeService, - useClass: DesktopRoutedVaultFilterBridgeService, + provide: RoutedVaultFilterBridgeService, + useClass: RoutedVaultFilterBridgeService, deps: [Router, RoutedVaultFilterService, VaultFilterServiceAbstraction], }), ]; diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts index 3d6a481911d..aa54c736024 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts @@ -21,9 +21,9 @@ import { VaultFilter, VaultFilterServiceAbstraction as VaultFilterService, AddEditFolderDialogComponent, + RoutedVaultFilterBridgeService, } from "@bitwarden/vault"; -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"; @@ -56,7 +56,7 @@ import { TypeFilterComponent } from "./filters/type-filter.component"; ], }) export class VaultFilterComponent implements OnInit { - private routedVaultFilterBridgeService = inject(DesktopRoutedVaultFilterBridgeService); + private routedVaultFilterBridgeService = inject(RoutedVaultFilterBridgeService); private vaultFilterService: VaultFilterService = inject(VaultFilterService); private accountService: AccountService = inject(AccountService); private cipherArchiveService: CipherArchiveService = inject(CipherArchiveService); diff --git a/apps/desktop/src/vault/app/vault-v3/vault.component.ts b/apps/desktop/src/vault/app/vault-v3/vault.component.ts index 81761a4a88c..f4267036a2b 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault.component.ts @@ -1,13 +1,5 @@ import { CommonModule } from "@angular/common"; -import { - ChangeDetectorRef, - Component, - NgZone, - OnDestroy, - OnInit, - ViewChild, - ViewContainerRef, -} from "@angular/core"; +import { ChangeDetectorRef, Component, NgZone, OnDestroy, OnInit, ViewChild } from "@angular/core"; import { ActivatedRoute, Router } from "@angular/router"; import { firstValueFrom, @@ -83,10 +75,10 @@ import { ArchiveCipherUtilitiesService, VaultFilter, VaultFilterServiceAbstraction as VaultFilterService, + RoutedVaultFilterBridgeService, } from "@bitwarden/vault"; import { SearchBarService } from "../../../app/layout/search/search-bar.service"; -import { DesktopRoutedVaultFilterBridgeService } from "../../../app/services/desktop-routed-vault-filter-bridge.service"; import { DesktopCredentialGenerationService } from "../../../services/desktop-cipher-form-generator.service"; import { DesktopPremiumUpgradePromptService } from "../../../services/desktop-premium-upgrade-prompt.service"; import { invokeMenu, RendererMenuItem } from "../../../utils"; @@ -144,10 +136,6 @@ export class VaultComponent implements OnInit, OnDestroy, CopyClickListener { vaultItemsComponent: VaultItemsV2Component<CipherView> | null = null; // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals // eslint-disable-next-line @angular-eslint/prefer-signals - @ViewChild("folderAddEdit", { read: ViewContainerRef, static: true }) - folderAddEditModalRef: ViewContainerRef | null = null; - // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals - // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChild(CipherFormComponent) cipherFormComponent: CipherFormComponent | null = null; @@ -221,7 +209,7 @@ export class VaultComponent implements OnInit, OnDestroy, CopyClickListener { private cipherArchiveService: CipherArchiveService, private policyService: PolicyService, private archiveCipherUtilitiesService: ArchiveCipherUtilitiesService, - private routedVaultFilterBridgeService: DesktopRoutedVaultFilterBridgeService, + private routedVaultFilterBridgeService: RoutedVaultFilterBridgeService, private vaultFilterService: VaultFilterService, ) {} diff --git a/libs/vault/src/services/routed-vault-filter.service.ts b/libs/vault/src/services/routed-vault-filter.service.ts index ae928f8583f..9005d507da7 100644 --- a/libs/vault/src/services/routed-vault-filter.service.ts +++ b/libs/vault/src/services/routed-vault-filter.service.ts @@ -1,14 +1,20 @@ -import { Injectable, OnDestroy } from "@angular/core"; +import { Injectable, OnDestroy, inject } from "@angular/core"; import { ActivatedRoute, NavigationExtras } from "@angular/router"; import { combineLatest, map, Observable, Subject, takeUntil } from "rxjs"; import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid"; +import { SafeInjectionToken } from "@bitwarden/ui-common"; import { isRoutedVaultFilterItemType, RoutedVaultFilterModel, } from "../models/routed-vault-filter.model"; +/** + * Injection token for the base route path used in vault filter navigation. + */ +export const VAULT_FILTER_BASE_ROUTE = new SafeInjectionToken<string>("VaultFilterBaseRoute"); + /** * This service is an abstraction layer on top of ActivatedRoute that * encapsulates the logic of how filters are stored in the URL. @@ -19,6 +25,7 @@ import { @Injectable() export class RoutedVaultFilterService implements OnDestroy { private onDestroy = new Subject<void>(); + private baseRoute: string = inject(VAULT_FILTER_BASE_ROUTE, { optional: true }) ?? ""; /** * Filter values extracted from the URL. @@ -64,7 +71,7 @@ export class RoutedVaultFilterService implements OnDestroy { * @returns route that can be used with Router or RouterLink */ createRoute(filter: RoutedVaultFilterModel): [commands: any[], extras?: NavigationExtras] { - const commands: string[] = []; + const commands: string[] = this.baseRoute ? [this.baseRoute] : []; const extras: NavigationExtras = { queryParams: { collectionId: filter.collectionId ?? null, From b925ea3a5ec1faeb7fbd0385b70d049d80a0524e Mon Sep 17 00:00:00 2001 From: Leslie Xiong <lxiong@livefront.com> Date: Wed, 7 Jan 2026 14:50:18 -0500 Subject: [PATCH 53/56] removed `forceActveStyles` for 'Collections' and 'Folders' parent nav items --- .../vault/app/vault-v3/vault-filter/vault-filter.component.html | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.html index fb3a67eeb46..e0ae4687ed8 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.html +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.html @@ -18,7 +18,6 @@ [text]="'collections' | i18n" variant="tree" [appA11yTitle]="'collections' | i18n" - [forceActiveStyles]="activeFilter()?.selectedCollectionNode" [disableToggleOnClick]="true" > @for (collection of (collections$ | async)?.children ?? []; track collection.node.id) { @@ -31,7 +30,6 @@ [text]="'folders' | i18n" variant="tree" [appA11yTitle]="'folders' | i18n" - [forceActiveStyles]="activeFilter()?.selectedFolderNode" [disableToggleOnClick]="true" > @for (folder of (folders$ | async)?.children ?? []; track folder.node.id) { From f3b0a9271ee53a2467c9b52a694c9a78d71b9f99 Mon Sep 17 00:00:00 2001 From: Leslie Xiong <lxiong@livefront.com> Date: Thu, 8 Jan 2026 09:11:44 -0500 Subject: [PATCH 54/56] fixed type error in test --- libs/vault/src/services/vault-filter.service.spec.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/libs/vault/src/services/vault-filter.service.spec.ts b/libs/vault/src/services/vault-filter.service.spec.ts index 968966e9238..1c59c228357 100644 --- a/libs/vault/src/services/vault-filter.service.spec.ts +++ b/libs/vault/src/services/vault-filter.service.spec.ts @@ -97,7 +97,6 @@ describe("vault filter service", () => { stateProvider, collectionService, accountService, - configService, ); collapsedGroupingsState = stateProvider.singleUser.getFake(mockUserId, COLLAPSED_GROUPINGS); organizations.next([]); From 6e7362734fa0f8e2efb47bbabd5180b61f269278 Mon Sep 17 00:00:00 2001 From: Leslie Xiong <lxiong@livefront.com> Date: Mon, 12 Jan 2026 23:46:19 -0500 Subject: [PATCH 55/56] moved/fixed imports avoiding circular dependency --- .../vault-v2/view-v2/view-v2.component.ts | 2 +- .../services/vault-popup-items.service.spec.ts | 3 ++- .../vault-popup-list-filters.service.spec.ts | 3 ++- .../services/vault-popup-list-filters.service.ts | 10 +++++----- .../src/vault/popup/views/popup-cipher.view.ts | 2 +- .../models/response/collection.response.ts | 2 +- .../response/organization-collection.response.ts | 2 +- apps/cli/src/commands/get.command.ts | 5 ++--- apps/cli/src/utils.ts | 4 +--- .../src/vault/app/vault-v3/vault.component.ts | 3 ++- .../src/vault/app/vault/vault-v2.component.ts | 3 ++- .../bulk-collections-dialog.component.ts | 2 +- .../collection-name.badge.component.ts | 2 +- .../collections/pipes/get-collection-name.pipe.ts | 2 +- .../vault-filter/vault-filter.service.ts | 3 ++- .../vault-header/vault-header.component.ts | 6 +++--- .../organizations/collections/vault.component.ts | 13 ++++++------- .../core/views/add-edit-group-detail.ts | 2 +- .../organizations/core/views/group-details.view.ts | 2 +- .../core/views/organization-user-admin-view.ts | 6 ++---- .../core/views/organization-user.view.ts | 6 ++---- .../manage/group-add-edit.component.ts | 2 +- .../organizations/manage/groups.component.ts | 12 ++++++------ .../member-dialog/member-dialog.component.ts | 8 +++++--- .../organization-members.service.ts | 9 ++++----- .../access-selector/access-selector.models.ts | 6 ++---- .../collection-dialog.component.ts | 10 ++++++---- .../free-org-collection-limit.validator.ts | 2 +- .../import/import-collection-admin.service.ts | 3 ++- .../vault-item-dialog.component.ts | 2 +- .../vault-items/vault-cipher-row.component.ts | 2 +- .../vault-items/vault-collection-row.component.ts | 2 +- .../components/vault-items/vault-item-event.ts | 2 +- .../app/vault/components/vault-items/vault-item.ts | 2 +- .../vault-items/vault-items.component.spec.ts | 2 +- .../vault-items/vault-items.component.ts | 6 +++++- .../components/vault-items/vault-items.stories.ts | 6 +++--- .../bulk-delete-dialog.component.ts | 3 ++- .../organization-name-badge.component.ts | 2 +- .../vault-header/vault-header.component.ts | 6 +++--- .../app/vault/individual-vault/vault.component.ts | 14 +++++++------- ...dmin-console-cipher-form-config.service.spec.ts | 3 ++- .../services/member-access-report.service.ts | 2 +- .../abstractions/collection-admin.service.ts | 8 +++++--- .../collections/abstractions/collection.service.ts | 7 +++++-- .../models/collection-with-id.request.ts | 3 ++- .../common/collections/models/collection.spec.ts | 10 ++++++---- .../src/common/collections/models/index.ts | 6 ------ .../collections/services/collection.state.ts | 3 +-- .../services/default-collection-admin.service.ts | 14 ++++++++------ .../services/default-collection.service.spec.ts | 7 +++++-- .../services/default-collection.service.ts | 6 +++++- .../deprecated-vault-filter.service.ts | 4 +--- .../components/collection-filter.component.ts | 7 ++++--- .../components/vault-filter.component.ts | 4 +--- .../vault-filter/services/vault-filter.service.ts | 10 +++++----- libs/common/src/abstractions/api.service.ts | 5 ++--- .../collection-access-selection.view.ts | 0 .../models/collections}/collection-admin.view.ts | 2 +- .../models/collections}/collection.data.ts | 8 +++++--- .../models/collections}/collection.response.ts | 6 ++++-- .../models/collections}/collection.ts | 2 +- .../models/collections}/collection.view.ts | 0 .../src/admin-console/models/collections/index.ts | 6 ++++++ .../response/organization-export.response.ts | 4 +--- .../admin-console/utils/collection-utils.spec.ts | 3 +-- .../src/admin-console/utils/collection-utils.ts | 5 ++--- .../src/models/export/collection-with-id.export.ts | 7 ++++--- libs/common/src/models/export/collection.export.ts | 7 ++++--- .../src/platform/sync/default-sync.service.ts | 6 +++--- libs/common/src/platform/sync/sync.response.ts | 4 +--- libs/common/src/services/api.service.ts | 11 +++++------ .../services/cipher-authorization.service.spec.ts | 3 ++- libs/importer/src/components/import.component.ts | 10 +++++----- libs/importer/src/importers/base-importer.ts | 4 +--- .../bitwarden/bitwarden-encrypted-json-importer.ts | 4 +--- .../importer/src/importers/padlock-csv-importer.ts | 4 +--- .../src/importers/passpack-csv-importer.ts | 4 +--- .../password-depot-17-xml-importer.spec.ts | 4 +--- libs/importer/src/models/import-result.ts | 4 +--- .../import-collection.service.abstraction.ts | 4 +--- .../src/services/import.service.abstraction.ts | 5 +---- libs/importer/src/services/import.service.spec.ts | 6 +++--- libs/importer/src/services/import.service.ts | 5 ++--- .../src/services/org-vault-export.service.ts | 10 +++++----- .../vault/src/abstractions/vault-filter.service.ts | 6 ++++-- .../abstractions/cipher-form-config.service.ts | 4 +--- libs/vault/src/cipher-form/cipher-form.stories.ts | 4 +--- .../item-details-section.component.spec.ts | 8 +++++--- .../item-details/item-details-section.component.ts | 7 ++++--- .../vault/src/cipher-view/cipher-view.component.ts | 3 ++- .../item-details/item-details-v2.component.spec.ts | 4 +--- .../item-details/item-details-v2.component.ts | 6 ++++-- .../assign-collections.component.spec.ts | 10 +++++----- .../src/components/assign-collections.component.ts | 10 +++++----- libs/vault/src/models/filter-function.spec.ts | 5 +---- libs/vault/src/models/filter-function.ts | 3 +-- .../src/models/routed-vault-filter-bridge.model.ts | 3 +-- libs/vault/src/models/routed-vault-filter.model.ts | 3 +-- libs/vault/src/models/vault-filter.model.spec.ts | 5 +---- libs/vault/src/models/vault-filter.type.ts | 3 +-- .../default-vault-items-transfer.service.spec.ts | 3 ++- .../services/routed-vault-filter-bridge.service.ts | 3 +-- .../src/services/vault-filter.service.spec.ts | 12 ++++++------ libs/vault/src/services/vault-filter.service.ts | 10 +++++----- 105 files changed, 262 insertions(+), 271 deletions(-) rename libs/{admin-console/src/common/collections/models => common/src/admin-console/models/collections}/collection-access-selection.view.ts (100%) rename libs/{admin-console/src/common/collections/models => common/src/admin-console/models/collections}/collection-admin.view.ts (98%) rename libs/{admin-console/src/common/collections/models => common/src/admin-console/models/collections}/collection.data.ts (89%) rename libs/{admin-console/src/common/collections/models => common/src/admin-console/models/collections}/collection.response.ts (95%) rename libs/{admin-console/src/common/collections/models => common/src/admin-console/models/collections}/collection.ts (97%) rename libs/{admin-console/src/common/collections/models => common/src/admin-console/models/collections}/collection.view.ts (100%) create mode 100644 libs/common/src/admin-console/models/collections/index.ts diff --git a/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.ts b/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.ts index 64fa42bb2ba..b71e0d91ab7 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.ts @@ -7,9 +7,9 @@ import { FormsModule } from "@angular/forms"; import { ActivatedRoute, Router } from "@angular/router"; import { firstValueFrom, Observable, switchMap, of, map } from "rxjs"; -import { CollectionView } from "@bitwarden/admin-console/common"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; diff --git a/apps/browser/src/vault/popup/services/vault-popup-items.service.spec.ts b/apps/browser/src/vault/popup/services/vault-popup-items.service.spec.ts index 513e159f7aa..7cd73279c3d 100644 --- a/apps/browser/src/vault/popup/services/vault-popup-items.service.spec.ts +++ b/apps/browser/src/vault/popup/services/vault-popup-items.service.spec.ts @@ -3,8 +3,9 @@ import { TestBed } from "@angular/core/testing"; import { mock } from "jest-mock-extended"; import { BehaviorSubject, firstValueFrom, of, take, timeout } from "rxjs"; -import { CollectionService, CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionService } from "@bitwarden/admin-console/common"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { ProductTierType } from "@bitwarden/common/billing/enums"; diff --git a/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.spec.ts b/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.spec.ts index 866c5dd2e89..1358c5faebe 100644 --- a/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.spec.ts +++ b/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.spec.ts @@ -3,12 +3,13 @@ import { TestBed, discardPeriodicTasks, fakeAsync, tick } from "@angular/core/te import { FormBuilder } from "@angular/forms"; import { BehaviorSubject, skipWhile } from "rxjs"; -import { CollectionService, CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionService } from "@bitwarden/admin-console/common"; import { ViewCacheService } from "@bitwarden/angular/platform/view-cache"; import * as vaultFilterSvc from "@bitwarden/angular/vault/vault-filter/services/vault-filter.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { ProductTierType } from "@bitwarden/common/billing/enums"; diff --git a/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.ts b/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.ts index 439353cab50..85c415d01fe 100644 --- a/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.ts +++ b/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.ts @@ -14,17 +14,17 @@ import { take, } from "rxjs"; -import { - CollectionService, - CollectionTypes, - CollectionView, -} from "@bitwarden/admin-console/common"; +import { CollectionService } from "@bitwarden/admin-console/common"; import { ViewCacheService } from "@bitwarden/angular/platform/view-cache"; import { DynamicTreeNode } from "@bitwarden/angular/vault/vault-filter/models/dynamic-tree-node.model"; import { sortDefaultCollections } from "@bitwarden/angular/vault/vault-filter/services/vault-filter.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; +import { + CollectionView, + CollectionTypes, +} from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; diff --git a/apps/browser/src/vault/popup/views/popup-cipher.view.ts b/apps/browser/src/vault/popup/views/popup-cipher.view.ts index 6f85e7b6eb4..7d035ceb6df 100644 --- a/apps/browser/src/vault/popup/views/popup-cipher.view.ts +++ b/apps/browser/src/vault/popup/views/popup-cipher.view.ts @@ -1,4 +1,4 @@ -import { CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { CipherListView } from "@bitwarden/sdk-internal"; diff --git a/apps/cli/src/admin-console/models/response/collection.response.ts b/apps/cli/src/admin-console/models/response/collection.response.ts index a0d1ce1047d..4c56fdcd84a 100644 --- a/apps/cli/src/admin-console/models/response/collection.response.ts +++ b/apps/cli/src/admin-console/models/response/collection.response.ts @@ -1,4 +1,4 @@ -import { CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { CollectionWithIdExport } from "@bitwarden/common/models/export/collection-with-id.export"; import { BaseResponse } from "../../../models/response/base.response"; diff --git a/apps/cli/src/admin-console/models/response/organization-collection.response.ts b/apps/cli/src/admin-console/models/response/organization-collection.response.ts index a0d62b4c7b6..4b5c9a08f2b 100644 --- a/apps/cli/src/admin-console/models/response/organization-collection.response.ts +++ b/apps/cli/src/admin-console/models/response/organization-collection.response.ts @@ -1,4 +1,4 @@ -import { CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { SelectionReadOnly } from "../selection-read-only"; diff --git a/apps/cli/src/commands/get.command.ts b/apps/cli/src/commands/get.command.ts index 35816b56fb2..db070344628 100644 --- a/apps/cli/src/commands/get.command.ts +++ b/apps/cli/src/commands/get.command.ts @@ -1,12 +1,11 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { filter, firstValueFrom, map, switchMap } from "rxjs"; -import { CollectionService, CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionService } from "@bitwarden/admin-console/common"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AuditService } from "@bitwarden/common/abstractions/audit.service"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; diff --git a/apps/cli/src/utils.ts b/apps/cli/src/utils.ts index e321adbfd5e..72746cb9b71 100644 --- a/apps/cli/src/utils.ts +++ b/apps/cli/src/utils.ts @@ -1,12 +1,10 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import * as fs from "fs"; import * as path from "path"; import * as inquirer from "inquirer"; import * as JSZip from "jszip"; -import { CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; diff --git a/apps/desktop/src/vault/app/vault-v3/vault.component.ts b/apps/desktop/src/vault/app/vault-v3/vault.component.ts index cb10120d6eb..64f850826a3 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault.component.ts @@ -12,7 +12,7 @@ import { } from "rxjs"; import { filter, map, take } from "rxjs/operators"; -import { CollectionService, CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionService } from "@bitwarden/admin-console/common"; import { PremiumBadgeComponent } from "@bitwarden/angular/billing/components/premium-badge"; import { VaultViewPasswordHistoryService } from "@bitwarden/angular/services/view-password-history.service"; import { AuthRequestServiceAbstraction } from "@bitwarden/auth/common"; @@ -20,6 +20,7 @@ import { EventCollectionService } from "@bitwarden/common/abstractions/event/eve import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; diff --git a/apps/desktop/src/vault/app/vault/vault-v2.component.ts b/apps/desktop/src/vault/app/vault/vault-v2.component.ts index eedcb4dde83..f92b433125c 100644 --- a/apps/desktop/src/vault/app/vault/vault-v2.component.ts +++ b/apps/desktop/src/vault/app/vault/vault-v2.component.ts @@ -23,7 +23,7 @@ import { } from "rxjs"; import { filter, map, take } from "rxjs/operators"; -import { CollectionService, CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionService } from "@bitwarden/admin-console/common"; import { PremiumBadgeComponent } from "@bitwarden/angular/billing/components/premium-badge"; import { VaultViewPasswordHistoryService } from "@bitwarden/angular/services/view-password-history.service"; import { VaultFilter } from "@bitwarden/angular/vault/vault-filter/models/vault-filter.model"; @@ -32,6 +32,7 @@ import { EventCollectionService } from "@bitwarden/common/abstractions/event/eve import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason"; diff --git a/apps/web/src/app/admin-console/organizations/collections/bulk-collections-dialog/bulk-collections-dialog.component.ts b/apps/web/src/app/admin-console/organizations/collections/bulk-collections-dialog/bulk-collections-dialog.component.ts index b8c82ac2f01..d5d09ad53df 100644 --- a/apps/web/src/app/admin-console/organizations/collections/bulk-collections-dialog/bulk-collections-dialog.component.ts +++ b/apps/web/src/app/admin-console/organizations/collections/bulk-collections-dialog/bulk-collections-dialog.component.ts @@ -7,12 +7,12 @@ import { combineLatest, of, Subject, switchMap, takeUntil } from "rxjs"; import { CollectionAdminService, OrganizationUserApiService, - CollectionView, } from "@bitwarden/admin-console/common"; import { getOrganizationById, OrganizationService, } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; diff --git a/apps/web/src/app/admin-console/organizations/collections/collection-badge/collection-name.badge.component.ts b/apps/web/src/app/admin-console/organizations/collections/collection-badge/collection-name.badge.component.ts index 70a2e40001a..d10b18b77f6 100644 --- a/apps/web/src/app/admin-console/organizations/collections/collection-badge/collection-name.badge.component.ts +++ b/apps/web/src/app/admin-console/organizations/collections/collection-badge/collection-name.badge.component.ts @@ -2,7 +2,7 @@ // @ts-strict-ignore import { Component, Input } from "@angular/core"; -import { CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { uuidAsString } from "@bitwarden/common/platform/abstractions/sdk/sdk.service"; import { CollectionId } from "@bitwarden/sdk-internal"; diff --git a/apps/web/src/app/admin-console/organizations/collections/pipes/get-collection-name.pipe.ts b/apps/web/src/app/admin-console/organizations/collections/pipes/get-collection-name.pipe.ts index b52719304b8..0dfdf6c537b 100644 --- a/apps/web/src/app/admin-console/organizations/collections/pipes/get-collection-name.pipe.ts +++ b/apps/web/src/app/admin-console/organizations/collections/pipes/get-collection-name.pipe.ts @@ -1,6 +1,6 @@ import { Pipe, PipeTransform } from "@angular/core"; -import { CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; @Pipe({ name: "collectionNameFromId", diff --git a/apps/web/src/app/admin-console/organizations/collections/vault-filter/vault-filter.service.ts b/apps/web/src/app/admin-console/organizations/collections/vault-filter/vault-filter.service.ts index 2899295fd72..fa17e243516 100644 --- a/apps/web/src/app/admin-console/organizations/collections/vault-filter/vault-filter.service.ts +++ b/apps/web/src/app/admin-console/organizations/collections/vault-filter/vault-filter.service.ts @@ -1,9 +1,10 @@ import { Injectable, OnDestroy } from "@angular/core"; import { map, Observable, ReplaySubject, Subject } from "rxjs"; -import { CollectionAdminView, CollectionService } from "@bitwarden/admin-console/common"; +import { CollectionService } from "@bitwarden/admin-console/common"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { CollectionAdminView } from "@bitwarden/common/admin-console/models/collections"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { StateProvider } from "@bitwarden/common/platform/state"; diff --git a/apps/web/src/app/admin-console/organizations/collections/vault-header/vault-header.component.ts b/apps/web/src/app/admin-console/organizations/collections/vault-header/vault-header.component.ts index 20b31488649..1c9ae89f36b 100644 --- a/apps/web/src/app/admin-console/organizations/collections/vault-header/vault-header.component.ts +++ b/apps/web/src/app/admin-console/organizations/collections/vault-header/vault-header.component.ts @@ -7,12 +7,12 @@ import { Component, EventEmitter, Input, Output } from "@angular/core"; import { Router } from "@angular/router"; import { firstValueFrom, switchMap } from "rxjs"; +import { CollectionAdminService } from "@bitwarden/admin-console/common"; +import { JslibModule } from "@bitwarden/angular/jslib.module"; import { - CollectionAdminService, CollectionAdminView, Unassigned, -} from "@bitwarden/admin-console/common"; -import { JslibModule } from "@bitwarden/angular/jslib.module"; +} from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; diff --git a/apps/web/src/app/admin-console/organizations/collections/vault.component.ts b/apps/web/src/app/admin-console/organizations/collections/vault.component.ts index 5b99009869a..5f952fa8b4a 100644 --- a/apps/web/src/app/admin-console/organizations/collections/vault.component.ts +++ b/apps/web/src/app/admin-console/organizations/collections/vault.component.ts @@ -27,18 +27,17 @@ import { takeUntil, } from "rxjs/operators"; -import { - CollectionAdminService, - CollectionAdminView, - CollectionService, - CollectionView, - Unassigned, -} from "@bitwarden/admin-console/common"; +import { CollectionAdminService, CollectionService } from "@bitwarden/admin-console/common"; import { SearchPipe } from "@bitwarden/angular/pipes/search.pipe"; import { NoResults } from "@bitwarden/assets/svg"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { + CollectionView, + CollectionAdminView, + Unassigned, +} from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { getFlatCollectionTree, diff --git a/apps/web/src/app/admin-console/organizations/core/views/add-edit-group-detail.ts b/apps/web/src/app/admin-console/organizations/core/views/add-edit-group-detail.ts index 83fe65c07a9..6c40c4f99a0 100644 --- a/apps/web/src/app/admin-console/organizations/core/views/add-edit-group-detail.ts +++ b/apps/web/src/app/admin-console/organizations/core/views/add-edit-group-detail.ts @@ -1,4 +1,4 @@ -import { CollectionAccessSelectionView } from "@bitwarden/admin-console/common"; +import { CollectionAccessSelectionView } from "@bitwarden/common/admin-console/models/collections"; export interface AddEditGroupDetail { id: string; diff --git a/apps/web/src/app/admin-console/organizations/core/views/group-details.view.ts b/apps/web/src/app/admin-console/organizations/core/views/group-details.view.ts index a72b4c26ebb..35a53b293e8 100644 --- a/apps/web/src/app/admin-console/organizations/core/views/group-details.view.ts +++ b/apps/web/src/app/admin-console/organizations/core/views/group-details.view.ts @@ -1,6 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { CollectionAccessSelectionView } from "@bitwarden/admin-console/common"; +import { CollectionAccessSelectionView } from "@bitwarden/common/admin-console/models/collections"; import { View } from "@bitwarden/common/models/view/view"; import { GroupDetailsResponse } from "../services/group/responses/group.response"; diff --git a/apps/web/src/app/admin-console/organizations/core/views/organization-user-admin-view.ts b/apps/web/src/app/admin-console/organizations/core/views/organization-user-admin-view.ts index 264e37c6bd3..0d2fa5b77ce 100644 --- a/apps/web/src/app/admin-console/organizations/core/views/organization-user-admin-view.ts +++ b/apps/web/src/app/admin-console/organizations/core/views/organization-user-admin-view.ts @@ -1,14 +1,12 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { - CollectionAccessSelectionView, - OrganizationUserDetailsResponse, -} from "@bitwarden/admin-console/common"; +import { OrganizationUserDetailsResponse } from "@bitwarden/admin-console/common"; import { OrganizationUserStatusType, OrganizationUserType, } from "@bitwarden/common/admin-console/enums"; import { PermissionsApi } from "@bitwarden/common/admin-console/models/api/permissions.api"; +import { CollectionAccessSelectionView } from "@bitwarden/common/admin-console/models/collections"; export class OrganizationUserAdminView { id: string; diff --git a/apps/web/src/app/admin-console/organizations/core/views/organization-user.view.ts b/apps/web/src/app/admin-console/organizations/core/views/organization-user.view.ts index 34398728a51..33696af97c4 100644 --- a/apps/web/src/app/admin-console/organizations/core/views/organization-user.view.ts +++ b/apps/web/src/app/admin-console/organizations/core/views/organization-user.view.ts @@ -1,14 +1,12 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { - OrganizationUserUserDetailsResponse, - CollectionAccessSelectionView, -} from "@bitwarden/admin-console/common"; +import { OrganizationUserUserDetailsResponse } from "@bitwarden/admin-console/common"; import { OrganizationUserStatusType, OrganizationUserType, } from "@bitwarden/common/admin-console/enums"; import { PermissionsApi } from "@bitwarden/common/admin-console/models/api/permissions.api"; +import { CollectionAccessSelectionView } from "@bitwarden/common/admin-console/models/collections"; export class OrganizationUserView { id: string; diff --git a/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.ts b/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.ts index 03a24703c0f..e778d0fb63d 100644 --- a/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.ts +++ b/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.ts @@ -18,7 +18,6 @@ import { import { CollectionAdminService, - CollectionAdminView, OrganizationUserApiService, } from "@bitwarden/admin-console/common"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; @@ -26,6 +25,7 @@ import { getOrganizationById, OrganizationService, } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { CollectionAdminView } from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; diff --git a/apps/web/src/app/admin-console/organizations/manage/groups.component.ts b/apps/web/src/app/admin-console/organizations/manage/groups.component.ts index d7dcb8a8aa2..7a103becfad 100644 --- a/apps/web/src/app/admin-console/organizations/manage/groups.component.ts +++ b/apps/web/src/app/admin-console/organizations/manage/groups.component.ts @@ -17,15 +17,15 @@ import { } from "rxjs"; import { debounceTime, first } from "rxjs/operators"; +import { CollectionService } from "@bitwarden/admin-console/common"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { - CollectionService, - CollectionData, - Collection, + CollectionView, CollectionDetailsResponse, CollectionResponse, - CollectionView, -} from "@bitwarden/admin-console/common"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; + Collection, + CollectionData, +} from "@bitwarden/common/admin-console/models/collections"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { ListResponse } from "@bitwarden/common/models/response/list.response"; diff --git a/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts b/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts index 9e40e5afe37..1fa4c8bf8f7 100644 --- a/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts @@ -15,11 +15,8 @@ import { } from "rxjs"; import { - CollectionAccessSelectionView, CollectionAdminService, - CollectionAdminView, OrganizationUserApiService, - CollectionView, } from "@bitwarden/admin-console/common"; import { getOrganizationById, @@ -30,6 +27,11 @@ import { OrganizationUserType, } from "@bitwarden/common/admin-console/enums"; import { PermissionsApi } from "@bitwarden/common/admin-console/models/api/permissions.api"; +import { + CollectionAccessSelectionView, + CollectionAdminView, + CollectionView, +} from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; diff --git a/apps/web/src/app/admin-console/organizations/members/services/organization-members-service/organization-members.service.ts b/apps/web/src/app/admin-console/organizations/members/services/organization-members-service/organization-members.service.ts index 0dc417cc2c6..b2695b7568f 100644 --- a/apps/web/src/app/admin-console/organizations/members/services/organization-members-service/organization-members.service.ts +++ b/apps/web/src/app/admin-console/organizations/members/services/organization-members-service/organization-members.service.ts @@ -1,14 +1,13 @@ import { Injectable } from "@angular/core"; import { combineLatest, firstValueFrom, from, map, switchMap } from "rxjs"; +import { CollectionService, OrganizationUserApiService } from "@bitwarden/admin-console/common"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { + CollectionDetailsResponse, Collection, CollectionData, - CollectionDetailsResponse, - CollectionService, - OrganizationUserApiService, -} from "@bitwarden/admin-console/common"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; +} from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; diff --git a/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.models.ts b/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.models.ts index 45691ae98d6..9755beefbf1 100644 --- a/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.models.ts +++ b/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.models.ts @@ -1,13 +1,11 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { - CollectionAccessSelectionView, - OrganizationUserUserDetailsResponse, -} from "@bitwarden/admin-console/common"; +import { OrganizationUserUserDetailsResponse } from "@bitwarden/admin-console/common"; import { OrganizationUserStatusType, OrganizationUserType, } from "@bitwarden/common/admin-console/enums"; +import { CollectionAccessSelectionView } from "@bitwarden/common/admin-console/models/collections"; import { SelectItemView } from "@bitwarden/components"; import { GroupView } from "../../../core"; diff --git a/apps/web/src/app/admin-console/organizations/shared/components/collection-dialog/collection-dialog.component.ts b/apps/web/src/app/admin-console/organizations/shared/components/collection-dialog/collection-dialog.component.ts index 7b189270e1b..4f40ea701d2 100644 --- a/apps/web/src/app/admin-console/organizations/shared/components/collection-dialog/collection-dialog.component.ts +++ b/apps/web/src/app/admin-console/organizations/shared/components/collection-dialog/collection-dialog.component.ts @@ -18,19 +18,21 @@ import { import { first } from "rxjs/operators"; import { - CollectionAccessSelectionView, CollectionAdminService, - CollectionAdminView, OrganizationUserApiService, OrganizationUserUserMiniResponse, - CollectionResponse, - CollectionView, CollectionService, } from "@bitwarden/admin-console/common"; import { getOrganizationById, OrganizationService, } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { + CollectionAccessSelectionView, + CollectionAdminView, + CollectionView, + CollectionResponse, +} from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; diff --git a/apps/web/src/app/admin-console/organizations/shared/validators/free-org-collection-limit.validator.ts b/apps/web/src/app/admin-console/organizations/shared/validators/free-org-collection-limit.validator.ts index 7132428c375..32a5736748c 100644 --- a/apps/web/src/app/admin-console/organizations/shared/validators/free-org-collection-limit.validator.ts +++ b/apps/web/src/app/admin-console/organizations/shared/validators/free-org-collection-limit.validator.ts @@ -1,7 +1,7 @@ import { AbstractControl, AsyncValidatorFn, FormControl, ValidationErrors } from "@angular/forms"; import { combineLatest, map, Observable, of } from "rxjs"; -import { Collection } from "@bitwarden/admin-console/common"; +import { Collection } from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { getById } from "@bitwarden/common/platform/misc"; diff --git a/apps/web/src/app/tools/import/import-collection-admin.service.ts b/apps/web/src/app/tools/import/import-collection-admin.service.ts index b63cd15047b..1f77d99cace 100644 --- a/apps/web/src/app/tools/import/import-collection-admin.service.ts +++ b/apps/web/src/app/tools/import/import-collection-admin.service.ts @@ -1,7 +1,8 @@ import { Injectable } from "@angular/core"; import { firstValueFrom } from "rxjs"; -import { CollectionAdminService, CollectionAdminView } from "@bitwarden/admin-console/common"; +import { CollectionAdminService } from "@bitwarden/admin-console/common"; +import { CollectionAdminView } from "@bitwarden/common/admin-console/models/collections"; import { ImportCollectionServiceAbstraction } from "@bitwarden/importer-core"; import { UserId } from "@bitwarden/user-core"; diff --git a/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts b/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts index 34d59f12f26..7e861b211be 100644 --- a/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts +++ b/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts @@ -15,11 +15,11 @@ import { Router } from "@angular/router"; import { firstValueFrom, Observable, Subject, switchMap } from "rxjs"; import { map } from "rxjs/operators"; -import { CollectionView } from "@bitwarden/admin-console/common"; import { PremiumBadgeComponent } from "@bitwarden/angular/billing/components/premium-badge"; import { VaultViewPasswordHistoryService } from "@bitwarden/angular/services/view-password-history.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; diff --git a/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.ts b/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.ts index e437537b1cc..df1e70723ca 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.ts +++ b/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.ts @@ -11,7 +11,7 @@ import { input, } from "@angular/core"; -import { CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { CipherType } from "@bitwarden/common/vault/enums"; diff --git a/apps/web/src/app/vault/components/vault-items/vault-collection-row.component.ts b/apps/web/src/app/vault/components/vault-items/vault-collection-row.component.ts index daa981d509a..9e6a589849f 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-collection-row.component.ts +++ b/apps/web/src/app/vault/components/vault-items/vault-collection-row.component.ts @@ -7,7 +7,7 @@ import { Unassigned, CollectionView, CollectionTypes, -} from "@bitwarden/admin-console/common"; +} from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { CipherViewLike } from "@bitwarden/common/vault/utils/cipher-view-like-utils"; diff --git a/apps/web/src/app/vault/components/vault-items/vault-item-event.ts b/apps/web/src/app/vault/components/vault-items/vault-item-event.ts index a4651da69e2..8cd4b98af40 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-item-event.ts +++ b/apps/web/src/app/vault/components/vault-items/vault-item-event.ts @@ -1,4 +1,4 @@ -import { CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { CipherViewLike } from "@bitwarden/common/vault/utils/cipher-view-like-utils"; import { CollectionPermission } from "@bitwarden/web-vault/app/admin-console/organizations/shared/components/access-selector"; diff --git a/apps/web/src/app/vault/components/vault-items/vault-item.ts b/apps/web/src/app/vault/components/vault-items/vault-item.ts index bccb84fb0bf..27abca09eca 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-item.ts +++ b/apps/web/src/app/vault/components/vault-items/vault-item.ts @@ -1,4 +1,4 @@ -import { CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { CipherViewLike } from "@bitwarden/common/vault/utils/cipher-view-like-utils"; export interface VaultItem<C extends CipherViewLike> { diff --git a/apps/web/src/app/vault/components/vault-items/vault-items.component.spec.ts b/apps/web/src/app/vault/components/vault-items/vault-items.component.spec.ts index 91165146eef..ac74e75f07c 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-items.component.spec.ts +++ b/apps/web/src/app/vault/components/vault-items/vault-items.component.spec.ts @@ -2,7 +2,7 @@ import { ScrollingModule } from "@angular/cdk/scrolling"; import { TestBed } from "@angular/core/testing"; import { of, Subject } from "rxjs"; -import { CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { CipherArchiveService } from "@bitwarden/common/vault/abstractions/cipher-archive.service"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; diff --git a/apps/web/src/app/vault/components/vault-items/vault-items.component.ts b/apps/web/src/app/vault/components/vault-items/vault-items.component.ts index d8b060a188e..7deaa2ff75e 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-items.component.ts +++ b/apps/web/src/app/vault/components/vault-items/vault-items.component.ts @@ -13,7 +13,11 @@ import { switchMap, } from "rxjs"; -import { CollectionView, Unassigned, CollectionAdminView } from "@bitwarden/admin-console/common"; +import { + CollectionAdminView, + Unassigned, + CollectionView, +} from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { CipherArchiveService } from "@bitwarden/common/vault/abstractions/cipher-archive.service"; import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service"; diff --git a/apps/web/src/app/vault/components/vault-items/vault-items.stories.ts b/apps/web/src/app/vault/components/vault-items/vault-items.stories.ts index a46ef6018da..76d6b460df0 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-items.stories.ts +++ b/apps/web/src/app/vault/components/vault-items/vault-items.stories.ts @@ -11,13 +11,13 @@ import { } from "@storybook/angular"; import { BehaviorSubject, of } from "rxjs"; +import { OrganizationUserType } from "@bitwarden/common/admin-console/enums"; +import { PermissionsApi } from "@bitwarden/common/admin-console/models/api/permissions.api"; import { CollectionAccessSelectionView, CollectionAdminView, Unassigned, -} from "@bitwarden/admin-console/common"; -import { OrganizationUserType } from "@bitwarden/common/admin-console/enums"; -import { PermissionsApi } from "@bitwarden/common/admin-console/models/api/permissions.api"; +} from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AvatarService } from "@bitwarden/common/auth/abstractions/avatar.service"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; diff --git a/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-delete-dialog/bulk-delete-dialog.component.ts b/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-delete-dialog/bulk-delete-dialog.component.ts index 5f139ade144..46f2b5da735 100644 --- a/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-delete-dialog/bulk-delete-dialog.component.ts +++ b/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-delete-dialog/bulk-delete-dialog.component.ts @@ -3,8 +3,9 @@ import { Component, Inject } from "@angular/core"; import { firstValueFrom } from "rxjs"; -import { CollectionService, CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionService } from "@bitwarden/admin-console/common"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; diff --git a/apps/web/src/app/vault/individual-vault/organization-badge/organization-name-badge.component.ts b/apps/web/src/app/vault/individual-vault/organization-badge/organization-name-badge.component.ts index 19c462193e1..046def13d1a 100644 --- a/apps/web/src/app/vault/individual-vault/organization-badge/organization-name-badge.component.ts +++ b/apps/web/src/app/vault/individual-vault/organization-badge/organization-name-badge.component.ts @@ -3,7 +3,7 @@ import { Component, Input, OnChanges } from "@angular/core"; import { firstValueFrom } from "rxjs"; -import { Unassigned } from "@bitwarden/admin-console/common"; +import { Unassigned } from "@bitwarden/common/admin-console/models/collections"; import { AvatarService } from "@bitwarden/common/auth/abstractions/avatar.service"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; diff --git a/apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.ts b/apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.ts index b4dbc5cfb1e..e7c14825fe2 100644 --- a/apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.ts +++ b/apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.ts @@ -3,13 +3,13 @@ import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from import { Router } from "@angular/router"; import { firstValueFrom, switchMap } from "rxjs"; +import { CollectionAdminService } from "@bitwarden/admin-console/common"; +import { JslibModule } from "@bitwarden/angular/jslib.module"; import { Unassigned, CollectionView, - CollectionAdminService, CollectionTypes, -} from "@bitwarden/admin-console/common"; -import { JslibModule } from "@bitwarden/angular/jslib.module"; +} from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; diff --git a/apps/web/src/app/vault/individual-vault/vault.component.ts b/apps/web/src/app/vault/individual-vault/vault.component.ts index f249b0dd7a2..cad2c97557b 100644 --- a/apps/web/src/app/vault/individual-vault/vault.component.ts +++ b/apps/web/src/app/vault/individual-vault/vault.component.ts @@ -25,13 +25,7 @@ import { tap, } from "rxjs/operators"; -import { - CollectionData, - CollectionDetailsResponse, - CollectionService, - CollectionView, - Unassigned, -} from "@bitwarden/admin-console/common"; +import { CollectionService } from "@bitwarden/admin-console/common"; import { SearchPipe } from "@bitwarden/angular/pipes/search.pipe"; import { NoResults, @@ -50,6 +44,12 @@ import { } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; +import { + CollectionDetailsResponse, + CollectionView, + Unassigned, + CollectionData, +} from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { getNestedCollectionTree, diff --git a/apps/web/src/app/vault/org-vault/services/admin-console-cipher-form-config.service.spec.ts b/apps/web/src/app/vault/org-vault/services/admin-console-cipher-form-config.service.spec.ts index b365e06e6f8..bd97ed4ea55 100644 --- a/apps/web/src/app/vault/org-vault/services/admin-console-cipher-form-config.service.spec.ts +++ b/apps/web/src/app/vault/org-vault/services/admin-console-cipher-form-config.service.spec.ts @@ -1,11 +1,12 @@ import { TestBed } from "@angular/core/testing"; import { BehaviorSubject, of } from "rxjs"; -import { CollectionAdminService, CollectionAdminView } from "@bitwarden/admin-console/common"; +import { CollectionAdminService } from "@bitwarden/admin-console/common"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { OrganizationUserStatusType } from "@bitwarden/common/admin-console/enums"; +import { CollectionAdminView } from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { mockAccountServiceWith } from "@bitwarden/common/spec"; diff --git a/bitwarden_license/bit-web/src/app/dirt/reports/member-access-report/services/member-access-report.service.ts b/bitwarden_license/bit-web/src/app/dirt/reports/member-access-report/services/member-access-report.service.ts index 3bd74391419..bb14b61006e 100644 --- a/bitwarden_license/bit-web/src/app/dirt/reports/member-access-report/services/member-access-report.service.ts +++ b/bitwarden_license/bit-web/src/app/dirt/reports/member-access-report/services/member-access-report.service.ts @@ -3,7 +3,7 @@ import { Injectable } from "@angular/core"; import { firstValueFrom, map } from "rxjs"; -import { CollectionAccessSelectionView } from "@bitwarden/admin-console/common"; +import { CollectionAccessSelectionView } from "@bitwarden/common/admin-console/models/collections"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; diff --git a/libs/admin-console/src/common/collections/abstractions/collection-admin.service.ts b/libs/admin-console/src/common/collections/abstractions/collection-admin.service.ts index 9fde1b2090b..93e1aa1f32f 100644 --- a/libs/admin-console/src/common/collections/abstractions/collection-admin.service.ts +++ b/libs/admin-console/src/common/collections/abstractions/collection-admin.service.ts @@ -1,10 +1,12 @@ import { Observable } from "rxjs"; -import { CollectionDetailsResponse } from "@bitwarden/admin-console/common"; +import { + CollectionAdminView, + CollectionAccessSelectionView, + CollectionDetailsResponse, +} from "@bitwarden/common/admin-console/models/collections"; import { UserId } from "@bitwarden/common/types/guid"; -import { CollectionAccessSelectionView, CollectionAdminView } from "../models"; - export abstract class CollectionAdminService { abstract collectionAdminViews$( organizationId: string, diff --git a/libs/admin-console/src/common/collections/abstractions/collection.service.ts b/libs/admin-console/src/common/collections/abstractions/collection.service.ts index f0f02ee377e..2c44c8e095a 100644 --- a/libs/admin-console/src/common/collections/abstractions/collection.service.ts +++ b/libs/admin-console/src/common/collections/abstractions/collection.service.ts @@ -1,11 +1,14 @@ import { Observable } from "rxjs"; +import { + CollectionView, + Collection, + CollectionData, +} from "@bitwarden/common/admin-console/models/collections"; import { CollectionId, OrganizationId, UserId } from "@bitwarden/common/types/guid"; import { OrgKey } from "@bitwarden/common/types/key"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; -import { CollectionData, Collection, CollectionView } from "../models"; - export abstract class CollectionService { abstract encryptedCollections$(userId: UserId): Observable<Collection[] | null>; abstract decryptedCollections$(userId: UserId): Observable<CollectionView[]>; diff --git a/libs/admin-console/src/common/collections/models/collection-with-id.request.ts b/libs/admin-console/src/common/collections/models/collection-with-id.request.ts index 0f7b83705ef..f8f363e1fa8 100644 --- a/libs/admin-console/src/common/collections/models/collection-with-id.request.ts +++ b/libs/admin-console/src/common/collections/models/collection-with-id.request.ts @@ -1,4 +1,5 @@ -import { Collection } from "./collection"; +import { Collection } from "@bitwarden/common/admin-console/models/collections"; + import { BaseCollectionRequest } from "./collection.request"; export class CollectionWithIdRequest extends BaseCollectionRequest { diff --git a/libs/admin-console/src/common/collections/models/collection.spec.ts b/libs/admin-console/src/common/collections/models/collection.spec.ts index 16066f88ce1..ab81756ccdd 100644 --- a/libs/admin-console/src/common/collections/models/collection.spec.ts +++ b/libs/admin-console/src/common/collections/models/collection.spec.ts @@ -1,15 +1,17 @@ import { MockProxy, mock } from "jest-mock-extended"; +import { + CollectionDetailsResponse, + Collection, + CollectionTypes, + CollectionData, +} from "@bitwarden/common/admin-console/models/collections"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string"; import { makeSymmetricCryptoKey } from "@bitwarden/common/spec"; import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid"; import { OrgKey } from "@bitwarden/common/types/key"; -import { Collection, CollectionTypes } from "./collection"; -import { CollectionData } from "./collection.data"; -import { CollectionDetailsResponse } from "./collection.response"; - describe("Collection", () => { let data: CollectionData; let encService: MockProxy<EncryptService>; diff --git a/libs/admin-console/src/common/collections/models/index.ts b/libs/admin-console/src/common/collections/models/index.ts index d04ec663306..c36e48fbf98 100644 --- a/libs/admin-console/src/common/collections/models/index.ts +++ b/libs/admin-console/src/common/collections/models/index.ts @@ -1,9 +1,3 @@ export * from "./bulk-collection-access.request"; -export * from "./collection-access-selection.view"; -export * from "./collection-admin.view"; -export * from "./collection"; -export * from "./collection.data"; -export * from "./collection.view"; export * from "./collection.request"; -export * from "./collection.response"; export * from "./collection-with-id.request"; diff --git a/libs/admin-console/src/common/collections/services/collection.state.ts b/libs/admin-console/src/common/collections/services/collection.state.ts index 9ca6faac75b..9a96a7015b1 100644 --- a/libs/admin-console/src/common/collections/services/collection.state.ts +++ b/libs/admin-console/src/common/collections/services/collection.state.ts @@ -1,5 +1,6 @@ import { Jsonify } from "type-fest"; +import { CollectionView, CollectionData } from "@bitwarden/common/admin-console/models/collections"; import { COLLECTION_DISK, COLLECTION_MEMORY, @@ -7,8 +8,6 @@ import { } from "@bitwarden/common/platform/state"; import { CollectionId } from "@bitwarden/common/types/guid"; -import { CollectionData, CollectionView } from "../models"; - export const ENCRYPTED_COLLECTION_DATA_KEY = UserKeyDefinition.record<CollectionData, CollectionId>( COLLECTION_DISK, "collections", diff --git a/libs/admin-console/src/common/collections/services/default-collection-admin.service.ts b/libs/admin-console/src/common/collections/services/default-collection-admin.service.ts index ca797a0f9ae..f7f3274a648 100644 --- a/libs/admin-console/src/common/collections/services/default-collection-admin.service.ts +++ b/libs/admin-console/src/common/collections/services/default-collection-admin.service.ts @@ -5,6 +5,14 @@ import { getOrganizationById, OrganizationService, } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { + CollectionAccessSelectionView, + CollectionAdminView, + CollectionAccessDetailsResponse, + CollectionDetailsResponse, + CollectionResponse, + CollectionData, +} from "@bitwarden/common/admin-console/models/collections"; import { SelectionReadOnlyRequest } from "@bitwarden/common/admin-console/models/request/selection-read-only.request"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { CollectionId, OrganizationId, UserId } from "@bitwarden/common/types/guid"; @@ -13,13 +21,7 @@ import { KeyService } from "@bitwarden/key-management"; import { CollectionAdminService, CollectionService } from "../abstractions"; import { - CollectionData, - CollectionAccessDetailsResponse, - CollectionDetailsResponse, - CollectionResponse, BulkCollectionAccessRequest, - CollectionAccessSelectionView, - CollectionAdminView, BaseCollectionRequest, UpdateCollectionRequest, CreateCollectionRequest, diff --git a/libs/admin-console/src/common/collections/services/default-collection.service.spec.ts b/libs/admin-console/src/common/collections/services/default-collection.service.spec.ts index 2eaaa48594e..950b6a59dcd 100644 --- a/libs/admin-console/src/common/collections/services/default-collection.service.spec.ts +++ b/libs/admin-console/src/common/collections/services/default-collection.service.spec.ts @@ -1,6 +1,11 @@ import { mock, MockProxy } from "jest-mock-extended"; import { combineLatest, first, firstValueFrom, of, ReplaySubject, takeWhile } from "rxjs"; +import { + CollectionView, + CollectionTypes, + CollectionData, +} from "@bitwarden/common/admin-console/models/collections"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -18,8 +23,6 @@ import { OrgKey } from "@bitwarden/common/types/key"; import { newGuid } from "@bitwarden/guid"; import { KeyService } from "@bitwarden/key-management"; -import { CollectionData, CollectionTypes, CollectionView } from "../models"; - import { DECRYPTED_COLLECTION_DATA_KEY, ENCRYPTED_COLLECTION_DATA_KEY } from "./collection.state"; import { DefaultCollectionService } from "./default-collection.service"; diff --git a/libs/admin-console/src/common/collections/services/default-collection.service.ts b/libs/admin-console/src/common/collections/services/default-collection.service.ts index ccc2e6f0de5..9519e39504e 100644 --- a/libs/admin-console/src/common/collections/services/default-collection.service.ts +++ b/libs/admin-console/src/common/collections/services/default-collection.service.ts @@ -12,6 +12,11 @@ import { switchMap, } from "rxjs"; +import { + CollectionView, + Collection, + CollectionData, +} from "@bitwarden/common/admin-console/models/collections"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; @@ -23,7 +28,6 @@ import { ServiceUtils } from "@bitwarden/common/vault/service-utils"; import { KeyService } from "@bitwarden/key-management"; import { CollectionService } from "../abstractions/collection.service"; -import { Collection, CollectionData, CollectionView } from "../models"; import { DECRYPTED_COLLECTION_DATA_KEY, ENCRYPTED_COLLECTION_DATA_KEY } from "./collection.state"; diff --git a/libs/angular/src/vault/abstractions/deprecated-vault-filter.service.ts b/libs/angular/src/vault/abstractions/deprecated-vault-filter.service.ts index 30a4c6d4739..a499003b42b 100644 --- a/libs/angular/src/vault/abstractions/deprecated-vault-filter.service.ts +++ b/libs/angular/src/vault/abstractions/deprecated-vault-filter.service.ts @@ -1,8 +1,6 @@ import { Observable } from "rxjs"; -// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. -// eslint-disable-next-line no-restricted-imports -import { CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { UserId } from "@bitwarden/common/types/guid"; import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; diff --git a/libs/angular/src/vault/vault-filter/components/collection-filter.component.ts b/libs/angular/src/vault/vault-filter/components/collection-filter.component.ts index 4d4037a3517..dfb6069f130 100644 --- a/libs/angular/src/vault/vault-filter/components/collection-filter.component.ts +++ b/libs/angular/src/vault/vault-filter/components/collection-filter.component.ts @@ -2,9 +2,10 @@ // @ts-strict-ignore import { Directive, EventEmitter, Input, Output } from "@angular/core"; -// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. -// eslint-disable-next-line no-restricted-imports -import { CollectionTypes, CollectionView } from "@bitwarden/admin-console/common"; +import { + CollectionView, + CollectionTypes, +} from "@bitwarden/common/admin-console/models/collections"; import { ITreeNodeObject } from "@bitwarden/common/vault/models/domain/tree-node"; import { DynamicTreeNode } from "../models/dynamic-tree-node.model"; diff --git a/libs/angular/src/vault/vault-filter/components/vault-filter.component.ts b/libs/angular/src/vault/vault-filter/components/vault-filter.component.ts index f664cff2e8d..8886f93233f 100644 --- a/libs/angular/src/vault/vault-filter/components/vault-filter.component.ts +++ b/libs/angular/src/vault/vault-filter/components/vault-filter.component.ts @@ -3,9 +3,7 @@ import { Directive, EventEmitter, Input, OnInit, Output } from "@angular/core"; import { firstValueFrom, Observable } from "rxjs"; -// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. -// eslint-disable-next-line no-restricted-imports -import { CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; diff --git a/libs/angular/src/vault/vault-filter/services/vault-filter.service.ts b/libs/angular/src/vault/vault-filter/services/vault-filter.service.ts index 6777da7a9e5..9b34890cbce 100644 --- a/libs/angular/src/vault/vault-filter/services/vault-filter.service.ts +++ b/libs/angular/src/vault/vault-filter/services/vault-filter.service.ts @@ -3,14 +3,14 @@ import { firstValueFrom, from, map, mergeMap, Observable, switchMap, take } from // This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. // eslint-disable-next-line no-restricted-imports -import { - CollectionService, - CollectionTypes, - CollectionView, -} from "@bitwarden/admin-console/common"; +import { CollectionService } from "@bitwarden/admin-console/common"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; +import { + CollectionView, + CollectionTypes, +} from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; diff --git a/libs/common/src/abstractions/api.service.ts b/libs/common/src/abstractions/api.service.ts index 7e4ff031ef2..afca5b63703 100644 --- a/libs/common/src/abstractions/api.service.ts +++ b/libs/common/src/abstractions/api.service.ts @@ -1,12 +1,11 @@ // This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. // eslint-disable-next-line no-restricted-imports +import { CreateCollectionRequest, UpdateCollectionRequest } from "@bitwarden/admin-console/common"; import { CollectionAccessDetailsResponse, CollectionDetailsResponse, CollectionResponse, - CreateCollectionRequest, - UpdateCollectionRequest, -} from "@bitwarden/admin-console/common"; +} from "@bitwarden/common/admin-console/models/collections"; import { OrganizationConnectionType } from "../admin-console/enums"; import { OrganizationSponsorshipCreateRequest } from "../admin-console/models/request/organization/organization-sponsorship-create.request"; diff --git a/libs/admin-console/src/common/collections/models/collection-access-selection.view.ts b/libs/common/src/admin-console/models/collections/collection-access-selection.view.ts similarity index 100% rename from libs/admin-console/src/common/collections/models/collection-access-selection.view.ts rename to libs/common/src/admin-console/models/collections/collection-access-selection.view.ts diff --git a/libs/admin-console/src/common/collections/models/collection-admin.view.ts b/libs/common/src/admin-console/models/collections/collection-admin.view.ts similarity index 98% rename from libs/admin-console/src/common/collections/models/collection-admin.view.ts rename to libs/common/src/admin-console/models/collections/collection-admin.view.ts index d5effaad3aa..65486136922 100644 --- a/libs/admin-console/src/common/collections/models/collection-admin.view.ts +++ b/libs/common/src/admin-console/models/collections/collection-admin.view.ts @@ -1,9 +1,9 @@ +import { CollectionAccessSelectionView } from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string"; import { OrgKey } from "@bitwarden/common/types/key"; -import { CollectionAccessSelectionView } from "./collection-access-selection.view"; import { CollectionAccessDetailsResponse, CollectionResponse } from "./collection.response"; import { CollectionView } from "./collection.view"; diff --git a/libs/admin-console/src/common/collections/models/collection.data.ts b/libs/common/src/admin-console/models/collections/collection.data.ts similarity index 89% rename from libs/admin-console/src/common/collections/models/collection.data.ts rename to libs/common/src/admin-console/models/collections/collection.data.ts index a783a3c9ab1..ad67615d068 100644 --- a/libs/admin-console/src/common/collections/models/collection.data.ts +++ b/libs/common/src/admin-console/models/collections/collection.data.ts @@ -1,10 +1,12 @@ import { Jsonify } from "type-fest"; +import { + CollectionDetailsResponse, + CollectionType, + CollectionTypes, +} from "@bitwarden/common/admin-console/models/collections"; import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid"; -import { CollectionType, CollectionTypes } from "./collection"; -import { CollectionDetailsResponse } from "./collection.response"; - export class CollectionData { id: CollectionId; organizationId: OrganizationId; diff --git a/libs/admin-console/src/common/collections/models/collection.response.ts b/libs/common/src/admin-console/models/collections/collection.response.ts similarity index 95% rename from libs/admin-console/src/common/collections/models/collection.response.ts rename to libs/common/src/admin-console/models/collections/collection.response.ts index e6722635984..134e4c8d56d 100644 --- a/libs/admin-console/src/common/collections/models/collection.response.ts +++ b/libs/common/src/admin-console/models/collections/collection.response.ts @@ -1,9 +1,11 @@ +import { + CollectionType, + CollectionTypes, +} from "@bitwarden/common/admin-console/models/collections"; import { SelectionReadOnlyResponse } from "@bitwarden/common/admin-console/models/response/selection-read-only.response"; import { BaseResponse } from "@bitwarden/common/models/response/base.response"; import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid"; -import { CollectionType, CollectionTypes } from "./collection"; - export class CollectionResponse extends BaseResponse { id: CollectionId; organizationId: OrganizationId; diff --git a/libs/admin-console/src/common/collections/models/collection.ts b/libs/common/src/admin-console/models/collections/collection.ts similarity index 97% rename from libs/admin-console/src/common/collections/models/collection.ts rename to libs/common/src/admin-console/models/collections/collection.ts index cf5573b8f4f..24c4d882732 100644 --- a/libs/admin-console/src/common/collections/models/collection.ts +++ b/libs/common/src/admin-console/models/collections/collection.ts @@ -1,3 +1,4 @@ +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string"; import Domain from "@bitwarden/common/platform/models/domain/domain-base"; @@ -5,7 +6,6 @@ import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid"; import { OrgKey } from "@bitwarden/common/types/key"; import { CollectionData } from "./collection.data"; -import { CollectionView } from "./collection.view"; export const CollectionTypes = { SharedCollection: 0, diff --git a/libs/admin-console/src/common/collections/models/collection.view.ts b/libs/common/src/admin-console/models/collections/collection.view.ts similarity index 100% rename from libs/admin-console/src/common/collections/models/collection.view.ts rename to libs/common/src/admin-console/models/collections/collection.view.ts diff --git a/libs/common/src/admin-console/models/collections/index.ts b/libs/common/src/admin-console/models/collections/index.ts new file mode 100644 index 00000000000..74b92715eb0 --- /dev/null +++ b/libs/common/src/admin-console/models/collections/index.ts @@ -0,0 +1,6 @@ +export * from "./collection-access-selection.view"; +export * from "./collection-admin.view"; +export * from "./collection.view"; +export * from "./collection.response"; +export * from "./collection"; +export * from "./collection.data"; diff --git a/libs/common/src/admin-console/models/response/organization-export.response.ts b/libs/common/src/admin-console/models/response/organization-export.response.ts index 19a8dd9ad94..9666bca839a 100644 --- a/libs/common/src/admin-console/models/response/organization-export.response.ts +++ b/libs/common/src/admin-console/models/response/organization-export.response.ts @@ -1,8 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. -// eslint-disable-next-line no-restricted-imports -import { CollectionResponse } from "@bitwarden/admin-console/common"; +import { CollectionResponse } from "@bitwarden/common/admin-console/models/collections"; import { BaseResponse } from "../../../models/response/base.response"; import { CipherResponse } from "../../../vault/models/response/cipher.response"; diff --git a/libs/common/src/admin-console/utils/collection-utils.spec.ts b/libs/common/src/admin-console/utils/collection-utils.spec.ts index 19360e1a87b..e6aa1b96d54 100644 --- a/libs/common/src/admin-console/utils/collection-utils.spec.ts +++ b/libs/common/src/admin-console/utils/collection-utils.spec.ts @@ -1,5 +1,4 @@ -// eslint-disable-next-line no-restricted-imports -import { CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; import { newGuid } from "@bitwarden/guid"; diff --git a/libs/common/src/admin-console/utils/collection-utils.ts b/libs/common/src/admin-console/utils/collection-utils.ts index 900821510bf..e4ad05c0798 100644 --- a/libs/common/src/admin-console/utils/collection-utils.ts +++ b/libs/common/src/admin-console/utils/collection-utils.ts @@ -1,11 +1,10 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -// eslint-disable-next-line no-restricted-imports import { - CollectionAdminView, CollectionView, NestingDelimiter, -} from "@bitwarden/admin-console/common"; + CollectionAdminView, +} from "@bitwarden/common/admin-console/models/collections"; import { OrganizationId } from "@bitwarden/common/types/guid"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; import { ServiceUtils } from "@bitwarden/common/vault/service-utils"; diff --git a/libs/common/src/models/export/collection-with-id.export.ts b/libs/common/src/models/export/collection-with-id.export.ts index 9a372fbdfa9..eeb21d1b6d5 100644 --- a/libs/common/src/models/export/collection-with-id.export.ts +++ b/libs/common/src/models/export/collection-with-id.export.ts @@ -1,8 +1,9 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. -// eslint-disable-next-line no-restricted-imports -import { Collection as CollectionDomain, CollectionView } from "@bitwarden/admin-console/common"; +import { + CollectionView, + Collection as CollectionDomain, +} from "@bitwarden/common/admin-console/models/collections"; import { CollectionId } from "@bitwarden/common/types/guid"; import { CollectionExport } from "./collection.export"; diff --git a/libs/common/src/models/export/collection.export.ts b/libs/common/src/models/export/collection.export.ts index 631b31d8b7b..e02ae5fab49 100644 --- a/libs/common/src/models/export/collection.export.ts +++ b/libs/common/src/models/export/collection.export.ts @@ -1,8 +1,9 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. -// eslint-disable-next-line no-restricted-imports -import { Collection as CollectionDomain, CollectionView } from "@bitwarden/admin-console/common"; +import { + CollectionView, + Collection as CollectionDomain, +} from "@bitwarden/common/admin-console/models/collections"; import { EncString } from "../../key-management/crypto/models/enc-string"; import { CollectionId, emptyGuid, OrganizationId } from "../../types/guid"; diff --git a/libs/common/src/platform/sync/default-sync.service.ts b/libs/common/src/platform/sync/default-sync.service.ts index fdd05927b50..3c8f6e57e1e 100644 --- a/libs/common/src/platform/sync/default-sync.service.ts +++ b/libs/common/src/platform/sync/default-sync.service.ts @@ -4,11 +4,11 @@ import { firstValueFrom, map } from "rxjs"; // This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. // eslint-disable-next-line no-restricted-imports +import { CollectionService } from "@bitwarden/admin-console/common"; import { - CollectionData, CollectionDetailsResponse, - CollectionService, -} from "@bitwarden/admin-console/common"; + CollectionData, +} from "@bitwarden/common/admin-console/models/collections"; import { AccountCryptographicStateService } from "@bitwarden/common/key-management/account-cryptography/account-cryptographic-state.service"; import { SecurityStateService } from "@bitwarden/common/key-management/security-state/abstractions/security-state.service"; // This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. diff --git a/libs/common/src/platform/sync/sync.response.ts b/libs/common/src/platform/sync/sync.response.ts index 27b1145752b..bf378fe9aaf 100644 --- a/libs/common/src/platform/sync/sync.response.ts +++ b/libs/common/src/platform/sync/sync.response.ts @@ -1,6 +1,4 @@ -// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. -// eslint-disable-next-line no-restricted-imports -import { CollectionDetailsResponse } from "@bitwarden/admin-console/common"; +import { CollectionDetailsResponse } from "@bitwarden/common/admin-console/models/collections"; import { PolicyResponse } from "../../admin-console/models/response/policy.response"; import { UserDecryptionResponse } from "../../key-management/models/response/user-decryption.response"; diff --git a/libs/common/src/services/api.service.ts b/libs/common/src/services/api.service.ts index 8839ea8ca50..33e251f6411 100644 --- a/libs/common/src/services/api.service.ts +++ b/libs/common/src/services/api.service.ts @@ -4,16 +4,15 @@ import { firstValueFrom, map } from "rxjs"; // This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. // eslint-disable-next-line no-restricted-imports +import { CreateCollectionRequest, UpdateCollectionRequest } from "@bitwarden/admin-console/common"; +// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. +// eslint-disable-next-line no-restricted-imports +import { LogoutReason } from "@bitwarden/auth/common"; import { CollectionAccessDetailsResponse, CollectionDetailsResponse, CollectionResponse, - CreateCollectionRequest, - UpdateCollectionRequest, -} from "@bitwarden/admin-console/common"; -// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. -// eslint-disable-next-line no-restricted-imports -import { LogoutReason } from "@bitwarden/auth/common"; +} from "@bitwarden/common/admin-console/models/collections"; import { ApiService as ApiServiceAbstraction } from "../abstractions/api.service"; import { OrganizationConnectionType } from "../admin-console/enums"; diff --git a/libs/common/src/vault/services/cipher-authorization.service.spec.ts b/libs/common/src/vault/services/cipher-authorization.service.spec.ts index 78fe6f18913..f1cc8743492 100644 --- a/libs/common/src/vault/services/cipher-authorization.service.spec.ts +++ b/libs/common/src/vault/services/cipher-authorization.service.spec.ts @@ -3,8 +3,9 @@ import { Observable, firstValueFrom, of } from "rxjs"; // This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. // eslint-disable-next-line no-restricted-imports -import { CollectionService, CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionService } from "@bitwarden/admin-console/common"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { UserId } from "@bitwarden/common/types/guid"; diff --git a/libs/importer/src/components/import.component.ts b/libs/importer/src/components/import.component.ts index 0ff62b00e78..d58859ac163 100644 --- a/libs/importer/src/components/import.component.ts +++ b/libs/importer/src/components/import.component.ts @@ -29,15 +29,15 @@ import { combineLatestWith, filter, map, switchMap, takeUntil } from "rxjs/opera // This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. // eslint-disable-next-line no-restricted-imports -import { - CollectionService, - CollectionTypes, - CollectionView, -} from "@bitwarden/admin-console/common"; +import { CollectionService } from "@bitwarden/admin-console/common"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; +import { + CollectionView, + CollectionTypes, +} from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; diff --git a/libs/importer/src/importers/base-importer.ts b/libs/importer/src/importers/base-importer.ts index 7196a83783a..a32a53f3e60 100644 --- a/libs/importer/src/importers/base-importer.ts +++ b/libs/importer/src/importers/base-importer.ts @@ -2,9 +2,7 @@ // @ts-strict-ignore import * as papa from "papaparse"; -// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. -// eslint-disable-next-line no-restricted-imports -import { Collection, CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionView, Collection } from "@bitwarden/common/admin-console/models/collections"; import { normalizeExpiryYearFormat } from "@bitwarden/common/autofill/utils"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; diff --git a/libs/importer/src/importers/bitwarden/bitwarden-encrypted-json-importer.ts b/libs/importer/src/importers/bitwarden/bitwarden-encrypted-json-importer.ts index 4771f47b4c9..5d165d9a76d 100644 --- a/libs/importer/src/importers/bitwarden/bitwarden-encrypted-json-importer.ts +++ b/libs/importer/src/importers/bitwarden/bitwarden-encrypted-json-importer.ts @@ -2,9 +2,7 @@ // @ts-strict-ignore import { filter, firstValueFrom } from "rxjs"; -// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. -// eslint-disable-next-line no-restricted-imports -import { Collection } from "@bitwarden/admin-console/common"; +import { Collection } from "@bitwarden/common/admin-console/models/collections"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string"; diff --git a/libs/importer/src/importers/padlock-csv-importer.ts b/libs/importer/src/importers/padlock-csv-importer.ts index ec781170c4d..4554cd3a9be 100644 --- a/libs/importer/src/importers/padlock-csv-importer.ts +++ b/libs/importer/src/importers/padlock-csv-importer.ts @@ -1,8 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. -// eslint-disable-next-line no-restricted-imports -import { CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { ImportResult } from "../models/import-result"; diff --git a/libs/importer/src/importers/passpack-csv-importer.ts b/libs/importer/src/importers/passpack-csv-importer.ts index 09ff841b8a4..2cc7135f046 100644 --- a/libs/importer/src/importers/passpack-csv-importer.ts +++ b/libs/importer/src/importers/passpack-csv-importer.ts @@ -1,6 +1,4 @@ -// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. -// eslint-disable-next-line no-restricted-imports -import { CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { ImportResult } from "../models/import-result"; diff --git a/libs/importer/src/importers/password-depot/password-depot-17-xml-importer.spec.ts b/libs/importer/src/importers/password-depot/password-depot-17-xml-importer.spec.ts index 1f14d05c51e..8b78b33c154 100644 --- a/libs/importer/src/importers/password-depot/password-depot-17-xml-importer.spec.ts +++ b/libs/importer/src/importers/password-depot/password-depot-17-xml-importer.spec.ts @@ -1,6 +1,4 @@ -// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. -// eslint-disable-next-line no-restricted-imports -import { CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { OrganizationId } from "@bitwarden/common/types/guid"; import { FieldType, SecureNoteType } from "@bitwarden/common/vault/enums"; import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; diff --git a/libs/importer/src/models/import-result.ts b/libs/importer/src/models/import-result.ts index b99068ff83f..cefc80be61d 100644 --- a/libs/importer/src/models/import-result.ts +++ b/libs/importer/src/models/import-result.ts @@ -1,8 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. -// eslint-disable-next-line no-restricted-imports -import { CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; diff --git a/libs/importer/src/services/import-collection.service.abstraction.ts b/libs/importer/src/services/import-collection.service.abstraction.ts index f74d556b897..1dae6411ac8 100644 --- a/libs/importer/src/services/import-collection.service.abstraction.ts +++ b/libs/importer/src/services/import-collection.service.abstraction.ts @@ -1,8 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. -// eslint-disable-next-line no-restricted-imports -import { CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { UserId } from "@bitwarden/user-core"; export abstract class ImportCollectionServiceAbstraction { diff --git a/libs/importer/src/services/import.service.abstraction.ts b/libs/importer/src/services/import.service.abstraction.ts index d8f1f6ccd5c..51867212689 100644 --- a/libs/importer/src/services/import.service.abstraction.ts +++ b/libs/importer/src/services/import.service.abstraction.ts @@ -1,9 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore - -// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. -// eslint-disable-next-line no-restricted-imports -import { CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; import { Importer } from "../importers/importer"; diff --git a/libs/importer/src/services/import.service.spec.ts b/libs/importer/src/services/import.service.spec.ts index b82772669de..33a1e47a4ce 100644 --- a/libs/importer/src/services/import.service.spec.ts +++ b/libs/importer/src/services/import.service.spec.ts @@ -2,11 +2,11 @@ import { mock, MockProxy } from "jest-mock-extended"; // This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. // eslint-disable-next-line no-restricted-imports +import { CollectionService } from "@bitwarden/admin-console/common"; import { - CollectionService, - CollectionTypes, CollectionView, -} from "@bitwarden/admin-console/common"; + CollectionTypes, +} from "@bitwarden/common/admin-console/models/collections"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { KeyGenerationService } from "@bitwarden/common/key-management/crypto"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; diff --git a/libs/importer/src/services/import.service.ts b/libs/importer/src/services/import.service.ts index 829bd04e994..bd0a9eb0d4d 100644 --- a/libs/importer/src/services/import.service.ts +++ b/libs/importer/src/services/import.service.ts @@ -4,12 +4,11 @@ import { firstValueFrom, map } from "rxjs"; // This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. // eslint-disable-next-line no-restricted-imports +import { CollectionService, CollectionWithIdRequest } from "@bitwarden/admin-console/common"; import { - CollectionService, - CollectionWithIdRequest, CollectionView, CollectionTypes, -} from "@bitwarden/admin-console/common"; +} from "@bitwarden/common/admin-console/models/collections"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { KeyGenerationService } from "@bitwarden/common/key-management/crypto"; diff --git a/libs/tools/export/vault-export/vault-export-core/src/services/org-vault-export.service.ts b/libs/tools/export/vault-export/vault-export-core/src/services/org-vault-export.service.ts index 8d5178e0e0c..c3df13c7945 100644 --- a/libs/tools/export/vault-export/vault-export-core/src/services/org-vault-export.service.ts +++ b/libs/tools/export/vault-export/vault-export-core/src/services/org-vault-export.service.ts @@ -3,13 +3,13 @@ import * as papa from "papaparse"; import { filter, firstValueFrom, map } from "rxjs"; +import { CollectionService } from "@bitwarden/admin-console/common"; import { - CollectionService, - CollectionData, - Collection, - CollectionDetailsResponse, CollectionView, -} from "@bitwarden/admin-console/common"; + CollectionDetailsResponse, + Collection, + CollectionData, +} from "@bitwarden/common/admin-console/models/collections"; import { KeyGenerationService } from "@bitwarden/common/key-management/crypto"; import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; diff --git a/libs/vault/src/abstractions/vault-filter.service.ts b/libs/vault/src/abstractions/vault-filter.service.ts index b3f9c118555..22a1cf01cee 100644 --- a/libs/vault/src/abstractions/vault-filter.service.ts +++ b/libs/vault/src/abstractions/vault-filter.service.ts @@ -2,8 +2,10 @@ // @ts-strict-ignore import { Observable } from "rxjs"; -// eslint-disable-next-line no-restricted-imports -import { CollectionAdminView, CollectionView } from "@bitwarden/admin-console/common"; +import { + CollectionAdminView, + CollectionView, +} from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { UserId } from "@bitwarden/common/types/guid"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; diff --git a/libs/vault/src/cipher-form/abstractions/cipher-form-config.service.ts b/libs/vault/src/cipher-form/abstractions/cipher-form-config.service.ts index d1792da422c..35d3d8725ff 100644 --- a/libs/vault/src/cipher-form/abstractions/cipher-form-config.service.ts +++ b/libs/vault/src/cipher-form/abstractions/cipher-form-config.service.ts @@ -1,6 +1,4 @@ -// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. -// eslint-disable-next-line no-restricted-imports -import { CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { CipherId, CollectionId, OrganizationId } from "@bitwarden/common/types/guid"; import { CipherType } from "@bitwarden/common/vault/enums"; diff --git a/libs/vault/src/cipher-form/cipher-form.stories.ts b/libs/vault/src/cipher-form/cipher-form.stories.ts index e732513913d..475a026ff8b 100644 --- a/libs/vault/src/cipher-form/cipher-form.stories.ts +++ b/libs/vault/src/cipher-form/cipher-form.stories.ts @@ -12,14 +12,12 @@ import { import { BehaviorSubject, of } from "rxjs"; import { action } from "storybook/actions"; -// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. -// eslint-disable-next-line no-restricted-imports -import { CollectionView } from "@bitwarden/admin-console/common"; import { ViewCacheService } from "@bitwarden/angular/platform/view-cache"; import { NudgeStatus, NudgesService } from "@bitwarden/angular/vault"; import { AuditService } from "@bitwarden/common/abstractions/audit.service"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service"; diff --git a/libs/vault/src/cipher-form/components/item-details/item-details-section.component.spec.ts b/libs/vault/src/cipher-form/components/item-details/item-details-section.component.spec.ts index 62f23e77ec2..61ba2d19f8f 100644 --- a/libs/vault/src/cipher-form/components/item-details/item-details-section.component.spec.ts +++ b/libs/vault/src/cipher-form/components/item-details/item-details-section.component.spec.ts @@ -5,11 +5,13 @@ import { By } from "@angular/platform-browser"; import { mock, MockProxy } from "jest-mock-extended"; import { BehaviorSubject, of } from "rxjs"; -// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. -// eslint-disable-next-line no-restricted-imports -import { CollectionType, CollectionTypes, CollectionView } from "@bitwarden/admin-console/common"; import { ClientType } from "@bitwarden/client-type"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { + CollectionView, + CollectionType, + CollectionTypes, +} from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { Policy } from "@bitwarden/common/admin-console/models/domain/policy"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; diff --git a/libs/vault/src/cipher-form/components/item-details/item-details-section.component.ts b/libs/vault/src/cipher-form/components/item-details/item-details-section.component.ts index 6dd2dafe5e8..4985f777a0e 100644 --- a/libs/vault/src/cipher-form/components/item-details/item-details-section.component.ts +++ b/libs/vault/src/cipher-form/components/item-details/item-details-section.component.ts @@ -6,13 +6,14 @@ import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { FormBuilder, FormControl, ReactiveFormsModule, Validators } from "@angular/forms"; import { concatMap, distinctUntilChanged, firstValueFrom, map } from "rxjs"; -// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. -// eslint-disable-next-line no-restricted-imports -import { CollectionTypes, CollectionView } from "@bitwarden/admin-console/common"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { ClientType } from "@bitwarden/client-type"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { OrganizationUserType, PolicyType } from "@bitwarden/common/admin-console/enums"; +import { + CollectionView, + CollectionTypes, +} from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; diff --git a/libs/vault/src/cipher-view/cipher-view.component.ts b/libs/vault/src/cipher-view/cipher-view.component.ts index d5adb0b71a0..b5c063df51e 100644 --- a/libs/vault/src/cipher-view/cipher-view.component.ts +++ b/libs/vault/src/cipher-view/cipher-view.component.ts @@ -5,9 +5,10 @@ import { combineLatest, of, switchMap, map, catchError, from, Observable, startW // This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. // eslint-disable-next-line no-restricted-imports -import { CollectionService, CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionService } from "@bitwarden/admin-console/common"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { isCardExpired } from "@bitwarden/common/autofill/utils"; diff --git a/libs/vault/src/cipher-view/item-details/item-details-v2.component.spec.ts b/libs/vault/src/cipher-view/item-details/item-details-v2.component.spec.ts index ae78c49bdb4..338632fef04 100644 --- a/libs/vault/src/cipher-view/item-details/item-details-v2.component.spec.ts +++ b/libs/vault/src/cipher-view/item-details/item-details-v2.component.spec.ts @@ -4,9 +4,7 @@ import { By } from "@angular/platform-browser"; import { mock, MockProxy } from "jest-mock-extended"; import { of } from "rxjs"; -// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. -// eslint-disable-next-line no-restricted-imports -import { CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service"; import { ClientType } from "@bitwarden/common/enums"; diff --git a/libs/vault/src/cipher-view/item-details/item-details-v2.component.ts b/libs/vault/src/cipher-view/item-details/item-details-v2.component.ts index 8132780ccf4..eb0e468fa4f 100644 --- a/libs/vault/src/cipher-view/item-details/item-details-v2.component.ts +++ b/libs/vault/src/cipher-view/item-details/item-details-v2.component.ts @@ -6,10 +6,12 @@ import { Component, computed, input, signal } from "@angular/core"; import { toSignal } from "@angular/core/rxjs-interop"; import { fromEvent, map, startWith } from "rxjs"; -// eslint-disable-next-line no-restricted-imports -import { CollectionTypes, CollectionView } from "@bitwarden/admin-console/common"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { ClientType } from "@bitwarden/client-type"; +import { + CollectionView, + CollectionTypes, +} from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; diff --git a/libs/vault/src/components/assign-collections.component.spec.ts b/libs/vault/src/components/assign-collections.component.spec.ts index 414613e67d8..2dc13fef3f6 100644 --- a/libs/vault/src/components/assign-collections.component.spec.ts +++ b/libs/vault/src/components/assign-collections.component.spec.ts @@ -5,12 +5,12 @@ import { of } from "rxjs"; // This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. // eslint-disable-next-line no-restricted-imports -import { - CollectionService, - CollectionTypes, - CollectionView, -} from "@bitwarden/admin-console/common"; +import { CollectionService } from "@bitwarden/admin-console/common"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { + CollectionView, + CollectionTypes, +} from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { ProductTierType } from "@bitwarden/common/billing/enums"; diff --git a/libs/vault/src/components/assign-collections.component.ts b/libs/vault/src/components/assign-collections.component.ts index f0ce59b0c3c..0d04dd3ab32 100644 --- a/libs/vault/src/components/assign-collections.component.ts +++ b/libs/vault/src/components/assign-collections.component.ts @@ -26,17 +26,17 @@ import { // This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. // eslint-disable-next-line no-restricted-imports -import { - CollectionService, - CollectionTypes, - CollectionView, -} from "@bitwarden/admin-console/common"; +import { CollectionService } from "@bitwarden/admin-console/common"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { getOrganizationById, OrganizationService, } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { OrganizationUserStatusType } from "@bitwarden/common/admin-console/enums"; +import { + CollectionView, + CollectionTypes, +} from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; diff --git a/libs/vault/src/models/filter-function.spec.ts b/libs/vault/src/models/filter-function.spec.ts index 36bc6e683b5..1ffc1b119a8 100644 --- a/libs/vault/src/models/filter-function.spec.ts +++ b/libs/vault/src/models/filter-function.spec.ts @@ -1,7 +1,4 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -// eslint-disable-next-line no-restricted-imports -import { Unassigned } from "@bitwarden/admin-console/common"; +import { Unassigned } from "@bitwarden/common/admin-console/models/collections"; import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid"; import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; diff --git a/libs/vault/src/models/filter-function.ts b/libs/vault/src/models/filter-function.ts index d32c63eced4..0252ef13094 100644 --- a/libs/vault/src/models/filter-function.ts +++ b/libs/vault/src/models/filter-function.ts @@ -1,5 +1,4 @@ -// eslint-disable-next-line no-restricted-imports -import { Unassigned } from "@bitwarden/admin-console/common"; +import { Unassigned } from "@bitwarden/common/admin-console/models/collections"; import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherViewLike, diff --git a/libs/vault/src/models/routed-vault-filter-bridge.model.ts b/libs/vault/src/models/routed-vault-filter-bridge.model.ts index d2df82e0053..1d6d73ba7c5 100644 --- a/libs/vault/src/models/routed-vault-filter-bridge.model.ts +++ b/libs/vault/src/models/routed-vault-filter-bridge.model.ts @@ -1,5 +1,4 @@ -// eslint-disable-next-line no-restricted-imports -import { Unassigned } from "@bitwarden/admin-console/common"; +import { Unassigned } from "@bitwarden/common/admin-console/models/collections"; import { CollectionId } from "@bitwarden/common/types/guid"; import { CipherType } from "@bitwarden/common/vault/enums"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; diff --git a/libs/vault/src/models/routed-vault-filter.model.ts b/libs/vault/src/models/routed-vault-filter.model.ts index 9c6801dd449..ddc1689b61f 100644 --- a/libs/vault/src/models/routed-vault-filter.model.ts +++ b/libs/vault/src/models/routed-vault-filter.model.ts @@ -1,5 +1,4 @@ -// eslint-disable-next-line no-restricted-imports -import { Unassigned } from "@bitwarden/admin-console/common"; +import { Unassigned } from "@bitwarden/common/admin-console/models/collections"; import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid"; /** diff --git a/libs/vault/src/models/vault-filter.model.spec.ts b/libs/vault/src/models/vault-filter.model.spec.ts index 82296f8c836..6f90f5487bb 100644 --- a/libs/vault/src/models/vault-filter.model.spec.ts +++ b/libs/vault/src/models/vault-filter.model.spec.ts @@ -1,7 +1,4 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -// eslint-disable-next-line no-restricted-imports -import { CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid"; import { CipherType } from "@bitwarden/common/vault/enums"; diff --git a/libs/vault/src/models/vault-filter.type.ts b/libs/vault/src/models/vault-filter.type.ts index 764e7ea0ff9..14e01ba0735 100644 --- a/libs/vault/src/models/vault-filter.type.ts +++ b/libs/vault/src/models/vault-filter.type.ts @@ -1,5 +1,4 @@ -// eslint-disable-next-line no-restricted-imports -import { CollectionAdminView } from "@bitwarden/admin-console/common"; +import { CollectionAdminView } from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { CipherType } from "@bitwarden/common/vault/enums"; import { ITreeNodeObject } from "@bitwarden/common/vault/models/domain/tree-node"; diff --git a/libs/vault/src/services/default-vault-items-transfer.service.spec.ts b/libs/vault/src/services/default-vault-items-transfer.service.spec.ts index c0afa950c41..4ad9c53c6f0 100644 --- a/libs/vault/src/services/default-vault-items-transfer.service.spec.ts +++ b/libs/vault/src/services/default-vault-items-transfer.service.spec.ts @@ -2,11 +2,12 @@ import { mock, MockProxy } from "jest-mock-extended"; import { firstValueFrom, of, Subject } from "rxjs"; // eslint-disable-next-line no-restricted-imports -import { CollectionService, CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionService } from "@bitwarden/admin-console/common"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { Policy } from "@bitwarden/common/admin-console/models/domain/policy"; import { EventType } from "@bitwarden/common/enums"; diff --git a/libs/vault/src/services/routed-vault-filter-bridge.service.ts b/libs/vault/src/services/routed-vault-filter-bridge.service.ts index ac7b8921fed..1bff764964e 100644 --- a/libs/vault/src/services/routed-vault-filter-bridge.service.ts +++ b/libs/vault/src/services/routed-vault-filter-bridge.service.ts @@ -4,8 +4,7 @@ import { Injectable } from "@angular/core"; import { Router } from "@angular/router"; import { combineLatest, map, Observable } from "rxjs"; -// eslint-disable-next-line no-restricted-imports -import { Unassigned } from "@bitwarden/admin-console/common"; +import { Unassigned } from "@bitwarden/common/admin-console/models/collections"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; import { ServiceUtils } from "@bitwarden/common/vault/service-utils"; import { diff --git a/libs/vault/src/services/vault-filter.service.spec.ts b/libs/vault/src/services/vault-filter.service.spec.ts index 1c59c228357..90af45e571f 100644 --- a/libs/vault/src/services/vault-filter.service.spec.ts +++ b/libs/vault/src/services/vault-filter.service.spec.ts @@ -8,16 +8,16 @@ import { mock, MockProxy } from "jest-mock-extended"; import { firstValueFrom, of, ReplaySubject } from "rxjs"; // eslint-disable-next-line no-restricted-imports -import { - CollectionService, - CollectionType, - CollectionTypes, - CollectionView, -} from "@bitwarden/admin-console/common"; +import { CollectionService } from "@bitwarden/admin-console/common"; import * as vaultFilterSvc from "@bitwarden/angular/vault/vault-filter/services/vault-filter.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; +import { + CollectionView, + CollectionType, + CollectionTypes, +} from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; diff --git a/libs/vault/src/services/vault-filter.service.ts b/libs/vault/src/services/vault-filter.service.ts index 140a8d104e7..b21e140e023 100644 --- a/libs/vault/src/services/vault-filter.service.ts +++ b/libs/vault/src/services/vault-filter.service.ts @@ -13,15 +13,15 @@ import { } from "rxjs"; // eslint-disable-next-line no-restricted-imports -import { - CollectionService, - CollectionTypes, - CollectionView, -} from "@bitwarden/admin-console/common"; +import { CollectionService } from "@bitwarden/admin-console/common"; import { sortDefaultCollections } from "@bitwarden/angular/vault/vault-filter/services/vault-filter.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; +import { + CollectionView, + CollectionTypes, +} from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { cloneCollection } from "@bitwarden/common/admin-console/utils/collection-utils"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; From 5a35b9e339d141e39d009f4d4e8f7dd6c8089897 Mon Sep 17 00:00:00 2001 From: Leslie Xiong <lxiong@livefront.com> Date: Mon, 12 Jan 2026 23:54:31 -0500 Subject: [PATCH 56/56] fixed import --- apps/cli/src/commands/list.command.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/apps/cli/src/commands/list.command.ts b/apps/cli/src/commands/list.command.ts index ff210cf222d..2430035e34a 100644 --- a/apps/cli/src/commands/list.command.ts +++ b/apps/cli/src/commands/list.command.ts @@ -1,16 +1,15 @@ import { firstValueFrom, map } from "rxjs"; +import { OrganizationUserApiService, CollectionService } from "@bitwarden/admin-console/common"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; +import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { - OrganizationUserApiService, - CollectionService, CollectionData, Collection, CollectionDetailsResponse as ApiCollectionDetailsResponse, CollectionResponse as ApiCollectionResponse, -} from "@bitwarden/admin-console/common"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; -import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +} from "@bitwarden/common/admin-console/models/collections"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { EventType } from "@bitwarden/common/enums";