From c8ddaae6b34d1a450dd54678c1fc5300d3709c13 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Mon, 27 Oct 2025 13:11:29 +0100 Subject: [PATCH 01/18] [PM-27300] Update SDK to 357 (#17003) * Update sdk to 357 * Package.lock --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index c9abe11b585..8ce60e0826f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,7 @@ "@angular/platform-browser": "19.2.14", "@angular/platform-browser-dynamic": "19.2.14", "@angular/router": "19.2.14", - "@bitwarden/sdk-internal": "0.2.0-main.315", + "@bitwarden/sdk-internal": "0.2.0-main.357", "@electron/fuses": "1.8.0", "@emotion/css": "11.13.5", "@koa/multer": "4.0.0", @@ -4690,9 +4690,9 @@ "link": true }, "node_modules/@bitwarden/sdk-internal": { - "version": "0.2.0-main.315", - "resolved": "https://registry.npmjs.org/@bitwarden/sdk-internal/-/sdk-internal-0.2.0-main.315.tgz", - "integrity": "sha512-hdpFRLrDYSJ6+cNXfMyHdTgg/xIePIlEUSn4JWzwru4PvTcEkkFwGJM3L2LoUqTdNMiDQlr0UjDahopT+C2r0g==", + "version": "0.2.0-main.357", + "resolved": "https://registry.npmjs.org/@bitwarden/sdk-internal/-/sdk-internal-0.2.0-main.357.tgz", + "integrity": "sha512-qo8kCzrWNJP69HeI6WRyJMCFXYUJqLbaQCFoDgQkQa3ICrwpw5g9gW5y4P9FOa/DHdj8BgVbFGAX+YylbUb0/A==", "license": "GPL-3.0", "dependencies": { "type-fest": "^4.41.0" diff --git a/package.json b/package.json index 88cf2bda43c..89e127488b2 100644 --- a/package.json +++ b/package.json @@ -159,7 +159,7 @@ "@angular/platform-browser": "19.2.14", "@angular/platform-browser-dynamic": "19.2.14", "@angular/router": "19.2.14", - "@bitwarden/sdk-internal": "0.2.0-main.315", + "@bitwarden/sdk-internal": "0.2.0-main.357", "@electron/fuses": "1.8.0", "@emotion/css": "11.13.5", "@koa/multer": "4.0.0", From a6882c36b94b1a439a925fbd176582c70d1730fa Mon Sep 17 00:00:00 2001 From: cyprain-okeke <108260115+cyprain-okeke@users.noreply.github.com> Date: Mon, 27 Oct 2025 13:18:08 +0100 Subject: [PATCH 02/18] Resolve the redirect to subscription (#17017) --- .../organization-subscription-cloud.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.html b/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.html index 5fa10c4c87c..db3dde217c7 100644 --- a/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.html +++ b/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.html @@ -223,7 +223,7 @@

{{ "manageSubscription" | i18n }}

{{ "manageSubscriptionFromThe" | i18n }} - {{ + {{ "providerPortal" | i18n }}. From 43a1dfa46327d06d17eb4a23e030375b586255d0 Mon Sep 17 00:00:00 2001 From: Maciej Zieniuk <167752252+mzieniukbw@users.noreply.github.com> Date: Mon, 27 Oct 2025 13:40:56 +0100 Subject: [PATCH 03/18] icons and key connector urls for web development (#17043) --- apps/web/config/development.json | 1 + apps/web/config/selfhosted.json | 1 + apps/web/webpack.base.js | 7 +++++++ 3 files changed, 9 insertions(+) diff --git a/apps/web/config/development.json b/apps/web/config/development.json index 52a0fb0fdf2..6fd5fa49eb2 100644 --- a/apps/web/config/development.json +++ b/apps/web/config/development.json @@ -8,6 +8,7 @@ "proxyIdentity": "http://localhost:33656", "proxyEvents": "http://localhost:46273", "proxyNotifications": "http://localhost:61840", + "proxyIcons": "http://localhost:50024", "wsConnectSrc": "ws://localhost:61840" }, "additionalRegions": [ diff --git a/apps/web/config/selfhosted.json b/apps/web/config/selfhosted.json index cd36ab15c5e..ffb7621e594 100644 --- a/apps/web/config/selfhosted.json +++ b/apps/web/config/selfhosted.json @@ -4,6 +4,7 @@ "proxyIdentity": "http://localhost:33657", "proxyEvents": "http://localhost:46274", "proxyNotifications": "http://localhost:61841", + "proxyKeyConnector": "http://localhost:5000", "port": 8081 }, "flags": {} diff --git a/apps/web/webpack.base.js b/apps/web/webpack.base.js index 7930a55f61a..56fd6c7faf5 100644 --- a/apps/web/webpack.base.js +++ b/apps/web/webpack.base.js @@ -276,6 +276,13 @@ module.exports.buildConfig = function buildConfig(params) { secure: false, changeOrigin: true, }, + { + context: ["/key-connector"], + target: envConfig.dev?.proxyKeyConnector, + pathRewrite: { "^/key-connector": "" }, + secure: false, + changeOrigin: true, + }, ], headers: (req) => { if (!req.originalUrl.includes("connector.html")) { From b9f48d83b21734540340444c3c971eb3b5a7956c Mon Sep 17 00:00:00 2001 From: cyprain-okeke <108260115+cyprain-okeke@users.noreply.github.com> Date: Mon, 27 Oct 2025 13:53:05 +0100 Subject: [PATCH 04/18] [PM 25897] Copy and UI Tweaks for Payment Method Component (#16851) * Implement the Ui changes to align as expected * Align the Text in card number, expiration date and security code vertically * Change the Zip to ZIP * Remove readonly modifier from signal declarations --- apps/browser/src/_locales/en/messages.json | 6 ++++++ apps/desktop/src/locales/en/messages.json | 6 ++++++ .../payment/components/enter-billing-address.component.ts | 2 +- .../payment/components/enter-payment-method.component.ts | 6 +++--- .../billing/payment/components/payment-label.component.ts | 2 +- apps/web/src/app/billing/services/stripe.service.ts | 2 ++ apps/web/src/locales/en/messages.json | 6 ++++++ libs/common/src/vault/models/view/identity.view.ts | 2 +- .../cipher-form/components/identity/identity.component.html | 2 +- 9 files changed, 27 insertions(+), 7 deletions(-) diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 6a0e8c01c4d..29601bfa70c 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -5721,5 +5721,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json index 3e004e270a3..32545a0c1cd 100644 --- a/apps/desktop/src/locales/en/messages.json +++ b/apps/desktop/src/locales/en/messages.json @@ -4181,5 +4181,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/web/src/app/billing/payment/components/enter-billing-address.component.ts b/apps/web/src/app/billing/payment/components/enter-billing-address.component.ts index 40785e9b7ea..db95beea7f8 100644 --- a/apps/web/src/app/billing/payment/components/enter-billing-address.component.ts +++ b/apps/web/src/app/billing/payment/components/enter-billing-address.component.ts @@ -70,7 +70,7 @@ type Scenario =

- {{ "zipPostalCode" | i18n }} + {{ "zipPostalCodeLabel" | i18n }}
- {{ "number" | i18n }} + {{ "cardNumberLabel" | i18n }}
@@ -109,7 +109,7 @@ type PaymentMethodFormGroup = FormGroup<{ class="tw-border-none tw-bg-transparent tw-text-primary-600 tw-pr-1" [position]="'above-end'" > - +

{{ "cardSecurityCodeDescription" | i18n }}

@@ -217,7 +217,7 @@ type PaymentMethodFormGroup = FormGroup<{
- {{ "zipPostalCode" | i18n }} + {{ "zipPostalCodeLabel" | i18n }} - ({{ "required" | i18n }}) + ({{ "required" | i18n }})
`, diff --git a/apps/web/src/app/billing/services/stripe.service.ts b/apps/web/src/app/billing/services/stripe.service.ts index 7ea0d7d52c8..f7655ba0c6e 100644 --- a/apps/web/src/app/billing/services/stripe.service.ts +++ b/apps/web/src/app/billing/services/stripe.service.ts @@ -230,6 +230,8 @@ export class StripeService { '"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"', fontSize: "16px", fontSmoothing: "antialiased", + lineHeight: "1.5", + padding: "8px 12px", "::placeholder": { color: null, }, diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index f2cb9e5fd7b..f88af8aa1a7 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -11939,5 +11939,11 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/libs/common/src/vault/models/view/identity.view.ts b/libs/common/src/vault/models/view/identity.view.ts index 5fb0d1acba5..dca54fa04e8 100644 --- a/libs/common/src/vault/models/view/identity.view.ts +++ b/libs/common/src/vault/models/view/identity.view.ts @@ -23,7 +23,7 @@ export class IdentityView extends ItemView implements SdkIdentityView { city: string | undefined; @linkedFieldOption(LinkedId.State, { sortPosition: 16, i18nKey: "stateProvince" }) state: string | undefined; - @linkedFieldOption(LinkedId.PostalCode, { sortPosition: 17, i18nKey: "zipPostalCode" }) + @linkedFieldOption(LinkedId.PostalCode, { sortPosition: 17, i18nKey: "zipPostalCodeLabel" }) postalCode: string | undefined; @linkedFieldOption(LinkedId.Country, { sortPosition: 18 }) country: string | undefined; diff --git a/libs/vault/src/cipher-form/components/identity/identity.component.html b/libs/vault/src/cipher-form/components/identity/identity.component.html index 7f49bc21a10..2489977f63f 100644 --- a/libs/vault/src/cipher-form/components/identity/identity.component.html +++ b/libs/vault/src/cipher-form/components/identity/identity.component.html @@ -144,7 +144,7 @@ - {{ "zipPostalCode" | i18n }} + {{ "zipPostalCodeLabel" | i18n }} From 9d849d22341a61777dea6b417fbac9bfe077c33c Mon Sep 17 00:00:00 2001 From: neuronull <9162534+neuronull@users.noreply.github.com> Date: Mon, 27 Oct 2025 06:39:40 -0700 Subject: [PATCH 05/18] Convert `log` crate Records to `tracing` Events for desktop native. (#16827) * Convert `log` crate Records to `tracing` Events for desktop native. * sort deps * use the feature on tracing_subscriber --- apps/desktop/desktop_native/Cargo.toml | 2 +- apps/desktop/desktop_native/napi/src/lib.rs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/desktop/desktop_native/Cargo.toml b/apps/desktop/desktop_native/Cargo.toml index c0fe0b46f58..2168eaa0068 100644 --- a/apps/desktop/desktop_native/Cargo.toml +++ b/apps/desktop/desktop_native/Cargo.toml @@ -68,7 +68,7 @@ tokio = "=1.45.0" tokio-stream = "=0.1.15" tokio-util = "=0.7.13" tracing = "=0.1.41" -tracing-subscriber = { version = "=0.3.20", features = ["fmt", "env-filter"] } +tracing-subscriber = { version = "=0.3.20", features = ["fmt", "env-filter", "tracing-log"] } typenum = "=1.18.0" uniffi = "=0.28.3" widestring = "=1.2.0" diff --git a/apps/desktop/desktop_native/napi/src/lib.rs b/apps/desktop/desktop_native/napi/src/lib.rs index a193e44d6df..09f63f7854b 100644 --- a/apps/desktop/desktop_native/napi/src/lib.rs +++ b/apps/desktop/desktop_native/napi/src/lib.rs @@ -1051,6 +1051,10 @@ pub mod logging { // overriding the default directive for matching targets. .from_env_lossy(); + // With the `tracing-log` feature enabled for the `tracing_subscriber`, + // the registry below will initialize a log compatibility layer, which allows + // the subscriber to consume log::Records as though they were tracing Events. + // https://docs.rs/tracing-subscriber/latest/tracing_subscriber/util/trait.SubscriberInitExt.html#method.init tracing_subscriber::registry() .with(filter) .with(JsLayer) From 942f403ed0726f5e035317e3459bd6771b4e8334 Mon Sep 17 00:00:00 2001 From: Alex Morask <144709477+amorask-bitwarden@users.noreply.github.com> Date: Mon, 27 Oct 2025 08:41:22 -0500 Subject: [PATCH 06/18] Fix restart subscription modal showing twice from switcher (#16973) --- .../app/layouts/org-switcher/org-switcher.component.html | 1 - .../app/layouts/org-switcher/org-switcher.component.ts | 9 +-------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/apps/web/src/app/layouts/org-switcher/org-switcher.component.html b/apps/web/src/app/layouts/org-switcher/org-switcher.component.html index 96d17e7ada4..a9acddeb0b8 100644 --- a/apps/web/src/app/layouts/org-switcher/org-switcher.component.html +++ b/apps/web/src/app/layouts/org-switcher/org-switcher.component.html @@ -22,7 +22,6 @@ [route]="['../', org.id]" (mainContentClicked)="toggle()" [routerLinkActiveOptions]="{ exact: true }" - (click)="showInactiveSubscriptionDialog(org)" > - await firstValueFrom( - this.organizationWarningsService.showInactiveSubscriptionDialog$(organization), - ); } From abc6e54bb9d6c1e5d0e4f7e87cbc5aaef9da689e Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Mon, 27 Oct 2025 15:13:17 +0100 Subject: [PATCH 07/18] Platform - Prefer signal & change detection (#16946) --- .../platform/popup/components/pop-out.component.ts | 4 ++++ .../popup/view-cache/popup-router-cache.spec.ts | 2 ++ .../popup/view-cache/popup-view-cache.spec.ts | 4 ++++ .../src/platform/components/approve-ssh-request.ts | 2 ++ .../account-fingerprint.component.ts | 8 ++++++++ .../onboarding/onboarding-task.component.ts | 12 ++++++++++++ .../components/onboarding/onboarding.component.ts | 8 ++++++++ .../src/platform/guard/feature-flag.guard.spec.ts | 2 ++ 8 files changed, 42 insertions(+) diff --git a/apps/browser/src/platform/popup/components/pop-out.component.ts b/apps/browser/src/platform/popup/components/pop-out.component.ts index 320fa6f05ab..fd2acbd8aa7 100644 --- a/apps/browser/src/platform/popup/components/pop-out.component.ts +++ b/apps/browser/src/platform/popup/components/pop-out.component.ts @@ -7,12 +7,16 @@ import { IconButtonModule } from "@bitwarden/components"; import BrowserPopupUtils from "../../browser/browser-popup-utils"; +// 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-pop-out", templateUrl: "pop-out.component.html", imports: [CommonModule, JslibModule, IconButtonModule], }) export class PopOutComponent implements OnInit { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() show = true; constructor(private platformUtilsService: PlatformUtilsService) {} diff --git a/apps/browser/src/platform/popup/view-cache/popup-router-cache.spec.ts b/apps/browser/src/platform/popup/view-cache/popup-router-cache.spec.ts index 3304a99023e..835a8eebd2c 100644 --- a/apps/browser/src/platform/popup/view-cache/popup-router-cache.spec.ts +++ b/apps/browser/src/platform/popup/view-cache/popup-router-cache.spec.ts @@ -13,6 +13,8 @@ import { PopupRouterCacheService, popupRouterCacheGuard } from "./popup-router-c const flushPromises = async () => await new Promise(process.nextTick); +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ template: "", standalone: false, diff --git a/apps/browser/src/platform/popup/view-cache/popup-view-cache.spec.ts b/apps/browser/src/platform/popup/view-cache/popup-view-cache.spec.ts index 60baf94eeae..a18d51878ee 100644 --- a/apps/browser/src/platform/popup/view-cache/popup-view-cache.spec.ts +++ b/apps/browser/src/platform/popup/view-cache/popup-view-cache.spec.ts @@ -19,12 +19,16 @@ import { import { PopupViewCacheService } from "./popup-view-cache.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({ template: "", standalone: false, }) export class EmptyComponent {} +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ template: "", standalone: false, diff --git a/apps/desktop/src/platform/components/approve-ssh-request.ts b/apps/desktop/src/platform/components/approve-ssh-request.ts index 8cd63e0b1ac..1741124774d 100644 --- a/apps/desktop/src/platform/components/approve-ssh-request.ts +++ b/apps/desktop/src/platform/components/approve-ssh-request.ts @@ -21,6 +21,8 @@ export interface ApproveSshRequestParams { action: string; } +// 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-approve-ssh-request", templateUrl: "approve-ssh-request.html", diff --git a/apps/web/src/app/shared/components/account-fingerprint/account-fingerprint.component.ts b/apps/web/src/app/shared/components/account-fingerprint/account-fingerprint.component.ts index 256c8d6af34..eb84868dca1 100644 --- a/apps/web/src/app/shared/components/account-fingerprint/account-fingerprint.component.ts +++ b/apps/web/src/app/shared/components/account-fingerprint/account-fingerprint.component.ts @@ -6,14 +6,22 @@ import { KeyService } from "@bitwarden/key-management"; import { SharedModule } from "../../shared.module"; +// 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-account-fingerprint", templateUrl: "account-fingerprint.component.html", imports: [SharedModule], }) export class AccountFingerprintComponent implements OnInit { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() fingerprintMaterial: string; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() publicKeyBuffer: Uint8Array; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() fingerprintLabel: string; protected fingerprint: string; diff --git a/apps/web/src/app/shared/components/onboarding/onboarding-task.component.ts b/apps/web/src/app/shared/components/onboarding/onboarding-task.component.ts index f9798ec7f0f..277a4d2d26e 100644 --- a/apps/web/src/app/shared/components/onboarding/onboarding-task.component.ts +++ b/apps/web/src/app/shared/components/onboarding/onboarding-task.component.ts @@ -2,6 +2,8 @@ // @ts-strict-ignore import { Component, Input } from "@angular/core"; +// 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-onboarding-task", templateUrl: "./onboarding-task.component.html", @@ -11,18 +13,28 @@ import { Component, Input } from "@angular/core"; standalone: false, }) export class OnboardingTaskComponent { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() completed = false; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() icon = "bwi-info-circle"; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() title: string; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() route: string | any[]; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() isDisabled: boolean = false; diff --git a/apps/web/src/app/shared/components/onboarding/onboarding.component.ts b/apps/web/src/app/shared/components/onboarding/onboarding.component.ts index 5ead9fcc10b..832e7964cce 100644 --- a/apps/web/src/app/shared/components/onboarding/onboarding.component.ts +++ b/apps/web/src/app/shared/components/onboarding/onboarding.component.ts @@ -4,15 +4,23 @@ import { Component, ContentChildren, EventEmitter, Input, Output, QueryList } fr import { OnboardingTaskComponent } from "./onboarding-task.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-onboarding", templateUrl: "./onboarding.component.html", standalone: false, }) export class OnboardingComponent { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ContentChildren(OnboardingTaskComponent) tasks: QueryList; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() title: string; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() dismiss = new EventEmitter(); protected open = true; diff --git a/libs/angular/src/platform/guard/feature-flag.guard.spec.ts b/libs/angular/src/platform/guard/feature-flag.guard.spec.ts index 3bc8b085a7d..fa6d82b49e0 100644 --- a/libs/angular/src/platform/guard/feature-flag.guard.spec.ts +++ b/libs/angular/src/platform/guard/feature-flag.guard.spec.ts @@ -12,6 +12,8 @@ import { I18nMockService, ToastService } from "@bitwarden/components/src"; import { canAccessFeature } from "./feature-flag.guard"; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ template: "", standalone: false }) export class EmptyComponent {} From 64590cb3c82d9c8bc4173c5d1ec82ed07f1fd953 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Mon, 27 Oct 2025 15:17:20 +0100 Subject: [PATCH 08/18] [PM-25911] Add commercial sdk internal as dependency (#16883) * feat: add commercial sdk as optional dependency * feat: add alias to CLI * feat: add alias to browser * feat: add alias to web * fix: revert optional - we cant omit optional dependencies or the builds break * feat: remove commercial package from browser build * feat: remove commercial package from cli build * feat: remove commercial package from web build * chore: add commercial sdk to renovate * fix: windows cli workflow * fix: accidental change * feat: add lint for version string * undo weird merge changes --- .github/renovate.json5 | 1 + .github/workflows/build-browser.yml | 14 ++++++++++++ .github/workflows/build-cli.yml | 18 +++++++++++---- .github/workflows/build-web.yml | 10 +++++++++ .github/workflows/lint.yml | 3 +++ .npmrc | 2 +- apps/browser/webpack.base.js | 5 ++++- apps/cli/webpack.base.js | 2 ++ apps/web/Dockerfile | 6 +++++ apps/web/webpack.base.js | 2 ++ .../bit-browser/webpack.config.js | 12 ++++++++++ bitwarden_license/bit-cli/webpack.config.js | 12 ++++++++++ bitwarden_license/bit-web/webpack.config.js | 12 ++++++++++ package-lock.json | 22 +++++++++++++++++++ package.json | 2 ++ scripts/sdk-internal-versions.ts | 22 +++++++++++++++++++ 16 files changed, 139 insertions(+), 6 deletions(-) create mode 100644 scripts/sdk-internal-versions.ts diff --git a/.github/renovate.json5 b/.github/renovate.json5 index f898df460c9..ae7c2b023cb 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -139,6 +139,7 @@ "@babel/core", "@babel/preset-env", "@bitwarden/sdk-internal", + "@bitwarden/commercial-sdk-internal", "@electron/fuses", "@electron/notarize", "@electron/rebuild", diff --git a/.github/workflows/build-browser.yml b/.github/workflows/build-browser.yml index 5980ef507cc..1c805e8efbe 100644 --- a/.github/workflows/build-browser.yml +++ b/.github/workflows/build-browser.yml @@ -219,12 +219,14 @@ jobs: archive_name_prefix: "" npm_command_prefix: "dist:" readable: "open source license" + type: "oss" - build_prefix: "bit-" artifact_prefix: "bit-" source_archive_name_prefix: "bit-" archive_name_prefix: "bit-" npm_command_prefix: "dist:bit:" readable: "commercial license" + type: "commercial" browser: - name: "chrome" npm_command_suffix: "chrome" @@ -279,6 +281,11 @@ jobs: run: npm ci working-directory: browser-source/ + - name: Remove commercial packages + if: ${{ matrix.license_type.type == 'oss' }} + run: rm -rf node_modules/@bitwarden/commercial-sdk-internal + working-directory: browser-source/ + - name: Download SDK artifacts if: ${{ inputs.sdk_branch != '' }} uses: bitwarden/gh-actions/download-artifacts@main @@ -350,11 +357,13 @@ jobs: archive_name_prefix: "" npm_command_prefix: "dist:" readable: "open source license" + type: "oss" - build_prefix: "bit-" artifact_prefix: "bit-" archive_name_prefix: "bit-" npm_command_prefix: "dist:bit:" readable: "commercial license" + type: "commercial" env: _BUILD_NUMBER: ${{ needs.setup.outputs.adj_build_number }} _NODE_VERSION: ${{ needs.setup.outputs.node_version }} @@ -461,6 +470,11 @@ jobs: run: npm ci working-directory: ./ + - name: Remove commercial packages + if: ${{ matrix.license_type.type == 'oss' }} + run: rm -rf node_modules/@bitwarden/commercial-sdk-internal + working-directory: ./ + - name: Download SDK Artifacts if: ${{ inputs.sdk_branch != '' }} uses: bitwarden/gh-actions/download-artifacts@main diff --git a/.github/workflows/build-cli.yml b/.github/workflows/build-cli.yml index 1f7b35f3307..c2abbdf5e5c 100644 --- a/.github/workflows/build-cli.yml +++ b/.github/workflows/build-cli.yml @@ -98,8 +98,8 @@ jobs: ] license_type: [ - { build_prefix: "oss", artifact_prefix: "-oss", readable: "open source license" }, - { build_prefix: "bit", artifact_prefix: "", readable: "commercial license" } + { type: "oss", build_prefix: "oss", artifact_prefix: "-oss", readable: "open source license" }, + { type: "commercial", build_prefix: "bit", artifact_prefix: "", readable: "commercial license" } ] runs-on: ${{ matrix.os.distro }} needs: setup @@ -140,6 +140,11 @@ jobs: run: npm ci working-directory: ./ + - name: Remove commercial packages + if: ${{ matrix.license_type.type == 'oss' }} + run: rm -rf node_modules/@bitwarden/commercial-sdk-internal + working-directory: ./ + - name: Download SDK Artifacts if: ${{ inputs.sdk_branch != '' && needs.setup.outputs.has_secrets == 'true' }} uses: bitwarden/gh-actions/download-artifacts@main @@ -291,8 +296,8 @@ jobs: matrix: license_type: [ - { build_prefix: "oss", artifact_prefix: "-oss", readable: "open source license" }, - { build_prefix: "bit", artifact_prefix: "", readable: "commercial license" } + { type: "oss", build_prefix: "oss", artifact_prefix: "-oss", readable: "open source license" }, + { type: "commercial", build_prefix: "bit", artifact_prefix: "", readable: "commercial license" } ] runs-on: windows-2022 permissions: @@ -410,6 +415,11 @@ jobs: run: npm ci working-directory: ./ + - name: Remove commercial packages + if: ${{ matrix.license_type.type == 'oss' }} + run: Remove-Item -Recurse -Force -ErrorAction SilentlyContinue "node_modules/@bitwarden/commercial-sdk-internal" + working-directory: ./ + - name: Download SDK Artifacts if: ${{ inputs.sdk_branch != '' && needs.setup.outputs.has_secrets == 'true' }} uses: bitwarden/gh-actions/download-artifacts@main diff --git a/.github/workflows/build-web.yml b/.github/workflows/build-web.yml index ee7444f13a9..0ea3ad7af78 100644 --- a/.github/workflows/build-web.yml +++ b/.github/workflows/build-web.yml @@ -99,34 +99,43 @@ jobs: matrix: include: - artifact_name: selfhosted-open-source + license_type: "oss" image_name: web-oss npm_command: dist:oss:selfhost - artifact_name: cloud-COMMERCIAL + license_type: "commercial" image_name: web-cloud npm_command: dist:bit:cloud - artifact_name: selfhosted-COMMERCIAL + license_type: "commercial" image_name: web npm_command: dist:bit:selfhost - artifact_name: selfhosted-DEV + license_type: "commercial" image_name: web npm_command: build:bit:selfhost:dev git_metadata: true - artifact_name: cloud-QA + license_type: "commercial" image_name: web-qa-cloud npm_command: build:bit:qa git_metadata: true - artifact_name: ee + license_type: "commercial" image_name: web-ee npm_command: build:bit:ee git_metadata: true - artifact_name: cloud-euprd + license_type: "commercial" image_name: web-euprd npm_command: build:bit:euprd - artifact_name: cloud-euqa + license_type: "commercial" image_name: web-euqa npm_command: build:bit:euqa git_metadata: true - artifact_name: cloud-usdev + license_type: "commercial" image_name: web-usdev npm_command: build:bit:usdev git_metadata: true @@ -269,6 +278,7 @@ jobs: build-args: | NODE_VERSION=${{ env._NODE_VERSION }} NPM_COMMAND=${{ matrix.npm_command }} + LICENSE_TYPE=${{ matrix.license_type }} context: . file: apps/web/Dockerfile load: true diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index bc78462fdb5..21786339299 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -75,6 +75,9 @@ jobs: - name: Lint unowned dependencies run: npm run lint:dep-ownership + - name: Lint sdk-internal versions + run: npm run lint:sdk-internal-versions + - name: Run linter run: npm run lint diff --git a/.npmrc b/.npmrc index 421cf18217d..38a7eb153c0 100644 --- a/.npmrc +++ b/.npmrc @@ -1,4 +1,4 @@ save-exact=true # Increase available heap size to avoid running out of memory when compiling. # This applies to all npm scripts in this repository. -node-options=--max-old-space-size=8192 \ No newline at end of file +node-options=--max-old-space-size=8192 diff --git a/apps/browser/webpack.base.js b/apps/browser/webpack.base.js index 734a46ac187..4bc2a90c4ff 100644 --- a/apps/browser/webpack.base.js +++ b/apps/browser/webpack.base.js @@ -36,7 +36,8 @@ const DEFAULT_PARAMS = { * outputPath?: string; * mode?: string; * env?: string; - * additionalEntries?: { [outputPath: string]: string } + * additionalEntries?: { [outputPath: string]: string }; + * importAliases?: import("webpack").ResolveOptions["alias"]; * }} params - The input parameters for building the config. */ module.exports.buildConfig = function buildConfig(params) { @@ -362,6 +363,7 @@ module.exports.buildConfig = function buildConfig(params) { path: require.resolve("path-browserify"), }, cache: true, + alias: params.importAliases, }, output: { filename: "[name].js", @@ -482,6 +484,7 @@ module.exports.buildConfig = function buildConfig(params) { path: require.resolve("path-browserify"), }, cache: true, + alias: params.importAliases, }, dependencies: ["main"], plugins: [...requiredPlugins, new AngularCheckPlugin()], diff --git a/apps/cli/webpack.base.js b/apps/cli/webpack.base.js index 01d5fc5b175..532b0a747a0 100644 --- a/apps/cli/webpack.base.js +++ b/apps/cli/webpack.base.js @@ -31,6 +31,7 @@ const DEFAULT_PARAMS = { * localesPath?: string; * externalsModulesDir?: string; * watch?: boolean; + * importAliases?: import("webpack").ResolveOptions["alias"]; * }} params */ module.exports.buildConfig = function buildConfig(params) { @@ -95,6 +96,7 @@ module.exports.buildConfig = function buildConfig(params) { symlinks: false, modules: params.modulesPath, plugins: [new TsconfigPathsPlugin({ configFile: params.tsConfig })], + alias: params.importAliases, }, output: { filename: "[name].js", diff --git a/apps/web/Dockerfile b/apps/web/Dockerfile index 6017d60df5f..6d27e12537a 100644 --- a/apps/web/Dockerfile +++ b/apps/web/Dockerfile @@ -9,6 +9,12 @@ COPY package*.json ./ COPY . . RUN npm ci +# Remove commercial packages if LICENSE_TYPE is not 'commercial' +ARG LICENSE_TYPE=oss +RUN if [ "${LICENSE_TYPE}" != "commercial" ] ; then \ + rm -rf node_modules/@bitwarden/commercial-sdk-internal ; \ + fi + WORKDIR /source/apps/web ARG NPM_COMMAND=dist:bit:selfhost RUN npm run ${NPM_COMMAND} diff --git a/apps/web/webpack.base.js b/apps/web/webpack.base.js index 56fd6c7faf5..f1e627a58a8 100644 --- a/apps/web/webpack.base.js +++ b/apps/web/webpack.base.js @@ -36,6 +36,7 @@ const DEFAULT_PARAMS = { * outputPath?: string; * mode?: string; * env?: string; + * importAliases?: import("webpack").ResolveOptions["alias"]; * }} params */ module.exports.buildConfig = function buildConfig(params) { @@ -460,6 +461,7 @@ module.exports.buildConfig = function buildConfig(params) { process: false, path: require.resolve("path-browserify"), }, + alias: params.importAliases, }, output: { filename: "[name].[contenthash].js", diff --git a/bitwarden_license/bit-browser/webpack.config.js b/bitwarden_license/bit-browser/webpack.config.js index 1c6ab51549f..a0b1870721b 100644 --- a/bitwarden_license/bit-browser/webpack.config.js +++ b/bitwarden_license/bit-browser/webpack.config.js @@ -36,6 +36,12 @@ module.exports = (webpackConfig, context) => { : context.options.outputPath, mode: mode, env: ENV, + importAliases: [ + { + name: "@bitwarden/sdk-internal", + alias: "@bitwarden/commercial-sdk-internal", + }, + ], }); } else { // npm build configuration @@ -49,6 +55,12 @@ module.exports = (webpackConfig, context) => { entry: path.resolve(__dirname, "src/platform/background.ts"), }, tsConfig: path.resolve(__dirname, "tsconfig.json"), + importAliases: [ + { + name: "@bitwarden/sdk-internal", + alias: "@bitwarden/commercial-sdk-internal", + }, + ], }); } }; diff --git a/bitwarden_license/bit-cli/webpack.config.js b/bitwarden_license/bit-cli/webpack.config.js index f746da40761..6d31d0b5e96 100644 --- a/bitwarden_license/bit-cli/webpack.config.js +++ b/bitwarden_license/bit-cli/webpack.config.js @@ -24,6 +24,12 @@ module.exports = (webpackConfig, context) => { localesPath: "apps/cli/src/locales", externalsModulesDir: "node_modules", watch: context.options.watch || false, + importAliases: [ + { + name: "@bitwarden/sdk-internal", + alias: "@bitwarden/commercial-sdk-internal", + }, + ], }); } else { // npm build configuration @@ -43,6 +49,12 @@ module.exports = (webpackConfig, context) => { modulesPath: [path.resolve("../../node_modules")], localesPath: "../../apps/cli/src/locales", externalsModulesDir: "../../node_modules", + importAliases: [ + { + name: "@bitwarden/sdk-internal", + alias: "@bitwarden/commercial-sdk-internal", + }, + ], }); } }; diff --git a/bitwarden_license/bit-web/webpack.config.js b/bitwarden_license/bit-web/webpack.config.js index 6ac1efdc192..6433eee59f6 100644 --- a/bitwarden_license/bit-web/webpack.config.js +++ b/bitwarden_license/bit-web/webpack.config.js @@ -17,6 +17,12 @@ module.exports = (webpackConfig, context) => { context.context && context.context.root ? path.resolve(context.context.root, context.options.outputPath) : context.options.outputPath, + importAliases: [ + { + name: "@bitwarden/sdk-internal", + alias: "@bitwarden/commercial-sdk-internal", + }, + ], }); } else { return buildConfig({ @@ -26,6 +32,12 @@ module.exports = (webpackConfig, context) => { entryModule: "bitwarden_license/bit-web/src/app/app.module#AppModule", }, tsConfig: path.resolve(__dirname, "tsconfig.build.json"), + importAliases: [ + { + name: "@bitwarden/sdk-internal", + alias: "@bitwarden/commercial-sdk-internal", + }, + ], }); } }; diff --git a/package-lock.json b/package-lock.json index 8ce60e0826f..747576d4ca2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,7 @@ "@angular/platform-browser": "19.2.14", "@angular/platform-browser-dynamic": "19.2.14", "@angular/router": "19.2.14", + "@bitwarden/commercial-sdk-internal": "0.2.0-main.357", "@bitwarden/sdk-internal": "0.2.0-main.357", "@electron/fuses": "1.8.0", "@emotion/css": "11.13.5", @@ -4605,6 +4606,27 @@ "resolved": "libs/client-type", "link": true }, + "node_modules/@bitwarden/commercial-sdk-internal": { + "version": "0.2.0-main.357", + "resolved": "https://registry.npmjs.org/@bitwarden/commercial-sdk-internal/-/commercial-sdk-internal-0.2.0-main.357.tgz", + "integrity": "sha512-eIArJelJKwG+aEGbtdhc5dKRBFopmyGJl+ClUQGJUFHzfrPGDcaSI04a/sSUK0NtbaxQOsf8qSvk+iKvISkKmw==", + "license": "BITWARDEN SOFTWARE DEVELOPMENT KIT LICENSE AGREEMENT", + "dependencies": { + "type-fest": "^4.41.0" + } + }, + "node_modules/@bitwarden/commercial-sdk-internal/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@bitwarden/common": { "resolved": "libs/common", "link": true diff --git a/package.json b/package.json index 89e127488b2..c241e07e2e1 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "test:types": "node ./scripts/test-types.js", "test:locales": "tsc --project ./scripts/tsconfig.json && node ./scripts/dist/test-locales.js", "lint:dep-ownership": "tsc --project ./scripts/tsconfig.json && node ./scripts/dist/dep-ownership.js", + "lint:sdk-internal-versions": "tsc --project ./scripts/tsconfig.json && node ./scripts/dist/sdk-internal-versions.js", "docs:json": "compodoc -p ./tsconfig.json -e json -d . --disableRoutesGraph", "storybook": "ng run components:storybook", "build-storybook": "ng run components:build-storybook", @@ -160,6 +161,7 @@ "@angular/platform-browser-dynamic": "19.2.14", "@angular/router": "19.2.14", "@bitwarden/sdk-internal": "0.2.0-main.357", + "@bitwarden/commercial-sdk-internal": "0.2.0-main.357", "@electron/fuses": "1.8.0", "@emotion/css": "11.13.5", "@koa/multer": "4.0.0", diff --git a/scripts/sdk-internal-versions.ts b/scripts/sdk-internal-versions.ts new file mode 100644 index 00000000000..c442772e553 --- /dev/null +++ b/scripts/sdk-internal-versions.ts @@ -0,0 +1,22 @@ +/* eslint-disable no-console */ + +/// Ensure that `sdk-internal` and `commercial-sdk-internal` dependencies have matching versions. + +import fs from "fs"; +import path from "path"; + +const packageJson = JSON.parse( + fs.readFileSync(path.join(__dirname, "..", "..", "package.json"), "utf8"), +); + +const sdkInternal = packageJson.dependencies["@bitwarden/sdk-internal"]; +const commercialSdkInternal = packageJson.dependencies["@bitwarden/commercial-sdk-internal"]; + +if (sdkInternal !== commercialSdkInternal) { + console.error( + `Version mismatch between @bitwarden/sdk-internal (${sdkInternal}) and @bitwarden/commercial-sdk-internal (${commercialSdkInternal}), must be an exact match.`, + ); + process.exit(1); +} + +console.log(`All dependencies have matching versions: ${sdkInternal}`); From ea4b6779a57f810357b23fb62de70cd428564acb Mon Sep 17 00:00:00 2001 From: Brandon Treston Date: Mon, 27 Oct 2025 10:35:18 -0400 Subject: [PATCH 09/18] [PM-26373] Update invitation accepted toast copy (#17021) * update copy * update copy * update i18n.t * use toast service, remove toast title * fix spelling --- .../accept-organization.component.ts | 15 ++++++++------- apps/web/src/locales/en/messages.json | 4 ++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/apps/web/src/app/auth/organization-invite/accept-organization.component.ts b/apps/web/src/app/auth/organization-invite/accept-organization.component.ts index f98a62f91ea..cb1175a7002 100644 --- a/apps/web/src/app/auth/organization-invite/accept-organization.component.ts +++ b/apps/web/src/app/auth/organization-invite/accept-organization.component.ts @@ -11,6 +11,7 @@ import { OrganizationInvite } from "@bitwarden/common/auth/services/organization import { OrganizationInviteService } from "@bitwarden/common/auth/services/organization-invite/organization-invite.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { ToastService } from "@bitwarden/components"; import { BaseAcceptComponent } from "../../common/base.accept.component"; @@ -35,6 +36,7 @@ export class AcceptOrganizationComponent extends BaseAcceptComponent { private acceptOrganizationInviteService: AcceptOrganizationInviteService, private organizationInviteService: OrganizationInviteService, private accountService: AccountService, + private toastService: ToastService, ) { super(router, platformUtilsService, i18nService, route, authService); } @@ -51,14 +53,13 @@ export class AcceptOrganizationComponent extends BaseAcceptComponent { return; } - this.platformUtilService.showToast( - "success", - this.i18nService.t("inviteAccepted"), - invite.initOrganization + this.toastService.showToast({ + message: invite.initOrganization ? this.i18nService.t("inviteInitAcceptedDesc") - : this.i18nService.t("inviteAcceptedDesc"), - { timeout: 10000 }, - ); + : this.i18nService.t("invitationAcceptedDesc"), + variant: "success", + timeout: 10000, + }); await this.router.navigate(["/vault"]); } diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index f88af8aa1a7..72ca4d73976 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -4452,8 +4452,8 @@ "inviteAccepted": { "message": "Invitation accepted" }, - "inviteAcceptedDesc": { - "message": "You can access this organization once an administrator confirms your membership. We'll send you an email when that happens." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "You can now access this organization." From fd4568974520b75b67705380c0f6adc32acb4d0a Mon Sep 17 00:00:00 2001 From: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> Date: Mon, 27 Oct 2025 09:55:31 -0500 Subject: [PATCH 10/18] [PM-27342] Fix state migration (#17018) * Fix migration * Update test --- .../73-add-master-password-unlock-data.spec.ts | 12 ++++++++++++ .../migrations/73-add-master-password-unlock-data.ts | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/libs/state/src/state-migrations/migrations/73-add-master-password-unlock-data.spec.ts b/libs/state/src/state-migrations/migrations/73-add-master-password-unlock-data.spec.ts index 28e65216653..2956b1cbcd2 100644 --- a/libs/state/src/state-migrations/migrations/73-add-master-password-unlock-data.spec.ts +++ b/libs/state/src/state-migrations/migrations/73-add-master-password-unlock-data.spec.ts @@ -97,6 +97,18 @@ describe("AddMasterPasswordUnlockData", () => { user_user1_kdfConfig_kdfConfig: { kdfType: 0, iterations: 600000 }, }); }); + + it("handles users with missing global accounts", async () => { + const output = await runMigrator(sut, { + global_account_accounts: { user_user1: null }, + user_user1_kdfConfig_kdfConfig: { kdfType: 0, iterations: 600000 }, + }); + + expect(output).toEqual({ + global_account_accounts: { user_user1: null }, + user_user1_kdfConfig_kdfConfig: { kdfType: 0, iterations: 600000 }, + }); + }); }); describe("rollback", () => { diff --git a/libs/state/src/state-migrations/migrations/73-add-master-password-unlock-data.ts b/libs/state/src/state-migrations/migrations/73-add-master-password-unlock-data.ts index b9833f439a6..321df7d5cfc 100644 --- a/libs/state/src/state-migrations/migrations/73-add-master-password-unlock-data.ts +++ b/libs/state/src/state-migrations/migrations/73-add-master-password-unlock-data.ts @@ -32,7 +32,7 @@ type Account = { export class AddMasterPasswordUnlockData extends Migrator<72, 73> { async migrate(helper: MigrationHelper): Promise { async function migrateAccount(userId: string, account: Account) { - const email = account.email; + const email = account?.email; const kdfConfig = await helper.getFromUser(userId, KDF_CONFIG_DISK); const masterKeyEncryptedUserKey = await helper.getFromUser( userId, From af6e19335d751d4eebe6cf68ab7e44b9c7bea9ed Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Mon, 27 Oct 2025 16:13:11 +0100 Subject: [PATCH 11/18] Vault - Prefer signal & change detection (#16947) --- .../at-risk-password-callout.component.ts | 2 + .../at-risk-carousel-dialog.component.ts | 4 ++ .../at-risk-passwords.component.spec.ts | 14 ++++++ .../at-risk-passwords.component.ts | 2 + .../add-edit/add-edit-v2.component.ts | 2 + .../assign-collections.component.ts | 2 + .../attachments-v2.component.spec.ts | 10 ++++ .../attachments/attachments-v2.component.ts | 2 + .../open-attachments.component.ts | 4 ++ .../autofill-vault-list-items.component.ts | 4 +- .../blocked-injection-banner.component.ts | 2 + .../intro-carousel.component.ts | 2 + .../item-copy-actions.component.ts | 4 ++ .../item-more-options.component.ts | 8 +++ .../new-item-dropdown-v2.component.ts | 4 ++ .../vault-generator-dialog.component.spec.ts | 10 ++++ .../vault-generator-dialog.component.ts | 2 + .../vault-header/vault-header-v2.component.ts | 4 ++ .../vault-list-filters.component.ts | 2 + .../vault-list-items-container.component.ts | 40 +++++++++++++++ .../vault-password-history-v2.component.ts | 2 + .../vault-search/vault-v2-search.component.ts | 2 + .../components/vault-v2/vault-v2.component.ts | 4 ++ .../vault-v2/view-v2/view-v2.component.ts | 2 + .../services/vault-popup-section.service.ts | 8 +-- .../settings/appearance-v2.component.spec.ts | 10 ++++ .../popup/settings/appearance-v2.component.ts | 2 + .../vault/popup/settings/archive.component.ts | 2 + .../settings/download-bitwarden.component.ts | 2 + .../settings/folders-v2.component.spec.ts | 10 ++++ .../popup/settings/folders-v2.component.ts | 2 + .../more-from-bitwarden-page-v2.component.ts | 2 + .../trash-list-items-container.component.ts | 4 ++ .../settings/vault-settings-v2.component.ts | 6 ++- .../assign-collections-desktop.component.ts | 2 + .../credential-generator-dialog.component.ts | 2 + .../vault/app/vault/item-footer.component.ts | 24 +++++++++ .../filters/collection-filter.component.ts | 2 + .../filters/folder-filter.component.ts | 2 + .../filters/organization-filter.component.ts | 2 + .../filters/status-filter.component.ts | 2 + .../filters/type-filter.component.ts | 2 + .../vault-filter/vault-filter.component.ts | 2 + .../app/vault/vault-items-v2.component.ts | 2 + .../src/vault/app/vault/vault-v2.component.ts | 10 ++++ .../assign-collections-web.component.ts | 2 + ...wser-extension-prompt-install.component.ts | 2 + .../browser-extension-prompt.component.ts | 2 + .../manually-open-extension.component.ts | 2 + .../add-extension-later-dialog.component.ts | 2 + .../add-extension-videos.component.ts | 4 ++ .../setup-extension.component.ts | 2 + .../vault-item-dialog.component.ts | 6 +++ .../vault-items/vault-cipher-row.component.ts | 46 +++++++++++++++++ .../vault-collection-row.component.ts | 32 ++++++++++++ .../vault-items/vault-items.component.ts | 50 +++++++++++++++++++ .../web-generator-dialog.component.spec.ts | 10 ++++ .../web-generator-dialog.component.ts | 2 + .../individual-vault/add-edit-v2.component.ts | 2 + .../bulk-delete-dialog.component.ts | 2 + .../bulk-move-dialog.component.ts | 2 + .../organization-name-badge.component.ts | 8 +++ .../vault-banners/vault-banners.component.ts | 4 ++ .../organization-options.component.ts | 2 + .../components/vault-filter.component.ts | 10 ++++ .../vault-filter-section.component.ts | 6 +++ .../vault-header/vault-header.component.ts | 20 ++++++++ .../vault-onboarding.component.ts | 8 +++ .../vault/individual-vault/vault.component.ts | 6 +++ .../vault/settings/purge-vault.component.ts | 2 + .../components/folder-add-edit.component.ts | 6 +++ .../src/vault/components/icon.component.ts | 6 +-- .../spotlight/spotlight.component.ts | 16 ++++++ .../vault/components/vault-items.component.ts | 10 ++++ .../components/collection-filter.component.ts | 12 +++++ .../components/folder-filter.component.ts | 16 ++++++ .../organization-filter.component.ts | 16 ++++++ .../components/status-filter.component.ts | 10 ++++ .../components/type-filter.component.ts | 12 +++++ .../components/vault-filter.component.ts | 18 +++++++ ...ditional-options-section.component.spec.ts | 4 ++ .../additional-options-section.component.ts | 6 +++ .../cipher-attachments.component.spec.ts | 8 +++ .../cipher-attachments.component.ts | 18 +++++++ .../delete-attachment.component.ts | 10 ++++ .../advanced-uri-option-dialog.component.ts | 2 + .../autofill-options.component.ts | 4 ++ .../autofill-options/uri-option.component.ts | 18 +++++++ .../card-details-section.component.ts | 6 +++ .../components/cipher-form.component.ts | 18 +++++++ .../cipher-form-generator.component.spec.ts | 8 +++ .../cipher-form-generator.component.ts | 12 +++++ .../add-edit-custom-field-dialog.component.ts | 2 + .../custom-fields/custom-fields.component.ts | 8 +++ .../components/identity/identity.component.ts | 6 +++ .../item-details-section.component.ts | 6 +++ .../login-details-section.component.spec.ts | 2 + .../login-details-section.component.ts | 2 + .../new-item-nudge.component.ts | 4 +- .../sshkey-section.component.ts | 6 +++ .../additional-options.component.ts | 4 ++ .../attachments-v2-view.component.ts | 8 +++ .../attachments/attachments-v2.component.ts | 2 + .../autofill-options-view.component.ts | 6 +++ .../card-details-view.component.ts | 4 ++ .../src/cipher-view/cipher-view.component.ts | 10 ++++ .../custom-fields-v2.component.ts | 4 ++ .../item-details/item-details-v2.component.ts | 2 + .../item-history/item-history-v2.component.ts | 4 ++ .../login-credentials-view.component.ts | 12 +++++ .../read-only-cipher-card.component.ts | 4 ++ .../sshkey-sections/sshkey-view.component.ts | 4 ++ .../view-identity-sections.component.ts | 4 ++ .../add-edit-folder-dialog.component.ts | 6 +++ .../assign-collections.component.ts | 12 +++++ .../components/can-delete-cipher.directive.ts | 2 + .../carousel-button.component.ts | 10 ++++ .../carousel-content.component.spec.ts | 4 ++ .../carousel-content.component.ts | 4 ++ .../carousel-slide.component.spec.ts | 2 + .../carousel-slide.component.ts | 10 ++++ .../carousel/carousel.component.spec.ts | 2 + .../components/carousel/carousel.component.ts | 18 +++++++ .../components/copy-cipher-field.directive.ts | 7 ++- .../components/dark-image-source.directive.ts | 2 +- .../decryption-failure-dialog.component.ts | 2 + .../download-attachment.component.ts | 12 +++++ .../new-cipher-menu.component.ts | 10 ++++ .../src/components/org-icon.directive.ts | 4 ++ .../password-history-view.component.ts | 4 ++ .../password-history.component.ts | 2 + .../components/password-reprompt.component.ts | 2 + ...permit-cipher-details-popover.component.ts | 2 + .../totp-countdown.component.ts | 6 +++ 134 files changed, 918 insertions(+), 13 deletions(-) diff --git a/apps/browser/src/vault/popup/components/at-risk-callout/at-risk-password-callout.component.ts b/apps/browser/src/vault/popup/components/at-risk-callout/at-risk-password-callout.component.ts index c3d4f461d70..c37131b3ff1 100644 --- a/apps/browser/src/vault/popup/components/at-risk-callout/at-risk-password-callout.component.ts +++ b/apps/browser/src/vault/popup/components/at-risk-callout/at-risk-password-callout.component.ts @@ -10,6 +10,8 @@ import { AnchorLinkDirective, CalloutModule, BannerModule } from "@bitwarden/com import { I18nPipe } from "@bitwarden/ui-common"; import { AtRiskPasswordCalloutData, AtRiskPasswordCalloutService } 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: "vault-at-risk-password-callout", imports: [ diff --git a/apps/browser/src/vault/popup/components/at-risk-carousel-dialog/at-risk-carousel-dialog.component.ts b/apps/browser/src/vault/popup/components/at-risk-carousel-dialog/at-risk-carousel-dialog.component.ts index 08c466d21a9..f81bccc760c 100644 --- a/apps/browser/src/vault/popup/components/at-risk-carousel-dialog/at-risk-carousel-dialog.component.ts +++ b/apps/browser/src/vault/popup/components/at-risk-carousel-dialog/at-risk-carousel-dialog.component.ts @@ -17,6 +17,8 @@ export const AtRiskCarouselDialogResult = { type AtRiskCarouselDialogResult = UnionOfValues; +// 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: "vault-at-risk-carousel-dialog", templateUrl: "./at-risk-carousel-dialog.component.html", @@ -32,6 +34,8 @@ type AtRiskCarouselDialogResult = UnionOfValues`, }) class MockPopupHeaderComponent { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() pageTitle: string | undefined; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() backAction: (() => void) | undefined; } +// 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: "popup-page", template: ``, }) class MockPopupPageComponent { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() loading: boolean | undefined; } +// 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-icon", template: ``, }) class MockAppIcon { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() cipher: CipherView | undefined; } diff --git a/apps/browser/src/vault/popup/components/at-risk-passwords/at-risk-passwords.component.ts b/apps/browser/src/vault/popup/components/at-risk-passwords/at-risk-passwords.component.ts index 6918bedb9bf..3eeb2d1917b 100644 --- a/apps/browser/src/vault/popup/components/at-risk-passwords/at-risk-passwords.component.ts +++ b/apps/browser/src/vault/popup/components/at-risk-passwords/at-risk-passwords.component.ts @@ -58,6 +58,8 @@ import { import { AtRiskPasswordPageService } from "./at-risk-password-page.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({ imports: [ PopupPageComponent, diff --git a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts index 463819b96e4..60e44cefbdf 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts @@ -131,6 +131,8 @@ class QueryParams { export type AddEditQueryParams = Partial>; +// 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-add-edit-v2", templateUrl: "add-edit-v2.component.html", diff --git a/apps/browser/src/vault/popup/components/vault-v2/assign-collections/assign-collections.component.ts b/apps/browser/src/vault/popup/components/vault-v2/assign-collections/assign-collections.component.ts index 0b7346c8613..b314c48fecd 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/assign-collections/assign-collections.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/assign-collections/assign-collections.component.ts @@ -28,6 +28,8 @@ import { PopupFooterComponent } from "../../../../../platform/popup/layout/popup import { PopupHeaderComponent } from "../../../../../platform/popup/layout/popup-header.component"; import { PopupPageComponent } from "../../../../../platform/popup/layout/popup-page.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-assign-collections", templateUrl: "./assign-collections.component.html", diff --git a/apps/browser/src/vault/popup/components/vault-v2/attachments/attachments-v2.component.spec.ts b/apps/browser/src/vault/popup/components/vault-v2/attachments/attachments-v2.component.spec.ts index 6e4215c1ec2..871163ac80b 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/attachments/attachments-v2.component.spec.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/attachments/attachments-v2.component.spec.ts @@ -25,20 +25,30 @@ import { PopupRouterCacheService } from "../../../../../platform/popup/view-cach import { AttachmentsV2Component } from "./attachments-v2.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: "popup-header", template: ``, }) class MockPopupHeaderComponent { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() pageTitle: string; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() backAction: () => void; } +// 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: "popup-footer", template: ``, }) class MockPopupFooterComponent { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() pageTitle: string; } diff --git a/apps/browser/src/vault/popup/components/vault-v2/attachments/attachments-v2.component.ts b/apps/browser/src/vault/popup/components/vault-v2/attachments/attachments-v2.component.ts index fc6d882dfd5..295496c701f 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/attachments/attachments-v2.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/attachments/attachments-v2.component.ts @@ -17,6 +17,8 @@ import { PopupHeaderComponent } from "../../../../../platform/popup/layout/popup import { PopupPageComponent } from "../../../../../platform/popup/layout/popup-page.component"; import { PopupRouterCacheService } from "../../../../../platform/popup/view-cache/popup-router-cache.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-attachments-v2", templateUrl: "./attachments-v2.component.html", diff --git a/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.ts b/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.ts index 26410a46187..e2af3c44c7e 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.ts @@ -25,6 +25,8 @@ import { CipherFormContainer } from "@bitwarden/vault"; import BrowserPopupUtils from "../../../../../../platform/browser/browser-popup-utils"; import { FilePopoutUtilsService } from "../../../../../../tools/popup/services/file-popout-utils.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-open-attachments", templateUrl: "./open-attachments.component.html", @@ -39,6 +41,8 @@ import { FilePopoutUtilsService } from "../../../../../../tools/popup/services/f }) export class OpenAttachmentsComponent implements OnInit { /** Cipher `id` */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ required: true }) cipherId: CipherId; /** True when the attachments window should be opened in a popout */ diff --git a/apps/browser/src/vault/popup/components/vault-v2/autofill-vault-list-items/autofill-vault-list-items.component.ts b/apps/browser/src/vault/popup/components/vault-v2/autofill-vault-list-items/autofill-vault-list-items.component.ts index 1eef907821d..64f662ab840 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/autofill-vault-list-items/autofill-vault-list-items.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/autofill-vault-list-items/autofill-vault-list-items.component.ts @@ -15,6 +15,8 @@ import { VaultPopupItemsService } from "../../../services/vault-popup-items.serv import { PopupCipherViewLike } from "../../../views/popup-cipher.view"; import { VaultListItemsContainerComponent } from "../vault-list-items-container/vault-list-items-container.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({ imports: [ CommonModule, @@ -46,7 +48,7 @@ export class AutofillVaultListItemsComponent { startWith(true), // Start with true to avoid flashing the fill button on first load ); - protected groupByType = toSignal( + protected readonly groupByType = toSignal( this.vaultPopupItemsService.hasFilterApplied$.pipe(map((hasFilter) => !hasFilter)), ); diff --git a/apps/browser/src/vault/popup/components/vault-v2/blocked-injection-banner/blocked-injection-banner.component.ts b/apps/browser/src/vault/popup/components/vault-v2/blocked-injection-banner/blocked-injection-banner.component.ts index 5824e8d97ea..2125af289a2 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/blocked-injection-banner/blocked-injection-banner.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/blocked-injection-banner/blocked-injection-banner.component.ts @@ -15,6 +15,8 @@ import { VaultPopupAutofillService } from "../../../services/vault-popup-autofil const blockedURISettingsRoute = "/blocked-domains"; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ imports: [ BannerModule, diff --git a/apps/browser/src/vault/popup/components/vault-v2/intro-carousel/intro-carousel.component.ts b/apps/browser/src/vault/popup/components/vault-v2/intro-carousel/intro-carousel.component.ts index 94996a054e6..48c8f5682bc 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/intro-carousel/intro-carousel.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/intro-carousel/intro-carousel.component.ts @@ -9,6 +9,8 @@ import { VaultCarouselModule } from "@bitwarden/vault"; import { IntroCarouselService } from "../../../services/intro-carousel.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-intro-carousel", templateUrl: "./intro-carousel.component.html", diff --git a/apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.ts b/apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.ts index 6c7e8bcfbc3..2e2ee5cd56b 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.ts @@ -21,6 +21,8 @@ type CipherItem = { field: CopyAction; }; +// 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-item-copy-actions", templateUrl: "item-copy-actions.component.html", @@ -35,6 +37,8 @@ type CipherItem = { }) export class ItemCopyActionsComponent { protected showQuickCopyActions$ = inject(VaultPopupCopyButtonsService).showQuickCopyActions$; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ required: true }) cipher!: CipherViewLike; protected CipherViewLikeUtils = CipherViewLikeUtils; diff --git a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts index 1b8403e6024..94016d2670f 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts @@ -34,6 +34,8 @@ import { PasswordRepromptService } from "@bitwarden/vault"; import { VaultPopupAutofillService } from "../../../services/vault-popup-autofill.service"; import { AddEditQueryParams } from "../add-edit/add-edit-v2.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-item-more-options", templateUrl: "./item-more-options.component.html", @@ -42,6 +44,8 @@ import { AddEditQueryParams } from "../add-edit/add-edit-v2.component"; export class ItemMoreOptionsComponent { private _cipher$ = new BehaviorSubject(undefined); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ required: true, }) @@ -57,6 +61,8 @@ export class ItemMoreOptionsComponent { * Flag to show view item menu option. Used when something else is * assigned as the primary action for the item, such as autofill. */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ transform: booleanAttribute }) showViewOption: boolean; @@ -64,6 +70,8 @@ export class ItemMoreOptionsComponent { * Flag to hide the autofill menu options. Used for items that are * already in the autofill list suggestion. */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ transform: booleanAttribute }) hideAutofillOptions: boolean; diff --git a/apps/browser/src/vault/popup/components/vault-v2/new-item-dropdown/new-item-dropdown-v2.component.ts b/apps/browser/src/vault/popup/components/vault-v2/new-item-dropdown/new-item-dropdown-v2.component.ts index d1586bd6ad5..004980db181 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/new-item-dropdown/new-item-dropdown-v2.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/new-item-dropdown/new-item-dropdown-v2.component.ts @@ -23,6 +23,8 @@ export interface NewItemInitialValues { collectionId?: CollectionId; } +// 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-new-item-dropdown", templateUrl: "new-item-dropdown-v2.component.html", @@ -34,6 +36,8 @@ export class NewItemDropdownV2Component implements OnInit { /** * Optional initial values to pass to the add cipher form */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() initialValues: NewItemInitialValues; diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-generator-dialog/vault-generator-dialog.component.spec.ts b/apps/browser/src/vault/popup/components/vault-v2/vault-generator-dialog/vault-generator-dialog.component.spec.ts index b65138dac3a..2139b6d9a4f 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-generator-dialog/vault-generator-dialog.component.spec.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-generator-dialog/vault-generator-dialog.component.spec.ts @@ -18,14 +18,24 @@ import { VaultGeneratorDialogComponent, } from "./vault-generator-dialog.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: "vault-cipher-form-generator", template: "", }) class MockCipherFormGenerator { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() type: "password" | "username" = "password"; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() algorithmSelected: EventEmitter = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() uri: string = ""; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() valueGenerated = new EventEmitter(); } diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-generator-dialog/vault-generator-dialog.component.ts b/apps/browser/src/vault/popup/components/vault-v2/vault-generator-dialog/vault-generator-dialog.component.ts index b0103aaacfb..caeebdabc09 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-generator-dialog/vault-generator-dialog.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-generator-dialog/vault-generator-dialog.component.ts @@ -38,6 +38,8 @@ export const GeneratorDialogAction = { type GeneratorDialogAction = UnionOfValues; +// 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-generator-dialog", templateUrl: "./vault-generator-dialog.component.html", diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-header/vault-header-v2.component.ts b/apps/browser/src/vault/popup/components/vault-v2/vault-header/vault-header-v2.component.ts index f64b5e6b83d..6381b8be147 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-header/vault-header-v2.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-header/vault-header-v2.component.ts @@ -17,6 +17,8 @@ import { VaultPopupListFiltersService } from "../../../../../vault/popup/service import { VaultListFiltersComponent } from "../vault-list-filters/vault-list-filters.component"; import { VaultV2SearchComponent } from "../vault-search/vault-v2-search.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-header-v2", templateUrl: "vault-header-v2.component.html", @@ -31,6 +33,8 @@ import { VaultV2SearchComponent } from "../vault-search/vault-v2-search.componen ], }) export class VaultHeaderV2Component { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChild(DisclosureComponent) disclosure: DisclosureComponent; /** Emits the visibility status of the disclosure component. */ diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-list-filters/vault-list-filters.component.ts b/apps/browser/src/vault/popup/components/vault-v2/vault-list-filters/vault-list-filters.component.ts index 81fad896ad2..50da66fe5b8 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-list-filters/vault-list-filters.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-list-filters/vault-list-filters.component.ts @@ -8,6 +8,8 @@ import { ChipSelectComponent } from "@bitwarden/components"; import { VaultPopupListFiltersService } from "../../../services/vault-popup-list-filters.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-vault-list-filters", templateUrl: "./vault-list-filters.component.html", diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.ts b/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.ts index 61d7815d93e..6850a474af5 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.ts @@ -90,12 +90,18 @@ export class VaultListItemsContainerComponent implements AfterViewInit { private vaultPopupSectionService = inject(VaultPopupSectionService); protected CipherViewLikeUtils = CipherViewLikeUtils; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChild(CdkVirtualScrollViewport, { static: false }) viewPort!: CdkVirtualScrollViewport; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChild(DisclosureComponent) disclosure!: DisclosureComponent; /** * Indicates whether the section should be open or closed if collapsibleKey is provided */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals protected sectionOpenState: Signal = computed(() => { if (!this.collapsibleKey()) { return true; @@ -130,17 +136,23 @@ export class VaultListItemsContainerComponent implements AfterViewInit { */ private viewCipherTimeout?: number; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals ciphers = input([]); /** * If true, we will group ciphers by type (Login, Card, Identity) * within subheadings in a single container, converted to a WritableSignal. */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals groupByType = input(false); /** * Computed signal for a grouped list of ciphers with an optional header */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals cipherGroups = computed< { subHeaderKey?: string; @@ -183,6 +195,8 @@ export class VaultListItemsContainerComponent implements AfterViewInit { /** * Title for the vault list item section. */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals title = input(undefined); /** @@ -191,33 +205,45 @@ export class VaultListItemsContainerComponent implements AfterViewInit { * The key must be added to the state definition in `vault-popup-section.service.ts` since the * collapsed state is stored locally. */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals collapsibleKey = input(undefined); /** * Optional description for the vault list item section. Will be shown below the title even when * no ciphers are available. */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals description = input(undefined); /** * Option to show a refresh button in the section header. */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals showRefresh = input(false, { transform: booleanAttribute }); /** * Event emitted when the refresh button is clicked. */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onRefresh = new EventEmitter(); /** * Flag indicating that the current tab location is blocked */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals currentURIIsBlocked = toSignal(this.vaultPopupAutofillService.currentTabIsOnBlocklist$); /** * Resolved i18n key to use for suggested cipher items */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals cipherItemTitleKey = computed(() => { return (cipher: CipherViewLike) => { const login = CipherViewLikeUtils.getLogin(cipher); @@ -233,11 +259,15 @@ export class VaultListItemsContainerComponent implements AfterViewInit { /** * Option to show the autofill button for each item. */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals showAutofillButton = input(false, { transform: booleanAttribute }); /** * Flag indicating whether the suggested cipher item autofill button should be shown or not */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals hideAutofillButton = computed( () => !this.showAutofillButton() || this.currentURIIsBlocked() || this.primaryActionAutofill(), ); @@ -245,22 +275,30 @@ export class VaultListItemsContainerComponent implements AfterViewInit { /** * Flag indicating whether the cipher item autofill menu options should be shown or not */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals hideAutofillMenuOptions = computed(() => this.currentURIIsBlocked() || this.showAutofillButton()); /** * Option to perform autofill operation as the primary action for autofill suggestions. */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals primaryActionAutofill = input(false, { transform: booleanAttribute }); /** * Remove the bottom margin from the bit-section in this component * (used for containers at the end of the page where bottom margin is not needed) */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals disableSectionMargin = input(false, { transform: booleanAttribute }); /** * Remove the description margin */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals disableDescriptionMargin = input(false, { transform: booleanAttribute }); /** @@ -275,6 +313,8 @@ export class VaultListItemsContainerComponent implements AfterViewInit { return collections[0]?.name; } + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals protected autofillShortcutTooltip = signal(undefined); constructor( diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-password-history-v2/vault-password-history-v2.component.ts b/apps/browser/src/vault/popup/components/vault-v2/vault-password-history-v2/vault-password-history-v2.component.ts index f2764df7ba7..7b9f358c01c 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-password-history-v2/vault-password-history-v2.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-password-history-v2/vault-password-history-v2.component.ts @@ -18,6 +18,8 @@ import { PopupHeaderComponent } from "../../../../../platform/popup/layout/popup import { PopupPageComponent } from "../../../../../platform/popup/layout/popup-page.component"; import { PopupRouterCacheService } from "../../../../../platform/popup/view-cache/popup-router-cache.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: "vault-password-history-v2", templateUrl: "vault-password-history-v2.component.html", diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-search/vault-v2-search.component.ts b/apps/browser/src/vault/popup/components/vault-v2/vault-search/vault-v2-search.component.ts index 72df3cba41a..c254c290915 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-search/vault-v2-search.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-search/vault-v2-search.component.ts @@ -10,6 +10,8 @@ import { SearchModule } from "@bitwarden/components"; import { VaultPopupItemsService } from "../../../services/vault-popup-items.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({ imports: [CommonModule, SearchModule, JslibModule, FormsModule], selector: "app-vault-v2-search", diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.ts b/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.ts index 604cc6b73ef..2dd6c1a0ce1 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.ts @@ -64,6 +64,8 @@ const VaultState = { type VaultState = UnionOfValues; +// 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", templateUrl: "vault-v2.component.html", @@ -89,6 +91,8 @@ type VaultState = UnionOfValues; ], }) export class VaultV2Component implements OnInit, AfterViewInit, OnDestroy { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChild(CdkVirtualScrollableElement) virtualScrollElement?: CdkVirtualScrollableElement; NudgeType = NudgeType; 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 915a27e4fd1..30074777e83 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 @@ -76,6 +76,8 @@ type LoadAction = | typeof COPY_VERIFICATION_CODE_ID | typeof UPDATE_PASSWORD; +// 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-view-v2", templateUrl: "view-v2.component.html", diff --git a/apps/browser/src/vault/popup/services/vault-popup-section.service.ts b/apps/browser/src/vault/popup/services/vault-popup-section.service.ts index ed641e0cdf7..b93eda72506 100644 --- a/apps/browser/src/vault/popup/services/vault-popup-section.service.ts +++ b/apps/browser/src/vault/popup/services/vault-popup-section.service.ts @@ -31,7 +31,7 @@ export class VaultPopupSectionService { private vaultPopupItemsService = inject(VaultPopupItemsService); private stateProvider = inject(StateProvider); - private hasFilterOrSearchApplied = toSignal( + private readonly hasFilterOrSearchApplied = toSignal( this.vaultPopupItemsService.hasFilterApplied$.pipe(map((hasFilter) => hasFilter)), ); @@ -40,7 +40,7 @@ export class VaultPopupSectionService { * application-applied overrides. * `null` means there is no current override */ - private temporaryStateOverride = signal | null>(null); + private readonly temporaryStateOverride = signal | null>(null); constructor() { effect( @@ -71,7 +71,7 @@ export class VaultPopupSectionService { * Stored disk state for the open/close state of the sections, with an initial value provided * if the stored disk state does not yet exist. */ - private sectionOpenStoredState = toSignal( + private readonly sectionOpenStoredState = toSignal( this.sectionOpenStateProvider.state$.pipe(map((sectionOpen) => sectionOpen ?? INITIAL_OPEN)), // Indicates that the state value is loading { initialValue: null }, @@ -81,7 +81,7 @@ export class VaultPopupSectionService { * Indicates the current open/close display state of each section, accounting for temporary * non-persisted overrides. */ - sectionOpenDisplayState: Signal> = computed(() => ({ + readonly sectionOpenDisplayState: Signal> = computed(() => ({ ...this.sectionOpenStoredState(), ...this.temporaryStateOverride(), })); diff --git a/apps/browser/src/vault/popup/settings/appearance-v2.component.spec.ts b/apps/browser/src/vault/popup/settings/appearance-v2.component.spec.ts index 738ec3ae1ff..9e1beab5787 100644 --- a/apps/browser/src/vault/popup/settings/appearance-v2.component.spec.ts +++ b/apps/browser/src/vault/popup/settings/appearance-v2.component.spec.ts @@ -22,20 +22,30 @@ import { VaultPopupCopyButtonsService } from "../services/vault-popup-copy-butto import { AppearanceV2Component } from "./appearance-v2.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: "popup-header", template: ``, }) class MockPopupHeaderComponent { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() pageTitle: string; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() backAction: () => void; } +// 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: "popup-page", template: ``, }) class MockPopupPageComponent { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() loading: boolean; } diff --git a/apps/browser/src/vault/popup/settings/appearance-v2.component.ts b/apps/browser/src/vault/popup/settings/appearance-v2.component.ts index 23a609bd008..e6515ae7461 100644 --- a/apps/browser/src/vault/popup/settings/appearance-v2.component.ts +++ b/apps/browser/src/vault/popup/settings/appearance-v2.component.ts @@ -33,6 +33,8 @@ import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.co import { PopupSizeService } from "../../../platform/popup/layout/popup-size.service"; import { VaultPopupCopyButtonsService } from "../services/vault-popup-copy-buttons.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({ templateUrl: "./appearance-v2.component.html", imports: [ diff --git a/apps/browser/src/vault/popup/settings/archive.component.ts b/apps/browser/src/vault/popup/settings/archive.component.ts index 2044389f295..58925eda428 100644 --- a/apps/browser/src/vault/popup/settings/archive.component.ts +++ b/apps/browser/src/vault/popup/settings/archive.component.ts @@ -33,6 +33,8 @@ import { PopOutComponent } from "../../../platform/popup/components/pop-out.comp import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component"; import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.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({ templateUrl: "archive.component.html", standalone: true, diff --git a/apps/browser/src/vault/popup/settings/download-bitwarden.component.ts b/apps/browser/src/vault/popup/settings/download-bitwarden.component.ts index d23d00a1ad7..109f3ea0404 100644 --- a/apps/browser/src/vault/popup/settings/download-bitwarden.component.ts +++ b/apps/browser/src/vault/popup/settings/download-bitwarden.component.ts @@ -13,6 +13,8 @@ import { PopOutComponent } from "../../../platform/popup/components/pop-out.comp import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component"; import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.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({ templateUrl: "download-bitwarden.component.html", imports: [ diff --git a/apps/browser/src/vault/popup/settings/folders-v2.component.spec.ts b/apps/browser/src/vault/popup/settings/folders-v2.component.spec.ts index d1450667fa8..3cb5503ed89 100644 --- a/apps/browser/src/vault/popup/settings/folders-v2.component.spec.ts +++ b/apps/browser/src/vault/popup/settings/folders-v2.component.spec.ts @@ -21,20 +21,30 @@ import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-heade import { FoldersV2Component } from "./folders-v2.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: "popup-header", template: ``, }) class MockPopupHeaderComponent { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() pageTitle: string = ""; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() backAction: () => void = () => {}; } +// 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: "popup-footer", template: ``, }) class MockPopupFooterComponent { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() pageTitle: string = ""; } diff --git a/apps/browser/src/vault/popup/settings/folders-v2.component.ts b/apps/browser/src/vault/popup/settings/folders-v2.component.ts index b749f651d53..20a816e7297 100644 --- a/apps/browser/src/vault/popup/settings/folders-v2.component.ts +++ b/apps/browser/src/vault/popup/settings/folders-v2.component.ts @@ -22,6 +22,8 @@ import { PopOutComponent } from "../../../platform/popup/components/pop-out.comp import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component"; import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.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({ templateUrl: "./folders-v2.component.html", imports: [ diff --git a/apps/browser/src/vault/popup/settings/more-from-bitwarden-page-v2.component.ts b/apps/browser/src/vault/popup/settings/more-from-bitwarden-page-v2.component.ts index ec7a73a3bc3..2f9fae43da7 100644 --- a/apps/browser/src/vault/popup/settings/more-from-bitwarden-page-v2.component.ts +++ b/apps/browser/src/vault/popup/settings/more-from-bitwarden-page-v2.component.ts @@ -17,6 +17,8 @@ import { PopOutComponent } from "../../../platform/popup/components/pop-out.comp import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component"; import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.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({ templateUrl: "more-from-bitwarden-page-v2.component.html", imports: [ diff --git a/apps/browser/src/vault/popup/settings/trash-list-items-container/trash-list-items-container.component.ts b/apps/browser/src/vault/popup/settings/trash-list-items-container/trash-list-items-container.component.ts index 1676fea3c01..70ba6842a0d 100644 --- a/apps/browser/src/vault/popup/settings/trash-list-items-container/trash-list-items-container.component.ts +++ b/apps/browser/src/vault/popup/settings/trash-list-items-container/trash-list-items-container.component.ts @@ -53,9 +53,13 @@ export class TrashListItemsContainerComponent { /** * The list of trashed items to display. */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() ciphers: PopupCipherViewLike[] = []; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() headerText: string; diff --git a/apps/browser/src/vault/popup/settings/vault-settings-v2.component.ts b/apps/browser/src/vault/popup/settings/vault-settings-v2.component.ts index 92cbf951ead..ff6e9b4065c 100644 --- a/apps/browser/src/vault/popup/settings/vault-settings-v2.component.ts +++ b/apps/browser/src/vault/popup/settings/vault-settings-v2.component.ts @@ -19,6 +19,8 @@ import { PopOutComponent } from "../../../platform/popup/components/pop-out.comp import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component"; import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.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({ templateUrl: "vault-settings-v2.component.html", imports: [ @@ -37,12 +39,12 @@ export class VaultSettingsV2Component implements OnInit, OnDestroy { private userId$ = this.accountService.activeAccount$.pipe(getUserId); // Check if user is premium user, they will be able to archive items - protected userCanArchive = toSignal( + protected readonly userCanArchive = toSignal( this.userId$.pipe(switchMap((userId) => this.cipherArchiveService.userCanArchive$(userId))), ); // Check if user has archived items (does not check if user is premium) - protected showArchiveFilter = toSignal( + protected readonly showArchiveFilter = toSignal( this.userId$.pipe(switchMap((userId) => this.cipherArchiveService.showArchiveVault$(userId))), ); diff --git a/apps/desktop/src/vault/app/vault/assign-collections/assign-collections-desktop.component.ts b/apps/desktop/src/vault/app/vault/assign-collections/assign-collections-desktop.component.ts index d81f1662c6c..5af1f96a569 100644 --- a/apps/desktop/src/vault/app/vault/assign-collections/assign-collections-desktop.component.ts +++ b/apps/desktop/src/vault/app/vault/assign-collections/assign-collections-desktop.component.ts @@ -10,6 +10,8 @@ import { CollectionAssignmentResult, } 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({ standalone: true, templateUrl: "./assign-collections-desktop.component.html", diff --git a/apps/desktop/src/vault/app/vault/credential-generator-dialog.component.ts b/apps/desktop/src/vault/app/vault/credential-generator-dialog.component.ts index 26349920106..775ef55b3eb 100644 --- a/apps/desktop/src/vault/app/vault/credential-generator-dialog.component.ts +++ b/apps/desktop/src/vault/app/vault/credential-generator-dialog.component.ts @@ -39,6 +39,8 @@ export const CredentialGeneratorDialogAction = { type CredentialGeneratorDialogAction = UnionOfValues; +// 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: "credential-generator-dialog", templateUrl: "credential-generator-dialog.component.html", diff --git a/apps/desktop/src/vault/app/vault/item-footer.component.ts b/apps/desktop/src/vault/app/vault/item-footer.component.ts index 5ebd657cee0..0034bd9a43c 100644 --- a/apps/desktop/src/vault/app/vault/item-footer.component.ts +++ b/apps/desktop/src/vault/app/vault/item-footer.component.ts @@ -25,22 +25,46 @@ import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cip import { ButtonComponent, ButtonModule, DialogService, ToastService } from "@bitwarden/components"; import { ArchiveCipherUtilitiesService, PasswordRepromptService } 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-vault-item-footer", templateUrl: "item-footer.component.html", imports: [ButtonModule, CommonModule, JslibModule], }) export class ItemFooterComponent implements OnInit, OnChanges { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ required: true }) cipher: CipherView = new CipherView(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() collectionId: string | null = null; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ required: true }) action: string = "view"; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() masterPasswordAlreadyPrompted: boolean = false; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onEdit = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onClone = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onDelete = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onRestore = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onCancel = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onArchiveToggle = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChild("submitBtn", { static: false }) submitBtn: ButtonComponent | null = null; activeUserId: UserId | null = null; diff --git a/apps/desktop/src/vault/app/vault/vault-filter/filters/collection-filter.component.ts b/apps/desktop/src/vault/app/vault/vault-filter/filters/collection-filter.component.ts index 22372410e5b..015b301efdb 100644 --- a/apps/desktop/src/vault/app/vault/vault-filter/filters/collection-filter.component.ts +++ b/apps/desktop/src/vault/app/vault/vault-filter/filters/collection-filter.component.ts @@ -2,6 +2,8 @@ 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", diff --git a/apps/desktop/src/vault/app/vault/vault-filter/filters/folder-filter.component.ts b/apps/desktop/src/vault/app/vault/vault-filter/filters/folder-filter.component.ts index d7364808f6d..f340e4082b8 100644 --- a/apps/desktop/src/vault/app/vault/vault-filter/filters/folder-filter.component.ts +++ b/apps/desktop/src/vault/app/vault/vault-filter/filters/folder-filter.component.ts @@ -2,6 +2,8 @@ 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", diff --git a/apps/desktop/src/vault/app/vault/vault-filter/filters/organization-filter.component.ts b/apps/desktop/src/vault/app/vault/vault-filter/filters/organization-filter.component.ts index 503c2b2ec6e..99338ddbb7c 100644 --- a/apps/desktop/src/vault/app/vault/vault-filter/filters/organization-filter.component.ts +++ b/apps/desktop/src/vault/app/vault/vault-filter/filters/organization-filter.component.ts @@ -9,6 +9,8 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic 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", diff --git a/apps/desktop/src/vault/app/vault/vault-filter/filters/status-filter.component.ts b/apps/desktop/src/vault/app/vault/vault-filter/filters/status-filter.component.ts index 276b11d7138..db546f76a2c 100644 --- a/apps/desktop/src/vault/app/vault/vault-filter/filters/status-filter.component.ts +++ b/apps/desktop/src/vault/app/vault/vault-filter/filters/status-filter.component.ts @@ -2,6 +2,8 @@ import { Component } from "@angular/core"; import { StatusFilterComponent as BaseStatusFilterComponent } from "@bitwarden/angular/vault/vault-filter/components/status-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-status-filter", templateUrl: "status-filter.component.html", diff --git a/apps/desktop/src/vault/app/vault/vault-filter/filters/type-filter.component.ts b/apps/desktop/src/vault/app/vault/vault-filter/filters/type-filter.component.ts index 27e7d5c5ecb..fbab7ce4667 100644 --- a/apps/desktop/src/vault/app/vault/vault-filter/filters/type-filter.component.ts +++ b/apps/desktop/src/vault/app/vault/vault-filter/filters/type-filter.component.ts @@ -5,6 +5,8 @@ import { TypeFilterComponent as BaseTypeFilterComponent } from "@bitwarden/angul 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", diff --git a/apps/desktop/src/vault/app/vault/vault-filter/vault-filter.component.ts b/apps/desktop/src/vault/app/vault/vault-filter/vault-filter.component.ts index 161d22687e8..d7c5bafc3a4 100644 --- a/apps/desktop/src/vault/app/vault/vault-filter/vault-filter.component.ts +++ b/apps/desktop/src/vault/app/vault/vault-filter/vault-filter.component.ts @@ -2,6 +2,8 @@ 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", diff --git a/apps/desktop/src/vault/app/vault/vault-items-v2.component.ts b/apps/desktop/src/vault/app/vault/vault-items-v2.component.ts index 290a38ac08c..d312d49277a 100644 --- a/apps/desktop/src/vault/app/vault/vault-items-v2.component.ts +++ b/apps/desktop/src/vault/app/vault/vault-items-v2.component.ts @@ -21,6 +21,8 @@ import { MenuModule } from "@bitwarden/components"; import { SearchBarService } from "../../../app/layout/search/search-bar.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-vault-items-v2", templateUrl: "vault-items-v2.component.html", 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 b7b0bf2e1b2..19c9cffeeb2 100644 --- a/apps/desktop/src/vault/app/vault/vault-v2.component.ts +++ b/apps/desktop/src/vault/app/vault/vault-v2.component.ts @@ -94,6 +94,8 @@ import { VaultItemsV2Component } from "./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", templateUrl: "vault-v2.component.html", @@ -138,12 +140,20 @@ const BroadcasterSubscriptionId = "VaultComponent"; export class VaultV2Component 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; diff --git a/apps/web/src/app/vault/components/assign-collections/assign-collections-web.component.ts b/apps/web/src/app/vault/components/assign-collections/assign-collections-web.component.ts index 753d2708e60..2b97222fb14 100644 --- a/apps/web/src/app/vault/components/assign-collections/assign-collections-web.component.ts +++ b/apps/web/src/app/vault/components/assign-collections/assign-collections-web.component.ts @@ -12,6 +12,8 @@ import { import { SharedModule } from "../../../shared"; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ imports: [SharedModule, AssignCollectionsComponent, PluralizePipe], templateUrl: "./assign-collections-web.component.html", diff --git a/apps/web/src/app/vault/components/browser-extension-prompt/browser-extension-prompt-install.component.ts b/apps/web/src/app/vault/components/browser-extension-prompt/browser-extension-prompt-install.component.ts index 005fbb1b14d..2444ed1f707 100644 --- a/apps/web/src/app/vault/components/browser-extension-prompt/browser-extension-prompt-install.component.ts +++ b/apps/web/src/app/vault/components/browser-extension-prompt/browser-extension-prompt-install.component.ts @@ -25,6 +25,8 @@ const WebStoreUrls: Partial> = { "https://microsoftedge.microsoft.com/addons/detail/jbkfoedolllekgbhcbcoahefnbanhhlh", }; +// 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: "vault-browser-extension-prompt-install", templateUrl: "./browser-extension-prompt-install.component.html", diff --git a/apps/web/src/app/vault/components/browser-extension-prompt/browser-extension-prompt.component.ts b/apps/web/src/app/vault/components/browser-extension-prompt/browser-extension-prompt.component.ts index f3a5b9aa532..cb927d0848c 100644 --- a/apps/web/src/app/vault/components/browser-extension-prompt/browser-extension-prompt.component.ts +++ b/apps/web/src/app/vault/components/browser-extension-prompt/browser-extension-prompt.component.ts @@ -14,6 +14,8 @@ import { } from "../../services/browser-extension-prompt.service"; import { ManuallyOpenExtensionComponent } from "../manually-open-extension/manually-open-extension.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: "vault-browser-extension-prompt", templateUrl: "./browser-extension-prompt.component.html", diff --git a/apps/web/src/app/vault/components/manually-open-extension/manually-open-extension.component.ts b/apps/web/src/app/vault/components/manually-open-extension/manually-open-extension.component.ts index 646ff76311e..6105aeacf9c 100644 --- a/apps/web/src/app/vault/components/manually-open-extension/manually-open-extension.component.ts +++ b/apps/web/src/app/vault/components/manually-open-extension/manually-open-extension.component.ts @@ -4,6 +4,8 @@ import { BitwardenIcon } from "@bitwarden/assets/svg"; import { IconModule } from "@bitwarden/components"; import { I18nPipe } from "@bitwarden/ui-common"; +// 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: "vault-manually-open-extension", templateUrl: "./manually-open-extension.component.html", diff --git a/apps/web/src/app/vault/components/setup-extension/add-extension-later-dialog.component.ts b/apps/web/src/app/vault/components/setup-extension/add-extension-later-dialog.component.ts index 5f4e3f586f5..9237d70b996 100644 --- a/apps/web/src/app/vault/components/setup-extension/add-extension-later-dialog.component.ts +++ b/apps/web/src/app/vault/components/setup-extension/add-extension-later-dialog.component.ts @@ -16,6 +16,8 @@ export type AddExtensionLaterDialogData = { onDismiss: () => void; }; +// 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: "vault-add-extension-later-dialog", templateUrl: "./add-extension-later-dialog.component.html", diff --git a/apps/web/src/app/vault/components/setup-extension/add-extension-videos.component.ts b/apps/web/src/app/vault/components/setup-extension/add-extension-videos.component.ts index c9c222e8e64..9a974a395f0 100644 --- a/apps/web/src/app/vault/components/setup-extension/add-extension-videos.component.ts +++ b/apps/web/src/app/vault/components/setup-extension/add-extension-videos.component.ts @@ -6,12 +6,16 @@ import { debounceTime, fromEvent } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { DarkImageSourceDirective } 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: "vault-add-extension-videos", templateUrl: "./add-extension-videos.component.html", imports: [CommonModule, JslibModule, DarkImageSourceDirective], }) export class AddExtensionVideosComponent { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChildren("video", { read: ElementRef }) protected videoElements!: QueryList< ElementRef >; diff --git a/apps/web/src/app/vault/components/setup-extension/setup-extension.component.ts b/apps/web/src/app/vault/components/setup-extension/setup-extension.component.ts index 012ac370c70..b5c0d096944 100644 --- a/apps/web/src/app/vault/components/setup-extension/setup-extension.component.ts +++ b/apps/web/src/app/vault/components/setup-extension/setup-extension.component.ts @@ -42,6 +42,8 @@ export const SetupExtensionState = { type SetupExtensionState = UnionOfValues; +// 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: "vault-setup-extension", templateUrl: "./setup-extension.component.html", 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 b48db2bba91..98922fb114f 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 @@ -129,6 +129,8 @@ export const VaultItemDialogResult = { export type VaultItemDialogResult = UnionOfValues; +// 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-item-dialog", templateUrl: "vault-item-dialog.component.html", @@ -159,9 +161,13 @@ export class VaultItemDialogComponent implements OnInit, OnDestroy { * Reference to the dialog content element. Used to scroll to the top of the dialog when switching modes. * @protected */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChild("dialogContent") protected dialogContent: ElementRef; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChild(CipherFormComponent) cipherFormComponent!: CipherFormComponent; /** 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 1e4b33777bb..4883043ddd6 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 @@ -28,6 +28,8 @@ import { import { VaultItemEvent } from "./vault-item-event"; import { RowHeightClass } from "./vault-items.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: "tr[appVaultCipherRow]", templateUrl: "vault-cipher-row.component.html", @@ -36,42 +38,86 @@ import { RowHeightClass } from "./vault-items.component"; export class VaultCipherRowComponent implements OnInit { protected RowHeightClass = RowHeightClass; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChild(MenuTriggerForDirective, { static: false }) menuTrigger: MenuTriggerForDirective; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() disabled: boolean; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() cipher: C; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() showOwner: boolean; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() showCollections: boolean; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() showGroups: boolean; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() showPremiumFeatures: boolean; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() useEvents: boolean; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() cloneable: boolean; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() organizations: Organization[]; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() collections: CollectionView[]; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() viewingOrgVault: boolean; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() canEditCipher: boolean; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() canAssignCollections: boolean; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() canManageCollection: boolean; /** * uses new permission delete logic from PM-15493 */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() canDeleteCipher: boolean; /** * uses new permission restore logic from PM-15493 */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() canRestoreCipher: boolean; /** * user has archive permissions */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() userCanArchive: boolean; /** * Enforge Org Data Ownership Policy Status */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() enforceOrgDataOwnershipPolicy: boolean; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onEvent = new EventEmitter>(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() checked: boolean; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() checkedToggled = new EventEmitter(); protected CipherType = CipherType; 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 746024eced8..daa981d509a 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 @@ -23,6 +23,8 @@ import { import { VaultItemEvent } from "./vault-item-event"; import { RowHeightClass } from "./vault-items.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: "tr[appVaultCollectionRow]", templateUrl: "vault-collection-row.component.html", @@ -34,23 +36,53 @@ export class VaultCollectionRowComponent { protected CollectionPermission = CollectionPermission; protected DefaultCollectionType = CollectionTypes.DefaultUserCollection; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChild(MenuTriggerForDirective, { static: false }) menuTrigger: MenuTriggerForDirective; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() disabled: boolean; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() collection: CollectionView; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() showOwner: boolean; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() showCollections: boolean; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() showGroups: boolean; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() canEditCollection: boolean; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() canDeleteCollection: boolean; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() canViewCollectionInfo: boolean; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() organizations: Organization[]; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() groups: GroupView[]; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() showPermissionsColumn: boolean; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onEvent = new EventEmitter>(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() checked: boolean; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() checkedToggled = new EventEmitter(); constructor(private i18nService: I18nService) {} 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 81ad29db9dd..9ea4c209009 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 @@ -36,6 +36,8 @@ const MaxSelectionCount = 500; type ItemPermission = CollectionPermission | "NoAccess"; +// 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-items", templateUrl: "vault-items.component.html", @@ -44,32 +46,76 @@ type ItemPermission = CollectionPermission | "NoAccess"; export class VaultItemsComponent { protected RowHeight = RowHeight; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() disabled: boolean; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() showOwner: boolean; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() showCollections: boolean; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() showGroups: boolean; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() useEvents: boolean; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() showPremiumFeatures: boolean; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() showBulkMove: boolean; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() showBulkTrashOptions: boolean; // Encompasses functionality only available from the organization vault context + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() showAdminActions = false; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() allOrganizations: Organization[] = []; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() allCollections: CollectionView[] = []; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() allGroups: GroupView[] = []; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() showBulkEditCollectionAccess = false; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() showBulkAddToCollections = false; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() showPermissionsColumn = false; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() viewingOrgVault: boolean; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() addAccessStatus: number; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() addAccessToggle: boolean; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() activeCollection: CollectionView | undefined; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() userCanArchive: boolean; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() enforceOrgDataOwnershipPolicy: boolean; private readonly restrictedPolicies = toSignal(this.restrictedItemTypesService.restricted$); private _ciphers?: C[] = []; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() get ciphers(): C[] { return this._ciphers; } @@ -79,6 +125,8 @@ export class VaultItemsComponent { } private _collections?: CollectionView[] = []; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() get collections(): CollectionView[] { return this._collections; } @@ -87,6 +135,8 @@ export class VaultItemsComponent { this.refreshItems(); } + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onEvent = new EventEmitter>(); protected editableItems: VaultItem[] = []; diff --git a/apps/web/src/app/vault/components/web-generator-dialog/web-generator-dialog.component.spec.ts b/apps/web/src/app/vault/components/web-generator-dialog/web-generator-dialog.component.spec.ts index afb32738901..c2d6c87d865 100644 --- a/apps/web/src/app/vault/components/web-generator-dialog/web-generator-dialog.component.spec.ts +++ b/apps/web/src/app/vault/components/web-generator-dialog/web-generator-dialog.component.spec.ts @@ -16,14 +16,24 @@ import { WebVaultGeneratorDialogResult, } from "./web-generator-dialog.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: "vault-cipher-form-generator", template: "", }) class MockCipherFormGenerator { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() type: "password" | "username" = "password"; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() algorithmSelected: EventEmitter = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() uri?: string; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() valueGenerated = new EventEmitter(); } diff --git a/apps/web/src/app/vault/components/web-generator-dialog/web-generator-dialog.component.ts b/apps/web/src/app/vault/components/web-generator-dialog/web-generator-dialog.component.ts index 7454b4d10f0..957f72015a5 100644 --- a/apps/web/src/app/vault/components/web-generator-dialog/web-generator-dialog.component.ts +++ b/apps/web/src/app/vault/components/web-generator-dialog/web-generator-dialog.component.ts @@ -34,6 +34,8 @@ export const WebVaultGeneratorDialogAction = { type WebVaultGeneratorDialogAction = UnionOfValues; +// 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: "web-vault-generator-dialog", templateUrl: "./web-generator-dialog.component.html", diff --git a/apps/web/src/app/vault/individual-vault/add-edit-v2.component.ts b/apps/web/src/app/vault/individual-vault/add-edit-v2.component.ts index c09238e7953..41c922cf4fe 100644 --- a/apps/web/src/app/vault/individual-vault/add-edit-v2.component.ts +++ b/apps/web/src/app/vault/individual-vault/add-edit-v2.component.ts @@ -62,6 +62,8 @@ export interface AddEditCipherDialogCloseResult { * Component for viewing a cipher, presented in a dialog. * @deprecated Use the VaultItemDialogComponent instead. */ +// 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-add-edit-v2", templateUrl: "add-edit-v2.component.html", 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 78abad1ebf8..3856bb65324 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 @@ -52,6 +52,8 @@ export const openBulkDeleteDialog = ( ); }; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ templateUrl: "bulk-delete-dialog.component.html", standalone: false, diff --git a/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-move-dialog/bulk-move-dialog.component.ts b/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-move-dialog/bulk-move-dialog.component.ts index ef43a3ead81..f76e505f87d 100644 --- a/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-move-dialog/bulk-move-dialog.component.ts +++ b/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-move-dialog/bulk-move-dialog.component.ts @@ -46,6 +46,8 @@ export const openBulkMoveDialog = ( ); }; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ templateUrl: "bulk-move-dialog.component.html", standalone: false, 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 79fae4d5b1f..19c462193e1 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 @@ -10,14 +10,22 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { Utils } from "@bitwarden/common/platform/misc/utils"; import { OrganizationId } from "@bitwarden/sdk-internal"; +// 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-org-badge", templateUrl: "organization-name-badge.component.html", standalone: false, }) export class OrganizationNameBadgeComponent implements OnChanges { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() organizationId?: OrganizationId | string; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() organizationName: string; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() disabled: boolean; // Need a separate variable or we get weird behavior when used as part of cdk virtual scrolling diff --git a/apps/web/src/app/vault/individual-vault/vault-banners/vault-banners.component.ts b/apps/web/src/app/vault/individual-vault/vault-banners/vault-banners.component.ts index 78624b3662c..80626d258f8 100644 --- a/apps/web/src/app/vault/individual-vault/vault-banners/vault-banners.component.ts +++ b/apps/web/src/app/vault/individual-vault/vault-banners/vault-banners.component.ts @@ -17,6 +17,8 @@ import { SharedModule } from "../../../shared"; import { VaultBannersService, VisibleVaultBanner } from "./services/vault-banners.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-vault-banners", templateUrl: "./vault-banners.component.html", @@ -32,6 +34,8 @@ export class VaultBannersComponent implements OnInit { visibleBanners: VisibleVaultBanner[] = []; premiumBannerVisible$: Observable; VisibleVaultBanner = VisibleVaultBanner; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() organizations: Organization[] = []; private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id)); 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 fe5ef281b2d..981e5703cb3 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 @@ -40,6 +40,8 @@ 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 @Component({ selector: "app-organization-options", templateUrl: "organization-options.component.html", 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 180152f054c..0326f8455a6 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 @@ -44,6 +44,8 @@ import { import { OrganizationOptionsComponent } from "./organization-options.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", @@ -51,10 +53,18 @@ import { OrganizationOptionsComponent } from "./organization-options.component"; }) export class VaultFilterComponent implements OnInit, OnDestroy { filters?: VaultFilterList; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() activeFilter: VaultFilter = new VaultFilter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onEditFolder = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() searchText = ""; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() searchTextChanged = new EventEmitter(); isLoaded = false; 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 1a0a96fa19c..e8cf49c3208 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 @@ -13,6 +13,8 @@ import { VaultFilterService } from "../../services/abstractions/vault-filter.ser import { VaultFilterSection, VaultFilterType } from "../models/vault-filter-section.type"; import { VaultFilter } from "../models/vault-filter.model"; +// 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-filter-section", templateUrl: "vault-filter-section.component.html", @@ -22,7 +24,11 @@ export class VaultFilterSectionComponent implements OnInit, OnDestroy { private destroy$ = new Subject(); private activeUserId$ = getUserId(this.accountService.activeAccount$); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() activeFilter: VaultFilter; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() section: VaultFilterSection; data: TreeNode; 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 929a8d07881..8fa801f5dc0 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 @@ -60,33 +60,53 @@ export class VaultHeaderComponent { * Boolean to determine the loading state of the header. * Shows a loading spinner if set to true */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() loading: boolean = true; /** Current active filter */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() filter: RoutedVaultFilterModel | undefined; /** All organizations that can be shown */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() organizations: Organization[] = []; /** Currently selected collection */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() collection?: TreeNode; /** Whether 'Collection' option is shown in the 'New' dropdown */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() canCreateCollections: boolean = false; /** Emits an event when the new item button is clicked in the header */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onAddCipher = new EventEmitter(); /** Emits an event when the new collection button is clicked in the 'New' dropdown menu */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onAddCollection = new EventEmitter(); /** Emits an event when the new folder button is clicked in the 'New' dropdown menu */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onAddFolder = new EventEmitter(); /** Emits an event when the edit collection button is clicked in the header */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onEditCollection = new EventEmitter<{ tab: CollectionDialogTabType }>(); /** Emits an event when the delete collection button is clicked in the header */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onDeleteCollection = new EventEmitter(); constructor( diff --git a/apps/web/src/app/vault/individual-vault/vault-onboarding/vault-onboarding.component.ts b/apps/web/src/app/vault/individual-vault/vault-onboarding/vault-onboarding.component.ts index 8dc442abe2e..503c088c3da 100644 --- a/apps/web/src/app/vault/individual-vault/vault-onboarding/vault-onboarding.component.ts +++ b/apps/web/src/app/vault/individual-vault/vault-onboarding/vault-onboarding.component.ts @@ -32,6 +32,8 @@ import { OnboardingModule } from "../../../shared/components/onboarding/onboardi import { VaultOnboardingService as VaultOnboardingServiceAbstraction } from "./services/abstraction/vault-onboarding.service"; import { VaultOnboardingService, VaultOnboardingTasks } from "./services/vault-onboarding.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({ imports: [OnboardingModule, CommonModule, JslibModule, LinkModule], providers: [ @@ -44,8 +46,14 @@ import { VaultOnboardingService, VaultOnboardingTasks } from "./services/vault-o templateUrl: "vault-onboarding.component.html", }) export class VaultOnboardingComponent implements OnInit, OnChanges, OnDestroy { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() ciphers: CipherViewLike[]; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() orgs: Organization[]; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onAddCipher = new EventEmitter(); extensionUrl: string; 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 7ea1d02110d..b9a3bbfdd19 100644 --- a/apps/web/src/app/vault/individual-vault/vault.component.ts +++ b/apps/web/src/app/vault/individual-vault/vault.component.ts @@ -154,6 +154,8 @@ type EmptyStateItem = { type EmptyStateMap = Record; +// 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", templateUrl: "vault.component.html", @@ -173,7 +175,11 @@ type EmptyStateMap = Record; ], }) export class VaultComponent implements OnInit, OnDestroy { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChild("vaultFilter", { static: true }) filterComponent: VaultFilterComponent; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChild("vaultItems", { static: false }) vaultItemsComponent: VaultItemsComponent; trashCleanupWarning: string = null; diff --git a/apps/web/src/app/vault/settings/purge-vault.component.ts b/apps/web/src/app/vault/settings/purge-vault.component.ts index 4c58a27adb7..a81c14e9cc4 100644 --- a/apps/web/src/app/vault/settings/purge-vault.component.ts +++ b/apps/web/src/app/vault/settings/purge-vault.component.ts @@ -25,6 +25,8 @@ export interface PurgeVaultDialogData { organizationId: string; } +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ templateUrl: "purge-vault.component.html", imports: [SharedModule, UserVerificationModule], diff --git a/libs/angular/src/vault/components/folder-add-edit.component.ts b/libs/angular/src/vault/components/folder-add-edit.component.ts index acf7511284d..486585b810c 100644 --- a/libs/angular/src/vault/components/folder-add-edit.component.ts +++ b/libs/angular/src/vault/components/folder-add-edit.component.ts @@ -16,8 +16,14 @@ import { KeyService } from "@bitwarden/key-management"; @Directive() export class FolderAddEditComponent implements OnInit { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() folderId: string; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onSavedFolder = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onDeletedFolder = new EventEmitter(); editMode = false; diff --git a/libs/angular/src/vault/components/icon.component.ts b/libs/angular/src/vault/components/icon.component.ts index ee2b535d716..851cec5656b 100644 --- a/libs/angular/src/vault/components/icon.component.ts +++ b/libs/angular/src/vault/components/icon.component.ts @@ -25,14 +25,14 @@ export class IconComponent { /** * The cipher to display the icon for. */ - cipher = input.required(); + readonly cipher = input.required(); /** * coloredIcon will adjust the size of favicons and the colors of the text icon when user is in the item details view. */ - coloredIcon = input(false); + readonly coloredIcon = input(false); - imageLoaded = signal(false); + readonly imageLoaded = signal(false); protected data$: Observable; diff --git a/libs/angular/src/vault/components/spotlight/spotlight.component.ts b/libs/angular/src/vault/components/spotlight/spotlight.component.ts index 3c64318a900..a912e4ce11b 100644 --- a/libs/angular/src/vault/components/spotlight/spotlight.component.ts +++ b/libs/angular/src/vault/components/spotlight/spotlight.component.ts @@ -4,6 +4,8 @@ import { Component, EventEmitter, Input, Output } from "@angular/core"; import { ButtonModule, IconButtonModule, TypographyModule } from "@bitwarden/components"; import { I18nPipe } from "@bitwarden/ui-common"; +// 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: "bit-spotlight", templateUrl: "spotlight.component.html", @@ -11,16 +13,30 @@ import { I18nPipe } from "@bitwarden/ui-common"; }) export class SpotlightComponent { // The title of the component + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ required: true }) title: string | null = null; // The subtitle of the component + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() subtitle?: string | null = null; // The text to display on the button + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() buttonText?: string; // Wheter the component can be dismissed, if true, the component will not show a close button + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() persistent = false; // Optional icon to display on the button + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() buttonIcon: string | null = null; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onDismiss = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onButtonClick = new EventEmitter(); handleButtonClick(event: MouseEvent): void { diff --git a/libs/angular/src/vault/components/vault-items.component.ts b/libs/angular/src/vault/components/vault-items.component.ts index 414ec1509ed..0254ddabf2b 100644 --- a/libs/angular/src/vault/components/vault-items.component.ts +++ b/libs/angular/src/vault/components/vault-items.component.ts @@ -31,10 +31,20 @@ import { @Directive() export class VaultItemsComponent implements OnDestroy { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() activeCipherId: string = null; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onCipherClicked = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onCipherRightClicked = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onAddCipher = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onAddCipherOptions = new EventEmitter(); loaded = false; 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 e9a6923c2fb..4d4037a3517 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 @@ -13,13 +13,25 @@ import { VaultFilter } from "../models/vault-filter.model"; @Directive() export class CollectionFilterComponent { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() hide = false; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() collapsedFilterNodes: Set; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() collectionNodes: DynamicTreeNode; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() activeFilter: VaultFilter; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onNodeCollapseStateChange: EventEmitter = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onFilterChange: EventEmitter = new EventEmitter(); DefaultCollectionType = CollectionTypes.DefaultUserCollection; diff --git a/libs/angular/src/vault/vault-filter/components/folder-filter.component.ts b/libs/angular/src/vault/vault-filter/components/folder-filter.component.ts index 45605d583aa..8c47a37b31b 100644 --- a/libs/angular/src/vault/vault-filter/components/folder-filter.component.ts +++ b/libs/angular/src/vault/vault-filter/components/folder-filter.component.ts @@ -11,15 +11,31 @@ import { VaultFilter } from "../models/vault-filter.model"; @Directive() export class FolderFilterComponent { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() hide = false; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() collapsedFilterNodes: Set; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() folderNodes: DynamicTreeNode; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() activeFilter: VaultFilter; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onNodeCollapseStateChange: EventEmitter = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onFilterChange: EventEmitter = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onAddFolder = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onEditFolder = new EventEmitter(); get folders() { diff --git a/libs/angular/src/vault/vault-filter/components/organization-filter.component.ts b/libs/angular/src/vault/vault-filter/components/organization-filter.component.ts index 45198d2bcc5..46be2df3884 100644 --- a/libs/angular/src/vault/vault-filter/components/organization-filter.component.ts +++ b/libs/angular/src/vault/vault-filter/components/organization-filter.component.ts @@ -11,15 +11,31 @@ import { VaultFilter } from "../models/vault-filter.model"; @Directive() export class OrganizationFilterComponent { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() hide = false; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() collapsedFilterNodes: Set; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() organizations: Organization[]; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() activeFilter: VaultFilter; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() activeOrganizationDataOwnership: boolean; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() activeSingleOrganizationPolicy: boolean; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onNodeCollapseStateChange: EventEmitter = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onFilterChange: EventEmitter = new EventEmitter(); get displayMode(): DisplayMode { diff --git a/libs/angular/src/vault/vault-filter/components/status-filter.component.ts b/libs/angular/src/vault/vault-filter/components/status-filter.component.ts index dc6a90f928d..6862019ab4e 100644 --- a/libs/angular/src/vault/vault-filter/components/status-filter.component.ts +++ b/libs/angular/src/vault/vault-filter/components/status-filter.component.ts @@ -7,10 +7,20 @@ import { VaultFilter } from "../models/vault-filter.model"; @Directive() export class StatusFilterComponent { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() hideFavorites = false; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() hideTrash = false; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() hideArchive = false; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onFilterChange: EventEmitter = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() activeFilter: VaultFilter; get show() { diff --git a/libs/angular/src/vault/vault-filter/components/type-filter.component.ts b/libs/angular/src/vault/vault-filter/components/type-filter.component.ts index 84cdf976309..a06be5e4b08 100644 --- a/libs/angular/src/vault/vault-filter/components/type-filter.component.ts +++ b/libs/angular/src/vault/vault-filter/components/type-filter.component.ts @@ -10,13 +10,25 @@ import { VaultFilter } from "../models/vault-filter.model"; @Directive() export class TypeFilterComponent { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() hide = false; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() collapsedFilterNodes: Set; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() selectedCipherType: CipherType = null; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() activeFilter: VaultFilter; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onNodeCollapseStateChange: EventEmitter = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onFilterChange: EventEmitter = new EventEmitter(); readonly typesNode: TopLevelTreeNode = { 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 9199c53bfcb..9b1d6286a9a 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 @@ -22,15 +22,33 @@ import { VaultFilter } from "../models/vault-filter.model"; // and refactor desktop/browser vault filters @Directive() export class VaultFilterComponent implements OnInit { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() activeFilter: VaultFilter = new VaultFilter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() hideFolders = false; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() hideCollections = false; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() hideFavorites = false; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() hideTrash = false; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() hideOrganizations = false; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onFilterChange = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onAddFolder = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onEditFolder = new EventEmitter(); private activeUserId: UserId; diff --git a/libs/vault/src/cipher-form/components/additional-options/additional-options-section.component.spec.ts b/libs/vault/src/cipher-form/components/additional-options/additional-options-section.component.spec.ts index a9a327b90c0..6a574053367 100644 --- a/libs/vault/src/cipher-form/components/additional-options/additional-options-section.component.spec.ts +++ b/libs/vault/src/cipher-form/components/additional-options/additional-options-section.component.spec.ts @@ -13,11 +13,15 @@ import { CustomFieldsComponent } from "../custom-fields/custom-fields.component" import { AdditionalOptionsSectionComponent } from "./additional-options-section.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: "vault-custom-fields", template: "", }) class MockCustomFieldsComponent { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() disableSectionMargin: boolean; } diff --git a/libs/vault/src/cipher-form/components/additional-options/additional-options-section.component.ts b/libs/vault/src/cipher-form/components/additional-options/additional-options-section.component.ts index 3a7152bfe24..f37d4f71f63 100644 --- a/libs/vault/src/cipher-form/components/additional-options/additional-options-section.component.ts +++ b/libs/vault/src/cipher-form/components/additional-options/additional-options-section.component.ts @@ -21,6 +21,8 @@ import { PasswordRepromptService } from "../../../services/password-reprompt.ser import { CipherFormContainer } from "../../cipher-form-container"; import { CustomFieldsComponent } from "../custom-fields/custom-fields.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: "vault-additional-options-section", templateUrl: "./additional-options-section.component.html", @@ -39,6 +41,8 @@ import { CustomFieldsComponent } from "../custom-fields/custom-fields.component" ], }) export class AdditionalOptionsSectionComponent implements OnInit { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChild(CustomFieldsComponent) customFieldsComponent: CustomFieldsComponent; additionalOptionsForm = this.formBuilder.group({ @@ -56,6 +60,8 @@ export class AdditionalOptionsSectionComponent implements OnInit { /** True when the form is in `partial-edit` mode */ isPartialEdit = false; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() disableSectionMargin: boolean; /** True when the form allows new fields to be added */ diff --git a/libs/vault/src/cipher-form/components/attachments/cipher-attachments.component.spec.ts b/libs/vault/src/cipher-form/components/attachments/cipher-attachments.component.spec.ts index c88ce9f0301..06f62976548 100644 --- a/libs/vault/src/cipher-form/components/attachments/cipher-attachments.component.spec.ts +++ b/libs/vault/src/cipher-form/components/attachments/cipher-attachments.component.spec.ts @@ -26,13 +26,21 @@ import { FakeAccountService, mockAccountServiceWith } from "../../../../../commo import { CipherAttachmentsComponent } from "./cipher-attachments.component"; import { DeleteAttachmentComponent } from "./delete-attachment/delete-attachment.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-download-attachment", template: "", }) class MockDownloadAttachmentComponent { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() attachment: AttachmentView; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() cipher: CipherView; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() admin: boolean = false; } diff --git a/libs/vault/src/cipher-form/components/attachments/cipher-attachments.component.ts b/libs/vault/src/cipher-form/components/attachments/cipher-attachments.component.ts index 9ae1c62bd3e..56c3414a12e 100644 --- a/libs/vault/src/cipher-form/components/attachments/cipher-attachments.component.ts +++ b/libs/vault/src/cipher-form/components/attachments/cipher-attachments.component.ts @@ -56,6 +56,8 @@ type CipherAttachmentForm = FormGroup<{ file: FormControl; }>; +// 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-cipher-attachments", templateUrl: "./cipher-attachments.component.html", @@ -77,27 +79,43 @@ export class CipherAttachmentsComponent implements OnInit, AfterViewInit { static attachmentFormID = "attachmentForm"; /** Reference to the file HTMLInputElement */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChild("fileInput", { read: ElementRef }) private fileInput: ElementRef; /** Reference to the BitSubmitDirective */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChild(BitSubmitDirective) bitSubmit: BitSubmitDirective; /** The `id` of the cipher in context */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ required: true }) cipherId: CipherId; /** The organization ID if this cipher belongs to an organization */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() organizationId?: OrganizationId; /** Denotes if the action is occurring from within the admin console */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() admin: boolean = false; /** An optional submit button, whose loading/disabled state will be tied to the form state. */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() submitBtn?: ButtonComponent; /** Emits after a file has been successfully uploaded */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onUploadSuccess = new EventEmitter(); /** Emits after a file has been successfully removed */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onRemoveSuccess = new EventEmitter(); organization: Organization; diff --git a/libs/vault/src/cipher-form/components/attachments/delete-attachment/delete-attachment.component.ts b/libs/vault/src/cipher-form/components/attachments/delete-attachment/delete-attachment.component.ts index 60002ca5924..1bb3e071a0c 100644 --- a/libs/vault/src/cipher-form/components/attachments/delete-attachment/delete-attachment.component.ts +++ b/libs/vault/src/cipher-form/components/attachments/delete-attachment/delete-attachment.component.ts @@ -17,6 +17,8 @@ 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-delete-attachment", templateUrl: "./delete-attachment.component.html", @@ -24,15 +26,23 @@ import { }) export class DeleteAttachmentComponent { /** Id of the cipher associated with the attachment */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ required: true }) cipherId!: string; /** The attachment that is can be deleted */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ required: true }) attachment!: AttachmentView; /** Whether the attachment is being accessed from the admin console */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() admin: boolean = false; /** Emits when the attachment is successfully deleted */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onDeletionSuccess = new EventEmitter(); constructor( diff --git a/libs/vault/src/cipher-form/components/autofill-options/advanced-uri-option-dialog.component.ts b/libs/vault/src/cipher-form/components/autofill-options/advanced-uri-option-dialog.component.ts index e63aa224149..f78c2c170f8 100644 --- a/libs/vault/src/cipher-form/components/autofill-options/advanced-uri-option-dialog.component.ts +++ b/libs/vault/src/cipher-form/components/autofill-options/advanced-uri-option-dialog.component.ts @@ -17,6 +17,8 @@ export type AdvancedUriOptionDialogParams = { onContinue: () => void; }; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ templateUrl: "advanced-uri-option-dialog.component.html", imports: [ButtonLinkDirective, ButtonModule, DialogModule, JslibModule], diff --git a/libs/vault/src/cipher-form/components/autofill-options/autofill-options.component.ts b/libs/vault/src/cipher-form/components/autofill-options/autofill-options.component.ts index 6a2b3e431ca..e6b8b5c9aca 100644 --- a/libs/vault/src/cipher-form/components/autofill-options/autofill-options.component.ts +++ b/libs/vault/src/cipher-form/components/autofill-options/autofill-options.component.ts @@ -36,6 +36,8 @@ interface UriField { matchDetection: UriMatchStrategySetting; } +// 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: "vault-autofill-options", templateUrl: "./autofill-options.component.html", @@ -60,6 +62,8 @@ export class AutofillOptionsComponent implements OnInit { /** * List of rendered UriOptionComponents. Used for focusing newly added Uri inputs. */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChildren(UriOptionComponent) protected uriOptions: QueryList; diff --git a/libs/vault/src/cipher-form/components/autofill-options/uri-option.component.ts b/libs/vault/src/cipher-form/components/autofill-options/uri-option.component.ts index 8b6b6a6490b..b61109a45bb 100644 --- a/libs/vault/src/cipher-form/components/autofill-options/uri-option.component.ts +++ b/libs/vault/src/cipher-form/components/autofill-options/uri-option.component.ts @@ -36,6 +36,8 @@ import { import { AdvancedUriOptionDialogComponent } from "./advanced-uri-option-dialog.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: "vault-autofill-uri-option", templateUrl: "./uri-option.component.html", @@ -58,9 +60,13 @@ import { AdvancedUriOptionDialogComponent } from "./advanced-uri-option-dialog.c ], }) export class UriOptionComponent implements ControlValueAccessor { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChild("uriInput") private inputElement: ElementRef; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChild("matchDetectionSelect") private matchDetectionSelect: SelectComponent; @@ -92,18 +98,24 @@ export class UriOptionComponent implements ControlValueAccessor { /** * Whether the option can be reordered. If false, the reorder button will be hidden. */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ required: true }) canReorder: boolean; /** * Whether the URI can be removed from the form. If false, the remove button will be hidden. */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ required: true }) canRemove: boolean; /** * The user's current default match detection strategy. Will be displayed in () after "Default" */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ required: true }) set defaultMatchDetection(value: UriMatchStrategySetting) { // The default selection has a value of `null` avoid showing "Default (Default)" @@ -120,14 +132,20 @@ export class UriOptionComponent implements ControlValueAccessor { /** * The index of the URI in the form. Used to render the correct label. */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ required: true }) index: number; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onKeydown = new EventEmitter(); /** * Emits when the remove button is clicked and URI should be removed from the form. */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() remove = new EventEmitter(); diff --git a/libs/vault/src/cipher-form/components/card-details-section/card-details-section.component.ts b/libs/vault/src/cipher-form/components/card-details-section/card-details-section.component.ts index 7b8149b6d7b..5fa8d0af131 100644 --- a/libs/vault/src/cipher-form/components/card-details-section/card-details-section.component.ts +++ b/libs/vault/src/cipher-form/components/card-details-section/card-details-section.component.ts @@ -23,6 +23,8 @@ import { import { CipherFormContainer } from "../../cipher-form-container"; +// 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: "vault-card-details-section", templateUrl: "./card-details-section.component.html", @@ -40,9 +42,13 @@ import { CipherFormContainer } from "../../cipher-form-container"; }) export class CardDetailsSectionComponent implements OnInit { /** The original cipher */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() originalCipherView: CipherView; /** True when all fields should be disabled */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() disabled: boolean; /** diff --git a/libs/vault/src/cipher-form/components/cipher-form.component.ts b/libs/vault/src/cipher-form/components/cipher-form.component.ts index f7676818edf..5e75ea5bc24 100644 --- a/libs/vault/src/cipher-form/components/cipher-form.component.ts +++ b/libs/vault/src/cipher-form/components/cipher-form.component.ts @@ -49,6 +49,8 @@ import { LoginDetailsSectionComponent } from "./login-details-section/login-deta import { NewItemNudgeComponent } from "./new-item-nudge/new-item-nudge.component"; import { SshKeySectionComponent } from "./sshkey-section/sshkey-section.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: "vault-cipher-form", templateUrl: "./cipher-form.component.html", @@ -79,6 +81,8 @@ import { SshKeySectionComponent } from "./sshkey-section/sshkey-section.componen ], }) export class CipherFormComponent implements AfterViewInit, OnInit, OnChanges, CipherFormContainer { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChild(BitSubmitDirective) private bitSubmit: BitSubmitDirective; private destroyRef = inject(DestroyRef); @@ -87,38 +91,52 @@ export class CipherFormComponent implements AfterViewInit, OnInit, OnChanges, Ci /** * The form ID to use for the form. Used to connect it to a submit button. */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ required: true }) formId: string; /** * The configuration for the add/edit form. Used to determine which controls are shown and what values are available. */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ required: true }) config: CipherFormConfig; /** * Optional submit button that will be disabled or marked as loading when the form is submitting. */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() submitBtn?: ButtonComponent; /** * Optional function to call before submitting the form. If the function returns false, the form will not be submitted. */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() beforeSubmit: () => Promise; /** * Event emitted when the cipher is saved successfully. */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() cipherSaved = new EventEmitter(); private formReadySubject = new Subject(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() formReady = this.formReadySubject.asObservable(); /** * Emitted when the form is enabled */ private formStatusChangeSubject = new BehaviorSubject<"enabled" | "disabled" | null>(null); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() formStatusChange$ = this.formStatusChangeSubject.asObservable(); /** diff --git a/libs/vault/src/cipher-form/components/cipher-generator/cipher-form-generator.component.spec.ts b/libs/vault/src/cipher-form/components/cipher-generator/cipher-form-generator.component.spec.ts index e98e4805d19..bc2b86f01ff 100644 --- a/libs/vault/src/cipher-form/components/cipher-generator/cipher-form-generator.component.spec.ts +++ b/libs/vault/src/cipher-form/components/cipher-generator/cipher-form-generator.component.spec.ts @@ -6,19 +6,27 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { GeneratorModule } from "@bitwarden/generator-components"; import { CipherFormGeneratorComponent } 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: "tools-password-generator", template: ``, }) class MockPasswordGeneratorComponent { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onGenerated = new EventEmitter(); } +// 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: "tools-username-generator", template: ``, }) class MockUsernameGeneratorComponent { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onGenerated = new EventEmitter(); } diff --git a/libs/vault/src/cipher-form/components/cipher-generator/cipher-form-generator.component.ts b/libs/vault/src/cipher-form/components/cipher-generator/cipher-form-generator.component.ts index f1e4c5c177c..e053dd96973 100644 --- a/libs/vault/src/cipher-form/components/cipher-generator/cipher-form-generator.component.ts +++ b/libs/vault/src/cipher-form/components/cipher-generator/cipher-form-generator.component.ts @@ -9,30 +9,42 @@ import { AlgorithmInfo, GeneratedCredential } from "@bitwarden/generator-core"; * Renders a password or username generator UI and emits the most recently generated value. * Used by the cipher form to be shown in a dialog/modal when generating cipher passwords/usernames. */ +// 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: "vault-cipher-form-generator", templateUrl: "./cipher-form-generator.component.html", imports: [CommonModule, GeneratorModule], }) export class CipherFormGeneratorComponent { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() uri: string = ""; /** * The type of generator form to show. */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ required: true }) type: "password" | "username" = "password"; /** Removes bottom margin of internal sections */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ transform: coerceBooleanProperty }) disableMargin = false; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() algorithmSelected = new EventEmitter(); /** * Emits an event when a new value is generated. */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() valueGenerated = new EventEmitter(); diff --git a/libs/vault/src/cipher-form/components/custom-fields/add-edit-custom-field-dialog/add-edit-custom-field-dialog.component.ts b/libs/vault/src/cipher-form/components/custom-fields/add-edit-custom-field-dialog/add-edit-custom-field-dialog.component.ts index 7d56db4366b..81720f8e612 100644 --- a/libs/vault/src/cipher-form/components/custom-fields/add-edit-custom-field-dialog/add-edit-custom-field-dialog.component.ts +++ b/libs/vault/src/cipher-form/components/custom-fields/add-edit-custom-field-dialog/add-edit-custom-field-dialog.component.ts @@ -28,6 +28,8 @@ export type AddEditCustomFieldDialogData = { disallowHiddenField?: boolean; }; +// 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: "vault-add-edit-custom-field-dialog", templateUrl: "./add-edit-custom-field-dialog.component.html", diff --git a/libs/vault/src/cipher-form/components/custom-fields/custom-fields.component.ts b/libs/vault/src/cipher-form/components/custom-fields/custom-fields.component.ts index 013ccd6c87e..b07d17af7d0 100644 --- a/libs/vault/src/cipher-form/components/custom-fields/custom-fields.component.ts +++ b/libs/vault/src/cipher-form/components/custom-fields/custom-fields.component.ts @@ -68,6 +68,8 @@ export type CustomField = { newField: boolean; }; +// 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: "vault-custom-fields", templateUrl: "./custom-fields.component.html", @@ -88,10 +90,16 @@ export type CustomField = { ], }) export class CustomFieldsComponent implements OnInit, AfterViewInit { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() numberOfFieldsChange = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChildren("customFieldRow") customFieldRows: QueryList>; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() disableSectionMargin: boolean; customFieldsForm = this.formBuilder.group({ diff --git a/libs/vault/src/cipher-form/components/identity/identity.component.ts b/libs/vault/src/cipher-form/components/identity/identity.component.ts index 4c90024e05a..642a0cc4aff 100644 --- a/libs/vault/src/cipher-form/components/identity/identity.component.ts +++ b/libs/vault/src/cipher-form/components/identity/identity.component.ts @@ -21,6 +21,8 @@ import { import { CipherFormContainer } from "../../cipher-form-container"; +// 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: "vault-identity-section", templateUrl: "./identity.component.html", @@ -38,7 +40,11 @@ import { CipherFormContainer } from "../../cipher-form-container"; ], }) export class IdentitySectionComponent implements OnInit { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() originalCipherView: CipherView; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() disabled: boolean; identityTitleOptions = [ { name: "-- " + this.i18nService.t("select") + " --", value: null }, 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 892fc5804ec..6fd74d86525 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 @@ -37,6 +37,8 @@ import { } from "../../abstractions/cipher-form-config.service"; import { CipherFormContainer } from "../../cipher-form-container"; +// 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: "vault-item-details-section", templateUrl: "./item-details-section.component.html", @@ -84,9 +86,13 @@ export class ItemDetailsSectionComponent implements OnInit { protected favoriteButtonDisabled = false; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ required: true }) config: CipherFormConfig; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() originalCipherView: CipherView; diff --git a/libs/vault/src/cipher-form/components/login-details-section/login-details-section.component.spec.ts b/libs/vault/src/cipher-form/components/login-details-section/login-details-section.component.spec.ts index d6fe8a64921..8e60b9f32e0 100644 --- a/libs/vault/src/cipher-form/components/login-details-section/login-details-section.component.spec.ts +++ b/libs/vault/src/cipher-form/components/login-details-section/login-details-section.component.spec.ts @@ -23,6 +23,8 @@ import { AutofillOptionsComponent } from "../autofill-options/autofill-options.c import { LoginDetailsSectionComponent } from "./login-details-section.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: "vault-autofill-options", template: "", diff --git a/libs/vault/src/cipher-form/components/login-details-section/login-details-section.component.ts b/libs/vault/src/cipher-form/components/login-details-section/login-details-section.component.ts index 061a8c4abf4..8b9c4ddeea1 100644 --- a/libs/vault/src/cipher-form/components/login-details-section/login-details-section.component.ts +++ b/libs/vault/src/cipher-form/components/login-details-section/login-details-section.component.ts @@ -30,6 +30,8 @@ import { TotpCaptureService } from "../../abstractions/totp-capture.service"; import { CipherFormContainer } from "../../cipher-form-container"; import { AutofillOptionsComponent } from "../autofill-options/autofill-options.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: "vault-login-details-section", templateUrl: "./login-details-section.component.html", diff --git a/libs/vault/src/cipher-form/components/new-item-nudge/new-item-nudge.component.ts b/libs/vault/src/cipher-form/components/new-item-nudge/new-item-nudge.component.ts index 70b94505731..5f4a44e5ef5 100644 --- a/libs/vault/src/cipher-form/components/new-item-nudge/new-item-nudge.component.ts +++ b/libs/vault/src/cipher-form/components/new-item-nudge/new-item-nudge.component.ts @@ -11,13 +11,15 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { UserId } from "@bitwarden/common/types/guid"; import { CipherType } from "@bitwarden/sdk-internal"; +// 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: "vault-new-item-nudge", templateUrl: "./new-item-nudge.component.html", imports: [SpotlightComponent, AsyncPipe], }) export class NewItemNudgeComponent { - configType = input.required(); + readonly configType = input.required(); activeUserId$ = this.accountService.activeAccount$.pipe(getUserId); showNewItemSpotlight$ = combineLatest([ this.activeUserId$, diff --git a/libs/vault/src/cipher-form/components/sshkey-section/sshkey-section.component.ts b/libs/vault/src/cipher-form/components/sshkey-section/sshkey-section.component.ts index f92c4420d03..649dd807f29 100644 --- a/libs/vault/src/cipher-form/components/sshkey-section/sshkey-section.component.ts +++ b/libs/vault/src/cipher-form/components/sshkey-section/sshkey-section.component.ts @@ -25,6 +25,8 @@ import { generate_ssh_key } from "@bitwarden/sdk-internal"; import { SshImportPromptService } from "../../../services/ssh-import-prompt.service"; import { CipherFormContainer } from "../../cipher-form-container"; +// 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: "vault-sshkey-section", templateUrl: "./sshkey-section.component.html", @@ -42,9 +44,13 @@ import { CipherFormContainer } from "../../cipher-form-container"; }) export class SshKeySectionComponent implements OnInit { /** The original cipher */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() originalCipherView: CipherView; /** True when all fields should be disabled */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() disabled: boolean; /** diff --git a/libs/vault/src/cipher-view/additional-options/additional-options.component.ts b/libs/vault/src/cipher-view/additional-options/additional-options.component.ts index 3e632983d49..4933c137e51 100644 --- a/libs/vault/src/cipher-view/additional-options/additional-options.component.ts +++ b/libs/vault/src/cipher-view/additional-options/additional-options.component.ts @@ -11,6 +11,8 @@ import { FormFieldModule, } 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-additional-options", templateUrl: "additional-options.component.html", @@ -26,5 +28,7 @@ import { ], }) export class AdditionalOptionsComponent { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() notes: string = ""; } diff --git a/libs/vault/src/cipher-view/attachments/attachments-v2-view.component.ts b/libs/vault/src/cipher-view/attachments/attachments-v2-view.component.ts index 711c63878e3..4e324d8002e 100644 --- a/libs/vault/src/cipher-view/attachments/attachments-v2-view.component.ts +++ b/libs/vault/src/cipher-view/attachments/attachments-v2-view.component.ts @@ -22,6 +22,8 @@ import { KeyService } from "@bitwarden/key-management"; import { DownloadAttachmentComponent } from "../../components/download-attachment/download-attachment.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-attachments-v2-view", templateUrl: "attachments-v2-view.component.html", @@ -36,11 +38,17 @@ import { DownloadAttachmentComponent } from "../../components/download-attachmen ], }) export class AttachmentsV2ViewComponent { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() cipher: CipherView; // Required for fetching attachment data when viewed from cipher via emergency access + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() emergencyAccessId?: EmergencyAccessId; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() admin: boolean = false; canAccessPremium: boolean; diff --git a/libs/vault/src/cipher-view/attachments/attachments-v2.component.ts b/libs/vault/src/cipher-view/attachments/attachments-v2.component.ts index 11c15f63505..2796cae08d0 100644 --- a/libs/vault/src/cipher-view/attachments/attachments-v2.component.ts +++ b/libs/vault/src/cipher-view/attachments/attachments-v2.component.ts @@ -40,6 +40,8 @@ export interface AttachmentDialogCloseResult { /** * Component for the attachments dialog. */ +// 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-attachments-v2", templateUrl: "attachments-v2.component.html", diff --git a/libs/vault/src/cipher-view/autofill-options/autofill-options-view.component.ts b/libs/vault/src/cipher-view/autofill-options/autofill-options-view.component.ts index 0643737d846..8bc55fb3760 100644 --- a/libs/vault/src/cipher-view/autofill-options/autofill-options-view.component.ts +++ b/libs/vault/src/cipher-view/autofill-options/autofill-options-view.component.ts @@ -18,6 +18,8 @@ import { TypographyModule, } 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-autofill-options-view", templateUrl: "autofill-options-view.component.html", @@ -32,7 +34,11 @@ import { ], }) export class AutofillOptionsViewComponent { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() loginUris: LoginUriView[]; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() cipherId: string; constructor( diff --git a/libs/vault/src/cipher-view/card-details/card-details-view.component.ts b/libs/vault/src/cipher-view/card-details/card-details-view.component.ts index 502214848f3..d80aafde46b 100644 --- a/libs/vault/src/cipher-view/card-details/card-details-view.component.ts +++ b/libs/vault/src/cipher-view/card-details/card-details-view.component.ts @@ -17,6 +17,8 @@ import { import { ReadOnlyCipherCardComponent } from "../read-only-cipher-card/read-only-cipher-card.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-card-details-view", templateUrl: "card-details-view.component.html", @@ -31,6 +33,8 @@ import { ReadOnlyCipherCardComponent } from "../read-only-cipher-card/read-only- ], }) export class CardDetailsComponent implements OnChanges { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() cipher: CipherView; EventType = EventType; diff --git a/libs/vault/src/cipher-view/cipher-view.component.ts b/libs/vault/src/cipher-view/cipher-view.component.ts index 1a294be46aa..15cb7d4651f 100644 --- a/libs/vault/src/cipher-view/cipher-view.component.ts +++ b/libs/vault/src/cipher-view/cipher-view.component.ts @@ -39,6 +39,8 @@ import { LoginCredentialsViewComponent } from "./login-credentials/login-credent import { SshKeyViewComponent } from "./sshkey-sections/sshkey-view.component"; import { ViewIdentitySectionsComponent } from "./view-identity-sections/view-identity-sections.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-cipher-view", templateUrl: "cipher-view.component.html", @@ -61,9 +63,13 @@ import { ViewIdentitySectionsComponent } from "./view-identity-sections/view-ide ], }) export class CipherViewComponent implements OnChanges, OnDestroy { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ required: true }) cipher: CipherView | null = null; // Required for fetching attachment data when viewed from cipher via emergency access + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() emergencyAccessId?: EmergencyAccessId; activeUserId$ = getUserId(this.accountService.activeAccount$); @@ -72,9 +78,13 @@ export class CipherViewComponent implements OnChanges, OnDestroy { * Optional list of collections the cipher is assigned to. If none are provided, they will be fetched using the * `CipherService` and the `collectionIds` property of the cipher. */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() collections?: CollectionView[]; /** Should be set to true when the component is used within the Admin Console */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() isAdminConsole?: boolean = false; organization$: Observable | undefined; diff --git a/libs/vault/src/cipher-view/custom-fields/custom-fields-v2.component.ts b/libs/vault/src/cipher-view/custom-fields/custom-fields-v2.component.ts index 7c2afd5029f..8b1eaab74bb 100644 --- a/libs/vault/src/cipher-view/custom-fields/custom-fields-v2.component.ts +++ b/libs/vault/src/cipher-view/custom-fields/custom-fields-v2.component.ts @@ -24,6 +24,8 @@ import { import { VaultAutosizeReadOnlyTextArea } from "../../directives/readonly-textarea.directive"; +// 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-custom-fields-v2", templateUrl: "custom-fields-v2.component.html", @@ -42,6 +44,8 @@ import { VaultAutosizeReadOnlyTextArea } from "../../directives/readonly-textare ], }) export class CustomFieldV2Component implements OnInit, OnChanges { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ required: true }) cipher!: CipherView; fieldType = FieldType; fieldOptions: Map | undefined; 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 31ba5c82d9d..2c310daad76 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 @@ -22,6 +22,8 @@ import { import { OrgIconDirective } from "../../components/org-icon.directive"; +// 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-item-details-v2", templateUrl: "item-details-v2.component.html", diff --git a/libs/vault/src/cipher-view/item-history/item-history-v2.component.ts b/libs/vault/src/cipher-view/item-history/item-history-v2.component.ts index 2bbb6418934..1295836d3d9 100644 --- a/libs/vault/src/cipher-view/item-history/item-history-v2.component.ts +++ b/libs/vault/src/cipher-view/item-history/item-history-v2.component.ts @@ -16,6 +16,8 @@ import { TypographyModule, } 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-item-history-v2", templateUrl: "item-history-v2.component.html", @@ -31,6 +33,8 @@ import { ], }) export class ItemHistoryV2Component { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() cipher: CipherView; constructor(private viewPasswordHistoryService: ViewPasswordHistoryService) {} diff --git a/libs/vault/src/cipher-view/login-credentials/login-credentials-view.component.ts b/libs/vault/src/cipher-view/login-credentials/login-credentials-view.component.ts index 5987d055e6b..4dbbf979b15 100644 --- a/libs/vault/src/cipher-view/login-credentials/login-credentials-view.component.ts +++ b/libs/vault/src/cipher-view/login-credentials/login-credentials-view.component.ts @@ -42,6 +42,8 @@ type TotpCodeValues = { totpCodeFormatted?: string; }; +// 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-login-credentials-view", templateUrl: "login-credentials-view.component.html", @@ -61,10 +63,20 @@ type TotpCodeValues = { ], }) export class LoginCredentialsViewComponent implements OnChanges { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() cipher: CipherView; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() activeUserId: UserId; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() hadPendingChangePasswordTask: boolean; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() handleChangePassword = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChild("passwordInput") private passwordInput!: ElementRef; diff --git a/libs/vault/src/cipher-view/read-only-cipher-card/read-only-cipher-card.component.ts b/libs/vault/src/cipher-view/read-only-cipher-card/read-only-cipher-card.component.ts index 8f6b9954a9f..7a17376472d 100644 --- a/libs/vault/src/cipher-view/read-only-cipher-card/read-only-cipher-card.component.ts +++ b/libs/vault/src/cipher-view/read-only-cipher-card/read-only-cipher-card.component.ts @@ -2,6 +2,8 @@ import { AfterViewInit, Component, ContentChildren, QueryList } from "@angular/c import { CardComponent, BitFormFieldComponent } 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: "read-only-cipher-card", templateUrl: "./read-only-cipher-card.component.html", @@ -11,6 +13,8 @@ import { CardComponent, BitFormFieldComponent } from "@bitwarden/components"; * A thin wrapper around the `bit-card` component that disables the bottom border for the last form field. */ export class ReadOnlyCipherCardComponent implements AfterViewInit { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ContentChildren(BitFormFieldComponent) formFields?: QueryList; ngAfterViewInit(): void { diff --git a/libs/vault/src/cipher-view/sshkey-sections/sshkey-view.component.ts b/libs/vault/src/cipher-view/sshkey-sections/sshkey-view.component.ts index 535c41b9aea..5d076d81cc7 100644 --- a/libs/vault/src/cipher-view/sshkey-sections/sshkey-view.component.ts +++ b/libs/vault/src/cipher-view/sshkey-sections/sshkey-view.component.ts @@ -14,6 +14,8 @@ import { import { ReadOnlyCipherCardComponent } from "../read-only-cipher-card/read-only-cipher-card.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-sshkey-view", templateUrl: "sshkey-view.component.html", @@ -28,6 +30,8 @@ import { ReadOnlyCipherCardComponent } from "../read-only-cipher-card/read-only- ], }) export class SshKeyViewComponent implements OnChanges { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() sshKey: SshKeyView; revealSshKey = false; diff --git a/libs/vault/src/cipher-view/view-identity-sections/view-identity-sections.component.ts b/libs/vault/src/cipher-view/view-identity-sections/view-identity-sections.component.ts index f9cb9d2b549..14fb7e2925c 100644 --- a/libs/vault/src/cipher-view/view-identity-sections/view-identity-sections.component.ts +++ b/libs/vault/src/cipher-view/view-identity-sections/view-identity-sections.component.ts @@ -12,6 +12,8 @@ import { import { ReadOnlyCipherCardComponent } from "../read-only-cipher-card/read-only-cipher-card.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-view-identity-sections", templateUrl: "./view-identity-sections.component.html", @@ -26,6 +28,8 @@ import { ReadOnlyCipherCardComponent } from "../read-only-cipher-card/read-only- ], }) export class ViewIdentitySectionsComponent { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ required: true }) cipher: CipherView | null = null; /** Returns all populated address fields */ diff --git a/libs/vault/src/components/add-edit-folder-dialog/add-edit-folder-dialog.component.ts b/libs/vault/src/components/add-edit-folder-dialog/add-edit-folder-dialog.component.ts index 0442bcd1f76..adc4c67b2f4 100644 --- a/libs/vault/src/components/add-edit-folder-dialog/add-edit-folder-dialog.component.ts +++ b/libs/vault/src/components/add-edit-folder-dialog/add-edit-folder-dialog.component.ts @@ -47,6 +47,8 @@ export type AddEditFolderDialogData = { editFolderConfig?: { folder: FolderView }; }; +// 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: "vault-add-edit-folder-dialog", templateUrl: "./add-edit-folder-dialog.component.html", @@ -62,7 +64,11 @@ export type AddEditFolderDialogData = { ], }) export class AddEditFolderDialogComponent implements AfterViewInit, OnInit { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChild(BitSubmitDirective) private bitSubmit?: BitSubmitDirective; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChild("submitBtn") private submitBtn?: ButtonComponent; folder: FolderView = new FolderView(); diff --git a/libs/vault/src/components/assign-collections.component.ts b/libs/vault/src/components/assign-collections.component.ts index 9890074a8c9..f0ce59b0c3c 100644 --- a/libs/vault/src/components/assign-collections.component.ts +++ b/libs/vault/src/components/assign-collections.component.ts @@ -96,6 +96,8 @@ export type CollectionAssignmentResult = UnionOfValues(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onCollectionAssign = new EventEmitter(); formGroup = this.formBuilder.group({ diff --git a/libs/vault/src/components/can-delete-cipher.directive.ts b/libs/vault/src/components/can-delete-cipher.directive.ts index 7eadedc7ada..8ab59f9d647 100644 --- a/libs/vault/src/components/can-delete-cipher.directive.ts +++ b/libs/vault/src/components/can-delete-cipher.directive.ts @@ -13,6 +13,8 @@ import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cip export class CanDeleteCipherDirective implements OnDestroy { private destroy$ = new Subject(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input("appCanDeleteCipher") set cipher(cipher: CipherView) { this.viewContainer.clear(); diff --git a/libs/vault/src/components/carousel/carousel-button/carousel-button.component.ts b/libs/vault/src/components/carousel/carousel-button/carousel-button.component.ts index ae2ce12cba8..bef7f5b12d6 100644 --- a/libs/vault/src/components/carousel/carousel-button/carousel-button.component.ts +++ b/libs/vault/src/components/carousel/carousel-button/carousel-button.component.ts @@ -7,6 +7,8 @@ import { IconModule } from "@bitwarden/components"; import { VaultCarouselSlideComponent } from "../carousel-slide/carousel-slide.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: "vault-carousel-button", templateUrl: "carousel-button.component.html", @@ -14,15 +16,23 @@ import { VaultCarouselSlideComponent } from "../carousel-slide/carousel-slide.co }) export class VaultCarouselButtonComponent implements FocusableOption { /** Slide component that is associated with the individual button */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ required: true }) slide!: VaultCarouselSlideComponent; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChild("btn", { static: true }) button!: ElementRef; protected CarouselIcon = CarouselIcon; /** When set to true the button is shown in an active state. */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ required: true }) isActive!: boolean; /** Emits when the button is clicked. */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onClick = new EventEmitter(); /** Focuses the underlying button element. */ diff --git a/libs/vault/src/components/carousel/carousel-content/carousel-content.component.spec.ts b/libs/vault/src/components/carousel/carousel-content/carousel-content.component.spec.ts index bc1c9250c2c..5d396984f17 100644 --- a/libs/vault/src/components/carousel/carousel-content/carousel-content.component.spec.ts +++ b/libs/vault/src/components/carousel/carousel-content/carousel-content.component.spec.ts @@ -5,6 +5,8 @@ import { By } from "@angular/platform-browser"; import { VaultCarouselContentComponent } from "./carousel-content.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-test-template-ref", imports: [VaultCarouselContentComponent], @@ -17,6 +19,8 @@ import { VaultCarouselContentComponent } from "./carousel-content.component"; }) class TestTemplateRefComponent implements OnInit { // Test template content by creating a wrapping component and then pass a portal to the carousel content component. + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChild("template", { static: true }) template!: TemplateRef; portal!: TemplatePortal; diff --git a/libs/vault/src/components/carousel/carousel-content/carousel-content.component.ts b/libs/vault/src/components/carousel/carousel-content/carousel-content.component.ts index 47027a77ae9..a3c3a9f1caf 100644 --- a/libs/vault/src/components/carousel/carousel-content/carousel-content.component.ts +++ b/libs/vault/src/components/carousel/carousel-content/carousel-content.component.ts @@ -1,6 +1,8 @@ import { TemplatePortal, CdkPortalOutlet } from "@angular/cdk/portal"; import { Component, Input } from "@angular/core"; +// 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: "vault-carousel-content", templateUrl: "carousel-content.component.html", @@ -8,5 +10,7 @@ import { Component, Input } from "@angular/core"; }) export class VaultCarouselContentComponent { /** Content to be displayed for the carousel. */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ required: true }) content!: TemplatePortal; } diff --git a/libs/vault/src/components/carousel/carousel-slide/carousel-slide.component.spec.ts b/libs/vault/src/components/carousel/carousel-slide/carousel-slide.component.spec.ts index 46f06f6dcb4..116403362f4 100644 --- a/libs/vault/src/components/carousel/carousel-slide/carousel-slide.component.spec.ts +++ b/libs/vault/src/components/carousel/carousel-slide/carousel-slide.component.spec.ts @@ -5,6 +5,8 @@ import { By } from "@angular/platform-browser"; import { VaultCarouselSlideComponent } from "./carousel-slide.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-test-carousel-slide", imports: [VaultCarouselSlideComponent], diff --git a/libs/vault/src/components/carousel/carousel-slide/carousel-slide.component.ts b/libs/vault/src/components/carousel/carousel-slide/carousel-slide.component.ts index 811572881da..973a615d6f9 100644 --- a/libs/vault/src/components/carousel/carousel-slide/carousel-slide.component.ts +++ b/libs/vault/src/components/carousel/carousel-slide/carousel-slide.component.ts @@ -11,6 +11,8 @@ import { ViewContainerRef, } from "@angular/core"; +// 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: "vault-carousel-slide", templateUrl: "./carousel-slide.component.html", @@ -18,7 +20,11 @@ import { }) export class VaultCarouselSlideComponent implements OnInit { /** `aria-label` that is assigned to the carousel toggle. */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ required: true }) label!: string; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ transform: booleanAttribute }) disablePadding = false; /** @@ -29,8 +35,12 @@ export class VaultCarouselSlideComponent implements OnInit { * * @remarks See note 4 of https://www.w3.org/WAI/ARIA/apg/patterns/tabpanel/ */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ transform: coerceBooleanProperty }) noFocusableChildren?: true; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChild(TemplateRef, { static: true }) implicitContent!: TemplateRef; private _contentPortal: TemplatePortal | null = null; diff --git a/libs/vault/src/components/carousel/carousel.component.spec.ts b/libs/vault/src/components/carousel/carousel.component.spec.ts index ebb38576813..abbfe963ddf 100644 --- a/libs/vault/src/components/carousel/carousel.component.spec.ts +++ b/libs/vault/src/components/carousel/carousel.component.spec.ts @@ -7,6 +7,8 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { VaultCarouselSlideComponent } from "./carousel-slide/carousel-slide.component"; import { VaultCarouselComponent } from "./carousel.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-test-carousel-slide", imports: [VaultCarouselComponent, VaultCarouselSlideComponent], diff --git a/libs/vault/src/components/carousel/carousel.component.ts b/libs/vault/src/components/carousel/carousel.component.ts index fdebbebc33b..4e180f09f9b 100644 --- a/libs/vault/src/components/carousel/carousel.component.ts +++ b/libs/vault/src/components/carousel/carousel.component.ts @@ -28,6 +28,8 @@ import { VaultCarouselButtonComponent } from "./carousel-button/carousel-button. import { VaultCarouselContentComponent } from "./carousel-content/carousel-content.component"; import { VaultCarouselSlideComponent } from "./carousel-slide/carousel-slide.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: "vault-carousel", templateUrl: "./carousel.component.html", @@ -50,30 +52,46 @@ export class VaultCarouselComponent implements AfterViewInit { * @remarks * The label should not include the word "carousel", `aria-roledescription="carousel"` is already included. */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ required: true }) label = ""; /** * Emits the index of the newly selected slide. */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() slideChange = new EventEmitter(); /** All slides within the carousel. */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ContentChildren(VaultCarouselSlideComponent) slides!: QueryList; /** All buttons that control the carousel */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChildren(VaultCarouselButtonComponent) carouselButtons!: QueryList; /** Wrapping container for the carousel content and buttons */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChild("container") carouselContainer!: ElementRef; /** Container for the carousel buttons */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChild("carouselButtonWrapper") carouselButtonWrapper!: ElementRef; /** Temporary container containing `tempSlideOutlet` */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChild("tempSlideContainer") tempSlideContainer!: ElementRef; /** Outlet to temporary render each slide within */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChild(CdkPortalOutlet) tempSlideOutlet!: CdkPortalOutlet; /** The currently selected index of the carousel. */ diff --git a/libs/vault/src/components/copy-cipher-field.directive.ts b/libs/vault/src/components/copy-cipher-field.directive.ts index 9725adae5e2..7e8ca334f9e 100644 --- a/libs/vault/src/components/copy-cipher-field.directive.ts +++ b/libs/vault/src/components/copy-cipher-field.directive.ts @@ -30,13 +30,18 @@ import { CopyAction, CopyCipherFieldService } from "@bitwarden/vault"; selector: "[appCopyField]", }) export class CopyCipherFieldDirective implements OnChanges { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ alias: "appCopyField", required: true, }) action!: Exclude; - @Input({ required: true }) cipher!: CipherViewLike; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals + @Input({ required: true }) + cipher!: CipherViewLike; constructor( private copyCipherFieldService: CopyCipherFieldService, diff --git a/libs/vault/src/components/dark-image-source.directive.ts b/libs/vault/src/components/dark-image-source.directive.ts index ee54f61209a..b899ad472d4 100644 --- a/libs/vault/src/components/dark-image-source.directive.ts +++ b/libs/vault/src/components/dark-image-source.directive.ts @@ -40,7 +40,7 @@ export class DarkImageSourceDirective implements OnInit { /** * The image source to use when the dark theme is applied. */ - darkImgSrc = input.required({ alias: "appDarkImgSrc" }); + readonly darkImgSrc = input.required({ alias: "appDarkImgSrc" }); @HostBinding("attr.src") src: string | undefined; diff --git a/libs/vault/src/components/decryption-failure-dialog/decryption-failure-dialog.component.ts b/libs/vault/src/components/decryption-failure-dialog/decryption-failure-dialog.component.ts index 91b1cef364c..628de79b3da 100644 --- a/libs/vault/src/components/decryption-failure-dialog/decryption-failure-dialog.component.ts +++ b/libs/vault/src/components/decryption-failure-dialog/decryption-failure-dialog.component.ts @@ -19,6 +19,8 @@ export type DecryptionFailureDialogParams = { cipherIds: CipherId[]; }; +// 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: "vault-decryption-failure-dialog", templateUrl: "./decryption-failure-dialog.component.html", diff --git a/libs/vault/src/components/download-attachment/download-attachment.component.ts b/libs/vault/src/components/download-attachment/download-attachment.component.ts index 8208887b888..2f9cd528990 100644 --- a/libs/vault/src/components/download-attachment/download-attachment.component.ts +++ b/libs/vault/src/components/download-attachment/download-attachment.component.ts @@ -17,6 +17,8 @@ import { AttachmentView } from "@bitwarden/common/vault/models/view/attachment.v import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { AsyncActionsModule, IconButtonModule, 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-download-attachment", templateUrl: "./download-attachment.component.html", @@ -24,18 +26,28 @@ import { AsyncActionsModule, IconButtonModule, ToastService } from "@bitwarden/c }) export class DownloadAttachmentComponent { /** Attachment to download */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ required: true }) attachment: AttachmentView; /** The cipher associated with the attachment */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ required: true }) cipher: CipherView; // When in view mode, we will want to check for the master password reprompt + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() checkPwReprompt?: boolean = false; // Required for fetching attachment data when viewed from cipher via emergency access + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() emergencyAccessId?: EmergencyAccessId; /** When owners/admins can mange all items and when accessing from the admin console, use the admin endpoint */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() admin?: boolean = false; constructor( diff --git a/libs/vault/src/components/new-cipher-menu/new-cipher-menu.component.ts b/libs/vault/src/components/new-cipher-menu/new-cipher-menu.component.ts index 82bbc9a0749..0a755a9cdb4 100644 --- a/libs/vault/src/components/new-cipher-menu/new-cipher-menu.component.ts +++ b/libs/vault/src/components/new-cipher-menu/new-cipher-menu.component.ts @@ -9,15 +9,25 @@ import { CIPHER_MENU_ITEMS } from "@bitwarden/common/vault/types/cipher-menu-ite import { ButtonModule, MenuModule } from "@bitwarden/components"; import { I18nPipe } from "@bitwarden/ui-common"; +// 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: "vault-new-cipher-menu", templateUrl: "new-cipher-menu.component.html", imports: [ButtonModule, CommonModule, MenuModule, I18nPipe, JslibModule], }) export class NewCipherMenuComponent { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals canCreateCipher = input(false); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals canCreateFolder = input(false); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals canCreateCollection = input(false); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals canCreateSshKey = input(false); folderAdded = output(); collectionAdded = output(); diff --git a/libs/vault/src/components/org-icon.directive.ts b/libs/vault/src/components/org-icon.directive.ts index d9c8f240474..e9f28cb246a 100644 --- a/libs/vault/src/components/org-icon.directive.ts +++ b/libs/vault/src/components/org-icon.directive.ts @@ -8,7 +8,11 @@ export type OrgIconSize = "default" | "small" | "large"; selector: "[appOrgIcon]", }) export class OrgIconDirective { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ required: true }) tierType!: ProductTierType; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() size?: OrgIconSize = "default"; constructor( diff --git a/libs/vault/src/components/password-history-view/password-history-view.component.ts b/libs/vault/src/components/password-history-view/password-history-view.component.ts index 427644f3e77..e7d64cfdfdc 100644 --- a/libs/vault/src/components/password-history-view/password-history-view.component.ts +++ b/libs/vault/src/components/password-history-view/password-history-view.component.ts @@ -8,6 +8,8 @@ import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { PasswordHistoryView } from "@bitwarden/common/vault/models/view/password-history.view"; import { ItemModule, ColorPasswordModule, IconButtonModule } 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: "vault-password-history-view", templateUrl: "./password-history-view.component.html", @@ -17,6 +19,8 @@ export class PasswordHistoryViewComponent implements OnInit { /** * Optional cipher view. When included `cipherId` is ignored. */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ required: true }) cipher: CipherView; /** The password history for the cipher. */ diff --git a/libs/vault/src/components/password-history/password-history.component.ts b/libs/vault/src/components/password-history/password-history.component.ts index 7845edb2369..dd2865fa2ce 100644 --- a/libs/vault/src/components/password-history/password-history.component.ts +++ b/libs/vault/src/components/password-history/password-history.component.ts @@ -26,6 +26,8 @@ export interface ViewPasswordHistoryDialogParams { /** * A dialog component that displays the password history for a cipher. */ +// 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-password-history", templateUrl: "password-history.component.html", diff --git a/libs/vault/src/components/password-reprompt.component.ts b/libs/vault/src/components/password-reprompt.component.ts index 7665b22be49..f5245f5cad6 100644 --- a/libs/vault/src/components/password-reprompt.component.ts +++ b/libs/vault/src/components/password-reprompt.component.ts @@ -22,6 +22,8 @@ import { KeyService } from "@bitwarden/key-management"; * Used to verify the user's Master Password for the "Master Password Re-prompt" feature only. * See UserVerificationComponent for any other situation where you need to verify the user's identity. */ +// 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: "vault-password-reprompt", imports: [ diff --git a/libs/vault/src/components/permit-cipher-details-popover/permit-cipher-details-popover.component.ts b/libs/vault/src/components/permit-cipher-details-popover/permit-cipher-details-popover.component.ts index 8e80ddf7810..3649c8a21e1 100644 --- a/libs/vault/src/components/permit-cipher-details-popover/permit-cipher-details-popover.component.ts +++ b/libs/vault/src/components/permit-cipher-details-popover/permit-cipher-details-popover.component.ts @@ -4,6 +4,8 @@ import { JslibModule } from "@bitwarden/angular/jslib.module"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { LinkModule, PopoverModule } 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: "vault-permit-cipher-details-popover", templateUrl: "./permit-cipher-details-popover.component.html", diff --git a/libs/vault/src/components/totp-countdown/totp-countdown.component.ts b/libs/vault/src/components/totp-countdown/totp-countdown.component.ts index 5274ce621f0..32f9a64bb87 100644 --- a/libs/vault/src/components/totp-countdown/totp-countdown.component.ts +++ b/libs/vault/src/components/totp-countdown/totp-countdown.component.ts @@ -15,13 +15,19 @@ import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { TotpInfo } from "@bitwarden/common/vault/services/totp.service"; import { TypographyModule } 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: "[bitTotpCountdown]", templateUrl: "totp-countdown.component.html", imports: [CommonModule, TypographyModule], }) export class BitTotpCountdownComponent implements OnInit, OnChanges { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ required: true }) cipher!: CipherView; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() sendCopyCode = new EventEmitter(); /** From f452f39f3c1b6204876602e4212788241cea0c3d Mon Sep 17 00:00:00 2001 From: Bryan Cunningham Date: Mon, 27 Oct 2025 11:14:42 -0400 Subject: [PATCH 12/18] [CL-847] Card consolidation (#16952) * created shared card directive * WIP * use base card in anon layout * use bit-card for pricing card component * add base card to integration cards * add base card to reports cards * add base card to integration card * use card content on report card * use base card directive on base component * update dirt card to use bit-card * run prettier. fix whitespace * add missing imports to report list stories * add base card story and docs --- .../report-card/report-card.component.html | 10 ++--- .../shared/report-card/report-card.stories.ts | 17 +++++++- .../shared/report-list/report-list.stories.ts | 17 +++++++- .../reports/shared/reports-shared.module.ts | 4 +- .../integration-card.component.html | 17 ++++---- .../integration-card.component.ts | 10 ++++- .../anon-layout/anon-layout.component.html | 6 +-- .../src/anon-layout/anon-layout.component.ts | 10 ++++- .../src/card/base-card/base-card.component.ts | 14 +++++++ .../src/card/base-card/base-card.directive.ts | 9 ++++ .../src/card/base-card/base-card.mdx | 23 +++++++++++ .../src/card/base-card/base-card.stories.ts | 41 +++++++++++++++++++ libs/components/src/card/base-card/index.ts | 2 + .../src/card/card-content.component.ts | 7 ++++ libs/components/src/card/card.component.ts | 6 ++- libs/components/src/card/card.stories.ts | 15 +------ libs/components/src/card/index.ts | 2 + libs/dirt/card/src/card.component.html | 14 ++++--- libs/dirt/card/src/card.component.ts | 8 +--- .../pricing-card/pricing-card.component.html | 6 +-- .../pricing-card/pricing-card.component.ts | 3 +- 21 files changed, 184 insertions(+), 57 deletions(-) create mode 100644 libs/components/src/card/base-card/base-card.component.ts create mode 100644 libs/components/src/card/base-card/base-card.directive.ts create mode 100644 libs/components/src/card/base-card/base-card.mdx create mode 100644 libs/components/src/card/base-card/base-card.stories.ts create mode 100644 libs/components/src/card/base-card/index.ts create mode 100644 libs/components/src/card/card-content.component.ts diff --git a/apps/web/src/app/dirt/reports/shared/report-card/report-card.component.html b/apps/web/src/app/dirt/reports/shared/report-card/report-card.component.html index 8db0db3b5e6..dab928e6ec3 100644 --- a/apps/web/src/app/dirt/reports/shared/report-card/report-card.component.html +++ b/apps/web/src/app/dirt/reports/shared/report-card/report-card.component.html @@ -1,8 +1,8 @@ -
+
-
+

{{ title }}

{{ description }}

-
+ {{ "premium" | i18n }} {{ "upgrade" | i18n }} - +
diff --git a/apps/web/src/app/dirt/reports/shared/report-card/report-card.stories.ts b/apps/web/src/app/dirt/reports/shared/report-card/report-card.stories.ts index 76951bf9451..50798fea6e1 100644 --- a/apps/web/src/app/dirt/reports/shared/report-card/report-card.stories.ts +++ b/apps/web/src/app/dirt/reports/shared/report-card/report-card.stories.ts @@ -4,7 +4,12 @@ import { Meta, StoryObj, applicationConfig, moduleMetadata } from "@storybook/an import { PremiumBadgeComponent } from "@bitwarden/angular/billing/components/premium-badge"; import { JslibModule } from "@bitwarden/angular/jslib.module"; -import { BadgeModule, IconModule } from "@bitwarden/components"; +import { + BadgeModule, + BaseCardComponent, + IconModule, + CardContentComponent, +} from "@bitwarden/components"; import { PreloadedEnglishI18nModule } from "../../../../core/tests"; import { ReportVariant } from "../models/report-variant"; @@ -16,7 +21,15 @@ export default { component: ReportCardComponent, decorators: [ moduleMetadata({ - imports: [JslibModule, BadgeModule, IconModule, RouterTestingModule, PremiumBadgeComponent], + imports: [ + JslibModule, + BadgeModule, + CardContentComponent, + IconModule, + RouterTestingModule, + PremiumBadgeComponent, + BaseCardComponent, + ], }), applicationConfig({ providers: [importProvidersFrom(PreloadedEnglishI18nModule)], diff --git a/apps/web/src/app/dirt/reports/shared/report-list/report-list.stories.ts b/apps/web/src/app/dirt/reports/shared/report-list/report-list.stories.ts index 22c7e851bed..5a89eeff803 100644 --- a/apps/web/src/app/dirt/reports/shared/report-list/report-list.stories.ts +++ b/apps/web/src/app/dirt/reports/shared/report-list/report-list.stories.ts @@ -4,7 +4,12 @@ import { Meta, StoryObj, applicationConfig, moduleMetadata } from "@storybook/an import { PremiumBadgeComponent } from "@bitwarden/angular/billing/components/premium-badge"; import { JslibModule } from "@bitwarden/angular/jslib.module"; -import { BadgeModule, IconModule } from "@bitwarden/components"; +import { + BadgeModule, + BaseCardComponent, + CardContentComponent, + IconModule, +} from "@bitwarden/components"; import { PreloadedEnglishI18nModule } from "../../../../core/tests"; import { reports } from "../../reports"; @@ -18,7 +23,15 @@ export default { component: ReportListComponent, decorators: [ moduleMetadata({ - imports: [JslibModule, BadgeModule, RouterTestingModule, IconModule, PremiumBadgeComponent], + imports: [ + JslibModule, + BadgeModule, + RouterTestingModule, + IconModule, + PremiumBadgeComponent, + CardContentComponent, + BaseCardComponent, + ], declarations: [ReportCardComponent], }), applicationConfig({ diff --git a/apps/web/src/app/dirt/reports/shared/reports-shared.module.ts b/apps/web/src/app/dirt/reports/shared/reports-shared.module.ts index cad5d06d798..59e59a6a500 100644 --- a/apps/web/src/app/dirt/reports/shared/reports-shared.module.ts +++ b/apps/web/src/app/dirt/reports/shared/reports-shared.module.ts @@ -1,13 +1,15 @@ import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; +import { BaseCardComponent, CardContentComponent } from "@bitwarden/components"; + import { SharedModule } from "../../../shared/shared.module"; import { ReportCardComponent } from "./report-card/report-card.component"; import { ReportListComponent } from "./report-list/report-list.component"; @NgModule({ - imports: [CommonModule, SharedModule], + imports: [CommonModule, SharedModule, BaseCardComponent, CardContentComponent], declarations: [ReportCardComponent, ReportListComponent], exports: [ReportCardComponent, ReportListComponent], }) diff --git a/bitwarden_license/bit-web/src/app/dirt/organization-integrations/integration-card/integration-card.component.html b/bitwarden_license/bit-web/src/app/dirt/organization-integrations/integration-card/integration-card.component.html index 423b0130385..19a12755ca0 100644 --- a/bitwarden_license/bit-web/src/app/dirt/organization-integrations/integration-card/integration-card.component.html +++ b/bitwarden_license/bit-web/src/app/dirt/organization-integrations/integration-card/integration-card.component.html @@ -1,5 +1,5 @@ -
@@ -27,8 +27,8 @@ }
-
-

+ +

{{ name }} @if (showConnectedBadge()) { @@ -41,8 +41,9 @@ }

-

{{ description }}

- + @if (description) { +

{{ description }}

+ } @if (canSetupConnection) {

-
+ + diff --git a/bitwarden_license/bit-web/src/app/dirt/organization-integrations/integration-card/integration-card.component.ts b/bitwarden_license/bit-web/src/app/dirt/organization-integrations/integration-card/integration-card.component.ts index f1b0f982d57..e6d4aff05fb 100644 --- a/bitwarden_license/bit-web/src/app/dirt/organization-integrations/integration-card/integration-card.component.ts +++ b/bitwarden_license/bit-web/src/app/dirt/organization-integrations/integration-card/integration-card.component.ts @@ -20,7 +20,13 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { ThemeType } from "@bitwarden/common/platform/enums"; import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service"; import { OrganizationId } from "@bitwarden/common/types/guid"; -import { DialogRef, DialogService, ToastService } from "@bitwarden/components"; +import { + BaseCardComponent, + CardContentComponent, + DialogRef, + DialogService, + ToastService, +} from "@bitwarden/components"; import { SharedModule } from "@bitwarden/web-vault/app/shared"; import { @@ -37,7 +43,7 @@ import { @Component({ selector: "app-integration-card", templateUrl: "./integration-card.component.html", - imports: [SharedModule], + imports: [SharedModule, BaseCardComponent, CardContentComponent], }) export class IntegrationCardComponent implements AfterViewInit, OnDestroy { private destroyed$: Subject = new Subject(); diff --git a/libs/components/src/anon-layout/anon-layout.component.html b/libs/components/src/anon-layout/anon-layout.component.html index f88bdd3f920..15f7d107542 100644 --- a/libs/components/src/anon-layout/anon-layout.component.html +++ b/libs/components/src/anon-layout/anon-layout.component.html @@ -48,11 +48,11 @@ } @else { -
-
+ } diff --git a/libs/components/src/anon-layout/anon-layout.component.ts b/libs/components/src/anon-layout/anon-layout.component.ts index 596a54f8825..e6572a0c3c1 100644 --- a/libs/components/src/anon-layout/anon-layout.component.ts +++ b/libs/components/src/anon-layout/anon-layout.component.ts @@ -21,6 +21,7 @@ import { ClientType } from "@bitwarden/common/enums"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { BaseCardComponent } from "../card"; import { IconModule } from "../icon"; import { SharedModule } from "../shared"; import { TypographyModule } from "../typography"; @@ -32,7 +33,14 @@ export type AnonLayoutMaxWidth = "md" | "lg" | "xl" | "2xl" | "3xl" | "4xl"; @Component({ selector: "auth-anon-layout", templateUrl: "./anon-layout.component.html", - imports: [IconModule, CommonModule, TypographyModule, SharedModule, RouterModule], + imports: [ + IconModule, + CommonModule, + TypographyModule, + SharedModule, + RouterModule, + BaseCardComponent, + ], }) export class AnonLayoutComponent implements OnInit, OnChanges { @HostBinding("class") diff --git a/libs/components/src/card/base-card/base-card.component.ts b/libs/components/src/card/base-card/base-card.component.ts new file mode 100644 index 00000000000..44f82a32c47 --- /dev/null +++ b/libs/components/src/card/base-card/base-card.component.ts @@ -0,0 +1,14 @@ +import { Component } from "@angular/core"; + +import { BaseCardDirective } from "./base-card.directive"; + +/** + * The base card component is a container that applies our standard card border and box-shadow. + * In most cases using our `` component should suffice. + */ +@Component({ + selector: "bit-base-card", + template: ``, + hostDirectives: [BaseCardDirective], +}) +export class BaseCardComponent {} diff --git a/libs/components/src/card/base-card/base-card.directive.ts b/libs/components/src/card/base-card/base-card.directive.ts new file mode 100644 index 00000000000..7c6ec2b3b2f --- /dev/null +++ b/libs/components/src/card/base-card/base-card.directive.ts @@ -0,0 +1,9 @@ +import { Directive } from "@angular/core"; + +@Directive({ + host: { + class: + "tw-box-border tw-block tw-bg-background tw-text-main tw-border tw-border-solid tw-border-secondary-100 tw-shadow tw-rounded-xl", + }, +}) +export class BaseCardDirective {} diff --git a/libs/components/src/card/base-card/base-card.mdx b/libs/components/src/card/base-card/base-card.mdx new file mode 100644 index 00000000000..df326462906 --- /dev/null +++ b/libs/components/src/card/base-card/base-card.mdx @@ -0,0 +1,23 @@ +import { Meta, Primary, Controls, Canvas, Title, Description } from "@storybook/addon-docs"; + +import * as stories from "./base-card.stories"; + + + +```ts +import { BaseCardComponent } from "@bitwarden/components"; +``` + + +<Description /> + +<Canvas of={stories.Default} /> + +## BaseCardDirective + +There is also a `BaseCardDirective` available for use as a hostDirective if need be. But, most +likely using `<bit-base-card>` in your template will do. + +```ts +import { BaseCardDirective } from "@bitwarden/components"; +``` diff --git a/libs/components/src/card/base-card/base-card.stories.ts b/libs/components/src/card/base-card/base-card.stories.ts new file mode 100644 index 00000000000..bae07dd1468 --- /dev/null +++ b/libs/components/src/card/base-card/base-card.stories.ts @@ -0,0 +1,41 @@ +import { Meta, StoryObj, moduleMetadata } from "@storybook/angular"; + +import { AnchorLinkDirective } from "../../link"; +import { TypographyModule } from "../../typography"; + +import { BaseCardComponent } from "./base-card.component"; + +export default { + title: "Component Library/Cards/BaseCard", + component: BaseCardComponent, + decorators: [ + moduleMetadata({ + imports: [AnchorLinkDirective, TypographyModule], + }), + ], + parameters: { + design: { + type: "figma", + url: "https://www.figma.com/design/Zt3YSeb6E6lebAffrNLa0h/Tailwind-Component-Library?node-id=16329-28355&t=b5tDKylm5sWm2yKo-4", + }, + }, +} as Meta; + +type Story = StoryObj<BaseCardComponent>; + +/** Cards are presentational containers. */ +export const Default: Story = { + render: (args) => ({ + props: args, + template: /*html*/ ` + <bit-base-card> + <p bitTypography="body1" class="!tw-mb-0"> + The <code><bit-base-card></code> component is a container that applies our standard border and box-shadow. In most cases, <code><bit-card></code> should be used for consistency + </p> + <p bitTypography="body1" class="!tw-mb-0"> + <code><bit-base-card></code> is used in the <a bitLink href="/?path=/story/web-reports-card--enabled">ReportCardComponent</a> and <strong>IntegrationsCardComponent</strong> since they have custom padding requirements + </p> + </bit-base-card> + `, + }), +}; diff --git a/libs/components/src/card/base-card/index.ts b/libs/components/src/card/base-card/index.ts new file mode 100644 index 00000000000..186f2e68f24 --- /dev/null +++ b/libs/components/src/card/base-card/index.ts @@ -0,0 +1,2 @@ +export * from "./base-card.component"; +export * from "./base-card.directive"; diff --git a/libs/components/src/card/card-content.component.ts b/libs/components/src/card/card-content.component.ts new file mode 100644 index 00000000000..60be20e78f0 --- /dev/null +++ b/libs/components/src/card/card-content.component.ts @@ -0,0 +1,7 @@ +import { Component } from "@angular/core"; + +@Component({ + selector: "bit-card-content", + template: `<div class="tw-p-4 [@media(min-width:650px)]:tw-p-6"><ng-content></ng-content></div>`, +}) +export class CardContentComponent {} diff --git a/libs/components/src/card/card.component.ts b/libs/components/src/card/card.component.ts index d7e36d1ea9e..9cca973f003 100644 --- a/libs/components/src/card/card.component.ts +++ b/libs/components/src/card/card.component.ts @@ -1,12 +1,14 @@ import { ChangeDetectionStrategy, Component } from "@angular/core"; +import { BaseCardDirective } from "./base-card/base-card.directive"; + @Component({ selector: "bit-card", template: `<ng-content></ng-content>`, changeDetection: ChangeDetectionStrategy.OnPush, host: { - class: - "tw-box-border tw-block tw-bg-background tw-text-main tw-border-solid tw-border-b tw-border-0 tw-border-b-secondary-300 [&:not(bit-layout_*)]:tw-rounded-lg [&:not(bit-layout_*)]:tw-border-b-shadow tw-py-4 bit-compact:tw-py-3 tw-px-3 bit-compact:tw-px-2", + class: "tw-p-4 [@media(min-width:650px)]:tw-p-6", }, + hostDirectives: [BaseCardDirective], }) export class CardComponent {} diff --git a/libs/components/src/card/card.stories.ts b/libs/components/src/card/card.stories.ts index 411cc8e83cc..77faceb8eb7 100644 --- a/libs/components/src/card/card.stories.ts +++ b/libs/components/src/card/card.stories.ts @@ -11,7 +11,7 @@ import { I18nMockService } from "../utils/i18n-mock.service"; import { CardComponent } from "./card.component"; export default { - title: "Component Library/Card", + title: "Component Library/Cards/Card", component: CardComponent, decorators: [ moduleMetadata({ @@ -84,16 +84,3 @@ export const WithinSections: Story = { `, }), }; - -export const WithoutBorderRadius: Story = { - render: (args) => ({ - props: args, - template: /*html*/ ` - <bit-layout> - <bit-card> - <p bitTypography="body1" class="!tw-mb-0">Cards used in <code class="tw-text-danger-700">bit-layout</code> will not have a border radius</p> - </bit-card> - </bit-layout> - `, - }), -}; diff --git a/libs/components/src/card/index.ts b/libs/components/src/card/index.ts index 8151bac4c8b..1027f9b1fe2 100644 --- a/libs/components/src/card/index.ts +++ b/libs/components/src/card/index.ts @@ -1 +1,3 @@ +export * from "./base-card"; export * from "./card.component"; +export * from "./card-content.component"; diff --git a/libs/dirt/card/src/card.component.html b/libs/dirt/card/src/card.component.html index 3fd9372087c..8688cd8fd2c 100644 --- a/libs/dirt/card/src/card.component.html +++ b/libs/dirt/card/src/card.component.html @@ -1,7 +1,9 @@ -<div class="tw-flex-col"> - <span bitTypography="body2" class="tw-flex tw-text-muted">{{ title }}</span> - <div class="tw-flex tw-items-baseline tw-gap-2"> - <span bitTypography="h1">{{ value }}</span> - <span bitTypography="body2">{{ "cardMetrics" | i18n: maxValue }}</span> +<bit-card> + <div class="tw-flex tw-flex-col tw-gap-1.5"> + <span bitTypography="body2" class="tw-flex tw-text-muted">{{ title }}</span> + <div class="tw-flex tw-items-baseline tw-gap-2"> + <span bitTypography="h1" class="!tw-mb-0">{{ value }}</span> + <span bitTypography="body2">{{ "cardMetrics" | i18n: maxValue }}</span> + </div> </div> -</div> +</bit-card> diff --git a/libs/dirt/card/src/card.component.ts b/libs/dirt/card/src/card.component.ts index b9f2e7aa72e..089115fc2bf 100644 --- a/libs/dirt/card/src/card.component.ts +++ b/libs/dirt/card/src/card.component.ts @@ -4,18 +4,14 @@ import { CommonModule } from "@angular/common"; import { Component, Input } from "@angular/core"; import { JslibModule } from "@bitwarden/angular/jslib.module"; -import { TypographyModule } from "@bitwarden/components"; +import { TypographyModule, CardComponent as BitCardComponent } 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: "dirt-card", templateUrl: "./card.component.html", - imports: [CommonModule, TypographyModule, JslibModule], - host: { - class: - "tw-box-border tw-bg-background tw-block tw-text-main tw-border-solid tw-border tw-border-secondary-300 tw-border [&:not(bit-layout_*)]:tw-rounded-lg tw-rounded-lg tw-p-6", - }, + imports: [CommonModule, TypographyModule, JslibModule, BitCardComponent], }) export class CardComponent { /** diff --git a/libs/pricing/src/components/pricing-card/pricing-card.component.html b/libs/pricing/src/components/pricing-card/pricing-card.component.html index 8eae7088ac9..bc0ca68c5c3 100644 --- a/libs/pricing/src/components/pricing-card/pricing-card.component.html +++ b/libs/pricing/src/components/pricing-card/pricing-card.component.html @@ -1,6 +1,4 @@ -<div - class="tw-box-border tw-bg-background tw-text-main tw-border tw-border-secondary-100 tw-rounded-3xl tw-p-8 tw-shadow-sm tw-size-full tw-flex tw-flex-col" -> +<bit-card class="tw-size-full tw-flex tw-flex-col"> <!-- Title Section with Active Badge --> <div class="tw-flex tw-items-center tw-justify-between tw-mb-2"> <ng-content select="[slot=title]"></ng-content> @@ -82,4 +80,4 @@ } } </div> -</div> +</bit-card> diff --git a/libs/pricing/src/components/pricing-card/pricing-card.component.ts b/libs/pricing/src/components/pricing-card/pricing-card.component.ts index a8fed031adf..f268c654331 100644 --- a/libs/pricing/src/components/pricing-card/pricing-card.component.ts +++ b/libs/pricing/src/components/pricing-card/pricing-card.component.ts @@ -6,6 +6,7 @@ import { BadgeVariant, ButtonModule, ButtonType, + CardComponent, IconModule, TypographyModule, } from "@bitwarden/components"; @@ -20,7 +21,7 @@ import { @Component({ selector: "billing-pricing-card", templateUrl: "./pricing-card.component.html", - imports: [BadgeModule, ButtonModule, IconModule, TypographyModule, CurrencyPipe], + imports: [BadgeModule, ButtonModule, IconModule, TypographyModule, CurrencyPipe, CardComponent], }) export class PricingCardComponent { readonly tagline = input.required<string>(); From 93227324bf69ecb9bd765beb270920b106bc3612 Mon Sep 17 00:00:00 2001 From: tangowithfoxtrot <5676771+tangowithfoxtrot@users.noreply.github.com> Date: Mon, 27 Oct 2025 08:22:13 -0700 Subject: [PATCH 13/18] [SM-1465] - Add Terraform provider to integrations page (#16876) * fix: add Datadog org integration service to SM integrations module * misc: add Terraform provider integration card * misc: update Ansible integration link --- .../images/secrets-manager/integrations/terraform.svg | 6 ++++++ .../integrations/integrations.component.spec.ts | 8 +++++++- .../integrations/integrations.component.ts | 9 ++++++++- 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 apps/web/src/images/secrets-manager/integrations/terraform.svg diff --git a/apps/web/src/images/secrets-manager/integrations/terraform.svg b/apps/web/src/images/secrets-manager/integrations/terraform.svg new file mode 100644 index 00000000000..813e95e0200 --- /dev/null +++ b/apps/web/src/images/secrets-manager/integrations/terraform.svg @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg xmlns="http://www.w3.org/2000/svg" width="98" height="111" viewBox="0 0 98 111" fill="none"> + <path d="M67.34 37.0927V72.1132L97.68 54.6122V19.5547L67.34 37.0927Z" fill="#4040B2"></path> + <path d="M33.6699 19.5547L64.0099 37.0927V72.1132L33.6699 54.5937V19.5547Z" fill="#5C4EE5"></path> + <path d="M0 0V35.039L30.34 52.5585V17.5195L0 0ZM33.67 93.4805L64.01 111V75.961L33.67 58.4415V93.4805Z" fill="#5C4EE5"></path> +</svg> diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.spec.ts b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.spec.ts index 43d512439f0..978cfeb1aa4 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.spec.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.spec.ts @@ -74,7 +74,13 @@ describe("IntegrationsComponent", () => { (integrationList.componentInstance as IntegrationGridComponent).integrations.map( (i) => i.name, ), - ).toEqual(["GitHub Actions", "GitLab CI/CD", "Ansible", "Kubernetes Operator"]); + ).toEqual([ + "GitHub Actions", + "GitLab CI/CD", + "Ansible", + "Kubernetes Operator", + "Terraform Provider", + ]); expect( (sdkList.componentInstance as IntegrationGridComponent).integrations.map((i) => i.name), diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.ts index 31aff308c51..b2279775191 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.ts @@ -36,7 +36,7 @@ export class IntegrationsComponent { }, { name: "Ansible", - linkURL: "https://bitwarden.com/help/ansible-integration/", + linkURL: "https://galaxy.ansible.com/ui/repo/published/bitwarden/secrets", image: "../../../../../../../images/secrets-manager/integrations/ansible.svg", type: IntegrationType.Integration, }, @@ -96,6 +96,13 @@ export class IntegrationsComponent { type: IntegrationType.Integration, newBadgeExpiration: "2024-8-12", }, + { + name: "Terraform Provider", + linkURL: "https://registry.terraform.io/providers/bitwarden/bitwarden-secrets/latest", + image: "../../../../../../../images/secrets-manager/integrations/terraform.svg", + type: IntegrationType.Integration, + newBadgeExpiration: "2025-12-12", // December 12, 2025 + }, ]; } From d5f2c9d5ec234a9cddc577319fcbbd7a14061eb1 Mon Sep 17 00:00:00 2001 From: Mick Letofsky <mletofsky@bitwarden.com> Date: Mon, 27 Oct 2025 16:25:40 +0100 Subject: [PATCH 14/18] Implement reusable Claude code review workflow (#16979) --- CLAUDE.md => .claude/CLAUDE.md | 0 .claude/prompts/review-code.md | 25 +++++++ .github/workflows/review-code.yml | 118 ++---------------------------- .gitignore | 1 - 4 files changed, 32 insertions(+), 112 deletions(-) rename CLAUDE.md => .claude/CLAUDE.md (100%) create mode 100644 .claude/prompts/review-code.md diff --git a/CLAUDE.md b/.claude/CLAUDE.md similarity index 100% rename from CLAUDE.md rename to .claude/CLAUDE.md diff --git a/.claude/prompts/review-code.md b/.claude/prompts/review-code.md new file mode 100644 index 00000000000..4e5f40b2743 --- /dev/null +++ b/.claude/prompts/review-code.md @@ -0,0 +1,25 @@ +Please review this pull request with a focus on: + +- Code quality and best practices +- Potential bugs or issues +- Security implications +- Performance considerations + +Note: The PR branch is already checked out in the current working directory. + +Provide a comprehensive review including: + +- Summary of changes since last review +- Critical issues found (be thorough) +- Suggested improvements (be thorough) +- Good practices observed (be concise - list only the most notable items without elaboration) +- Action items for the author +- Leverage collapsible <details> sections where appropriate for lengthy explanations or code snippets to enhance human readability + +When reviewing subsequent commits: + +- Track status of previously identified issues (fixed/unfixed/reopened) +- Identify NEW problems introduced since last review +- Note if fixes introduced new issues + +IMPORTANT: Be comprehensive about issues and improvements. For good practices, be brief - just note what was done well without explaining why or praising excessively. diff --git a/.github/workflows/review-code.yml b/.github/workflows/review-code.yml index 83cbc3bb547..46309af38ea 100644 --- a/.github/workflows/review-code.yml +++ b/.github/workflows/review-code.yml @@ -1,124 +1,20 @@ -name: Review code +name: Code Review on: pull_request: - types: [opened, synchronize, reopened] + types: [opened, synchronize, reopened, ready_for_review] permissions: {} jobs: review: name: Review - runs-on: ubuntu-24.04 + uses: bitwarden/gh-actions/.github/workflows/_review-code.yml@main + secrets: + AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} permissions: contents: read id-token: write pull-requests: write - - steps: - - name: Check out repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - with: - fetch-depth: 0 - persist-credentials: false - - - name: Check for Vault team changes - id: check_changes - run: | - # Ensure we have the base branch - git fetch origin ${{ github.base_ref }} - - echo "Comparing changes between origin/${{ github.base_ref }} and HEAD" - CHANGED_FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD) - - if [ -z "$CHANGED_FILES" ]; then - echo "Zero files changed" - echo "vault_team_changes=false" >> $GITHUB_OUTPUT - exit 0 - fi - - # Handle variations in spacing and multiple teams - VAULT_PATTERNS=$(grep -E "@bitwarden/team-vault-dev(\s|$)" .github/CODEOWNERS 2>/dev/null | awk '{print $1}') - - if [ -z "$VAULT_PATTERNS" ]; then - echo "⚠️ No patterns found for @bitwarden/team-vault-dev in CODEOWNERS" - echo "vault_team_changes=false" >> $GITHUB_OUTPUT - exit 0 - fi - - vault_team_changes=false - for pattern in $VAULT_PATTERNS; do - echo "Checking pattern: $pattern" - - # Handle **/directory patterns - if [[ "$pattern" == "**/"* ]]; then - # Remove the **/ prefix - dir_pattern="${pattern#\*\*/}" - # Check if any file contains this directory in its path - if echo "$CHANGED_FILES" | grep -qE "(^|/)${dir_pattern}(/|$)"; then - vault_team_changes=true - echo "✅ Found files matching pattern: $pattern" - echo "$CHANGED_FILES" | grep -E "(^|/)${dir_pattern}(/|$)" | sed 's/^/ - /' - break - fi - else - # Handle other patterns (shouldn't happen based on your CODEOWNERS) - if echo "$CHANGED_FILES" | grep -q "$pattern"; then - vault_team_changes=true - echo "✅ Found files matching pattern: $pattern" - echo "$CHANGED_FILES" | grep "$pattern" | sed 's/^/ - /' - break - fi - fi - done - - echo "vault_team_changes=$vault_team_changes" >> $GITHUB_OUTPUT - - if [ "$vault_team_changes" = "true" ]; then - echo "" - echo "✅ Vault team changes detected - proceeding with review" - else - echo "" - echo "❌ No Vault team changes detected - skipping review" - fi - - - name: Review with Claude Code - if: steps.check_changes.outputs.vault_team_changes == 'true' - uses: anthropics/claude-code-action@ac1a3207f3f00b4a37e2f3a6f0935733c7c64651 # v1.0.11 - with: - anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} - track_progress: true - use_sticky_comment: true - prompt: | - REPO: ${{ github.repository }} - PR NUMBER: ${{ github.event.pull_request.number }} - TITLE: ${{ github.event.pull_request.title }} - BODY: ${{ github.event.pull_request.body }} - AUTHOR: ${{ github.event.pull_request.user.login }} - COMMIT: ${{ github.event.pull_request.head.sha }} - - Please review this pull request with a focus on: - - Code quality and best practices - - Potential bugs or issues - - Security implications - - Performance considerations - - Note: The PR branch is already checked out in the current working directory. - - Provide a comprehensive review including: - - Summary of changes since last review - - Critical issues found (be thorough) - - Suggested improvements (be thorough) - - Good practices observed (be concise - list only the most notable items without elaboration) - - Action items for the author - - Leverage collapsible <details> sections where appropriate for lengthy explanations or code snippets to enhance human readability - - When reviewing subsequent commits: - - Track status of previously identified issues (fixed/unfixed/reopened) - - Identify NEW problems introduced since last review - - Note if fixes introduced new issues - - IMPORTANT: Be comprehensive about issues and improvements. For good practices, be brief - just note what was done well without explaining why or praising excessively. - - claude_args: | - --allowedTools "mcp__github_comment__update_claude_comment,mcp__github_inline_comment__create_inline_comment,Bash(gh pr diff:*),Bash(gh pr view:*)" diff --git a/.gitignore b/.gitignore index 6b13d22caa7..a88c3bd133b 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,6 @@ Thumbs.db *.launch .settings/ *.sublime-workspace -.claude .serena # Visual Studio Code From b3359872132bb4a63199aedc0936d6e6876432b6 Mon Sep 17 00:00:00 2001 From: Kyle Denney <4227399+kdenney@users.noreply.github.com> Date: Mon, 27 Oct 2025 10:44:56 -0500 Subject: [PATCH 15/18] [PM-27267] fix disappearing border from upgrade plan card (#17007) --- .../billing/organizations/change-plan-dialog.component.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/web/src/app/billing/organizations/change-plan-dialog.component.ts b/apps/web/src/app/billing/organizations/change-plan-dialog.component.ts index ac415ac4be2..e2a30dd585c 100644 --- a/apps/web/src/app/billing/organizations/change-plan-dialog.component.ts +++ b/apps/web/src/app/billing/organizations/change-plan-dialog.component.ts @@ -451,9 +451,9 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { "tw-border-solid", "tw-border-primary-600", "hover:tw-border-primary-700", - "focus:tw-border-2", - "focus:tw-border-primary-700", - "focus:tw-rounded-lg", + "tw-border-2", + "!tw-border-primary-700", + "tw-rounded-lg", ]; } case PlanCardState.NotSelected: { From bd89c0ce6de41140a609e7aabb3697c48edd615a Mon Sep 17 00:00:00 2001 From: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> Date: Mon, 27 Oct 2025 11:04:17 -0500 Subject: [PATCH 16/18] [PM-23628] Require userId for fetching provider keys (#16993) * remove getProviderKey and expose providerKeys$ * update consumers --- .../organization-plans.component.ts | 15 ++- ...-existing-organization-dialog.component.ts | 6 ++ .../dialogs/bulk-confirm-dialog.component.ts | 16 ++- .../providers/manage/members.component.ts | 14 ++- .../services/web-provider.service.spec.ts | 101 ++++++++++++++++-- .../services/web-provider.service.ts | 38 ++++--- .../src/abstractions/key.service.ts | 18 ++-- libs/key-management/src/key.service.spec.ts | 45 +++++++- libs/key-management/src/key.service.ts | 34 ++---- 9 files changed, 223 insertions(+), 64 deletions(-) diff --git a/apps/web/src/app/billing/organizations/organization-plans.component.ts b/apps/web/src/app/billing/organizations/organization-plans.component.ts index a4ebba7a760..7c081b38279 100644 --- a/apps/web/src/app/billing/organizations/organization-plans.component.ts +++ b/apps/web/src/app/billing/organizations/organization-plans.component.ts @@ -31,6 +31,7 @@ import { ProviderOrganizationCreateRequest } from "@bitwarden/common/admin-conso import { ProviderResponse } from "@bitwarden/common/admin-console/models/response/provider/provider.response"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; +import { assertNonNullish } from "@bitwarden/common/auth/utils"; import { PlanSponsorshipType, PlanType, ProductTierType } from "@bitwarden/common/billing/enums"; import { BillingResponse } from "@bitwarden/common/billing/models/response/billing.response"; import { OrganizationSubscriptionResponse } from "@bitwarden/common/billing/models/response/organization-subscription.response"; @@ -41,7 +42,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; -import { OrganizationId } from "@bitwarden/common/types/guid"; +import { OrganizationId, ProviderId, UserId } from "@bitwarden/common/types/guid"; import { OrgKey } from "@bitwarden/common/types/key"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; import { ToastService } from "@bitwarden/components"; @@ -654,7 +655,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { orgId = this.selfHosted ? await this.createSelfHosted(key, collectionCt, orgKeys) - : await this.createCloudHosted(key, collectionCt, orgKeys, orgKey[1]); + : await this.createCloudHosted(key, collectionCt, orgKeys, orgKey[1], activeUserId); this.toastService.showToast({ variant: "success", @@ -808,6 +809,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { collectionCt: string, orgKeys: [string, EncString], orgKey: SymmetricCryptoKey, + activeUserId: UserId, ): Promise<string> { const request = new OrganizationCreateRequest(); request.key = key; @@ -855,7 +857,14 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { this.formGroup.controls.clientOwnerEmail.value, request, ); - const providerKey = await this.keyService.getProviderKey(this.providerId); + + const providerKey = await firstValueFrom( + this.keyService + .providerKeys$(activeUserId) + .pipe(map((providerKeys) => providerKeys?.[this.providerId as ProviderId] ?? null)), + ); + assertNonNullish(providerKey, "Provider key not found"); + providerRequest.organizationCreateRequest.key = ( await this.encryptService.wrapSymmetricKey(orgKey, providerKey) ).encryptedString; diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/clients/add-existing-organization-dialog.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/clients/add-existing-organization-dialog.component.ts index e36e4e5f0c6..8ce8153b36e 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/clients/add-existing-organization-dialog.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/clients/add-existing-organization-dialog.component.ts @@ -1,8 +1,11 @@ import { Component, Inject, OnInit } from "@angular/core"; +import { firstValueFrom } from "rxjs"; import { ProviderApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/provider/provider-api.service.abstraction"; import { Provider } from "@bitwarden/common/admin-console/models/domain/provider"; import { AddableOrganizationResponse } from "@bitwarden/common/admin-console/models/response/addable-organization.response"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { DIALOG_DATA, @@ -46,6 +49,7 @@ export class AddExistingOrganizationDialogComponent implements OnInit { private providerApiService: ProviderApiServiceAbstraction, private toastService: ToastService, private webProviderService: WebProviderService, + private accountService: AccountService, ) {} async ngOnInit() { @@ -57,9 +61,11 @@ export class AddExistingOrganizationDialogComponent implements OnInit { addExistingOrganization = async (): Promise<void> => { if (this.selectedOrganization) { + const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$)); await this.webProviderService.addOrganizationToProvider( this.dialogParams.provider.id, this.selectedOrganization.id, + userId, ); this.toastService.showToast({ diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/dialogs/bulk-confirm-dialog.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/dialogs/bulk-confirm-dialog.component.ts index dd54b842062..7ade77ed01b 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/dialogs/bulk-confirm-dialog.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/dialogs/bulk-confirm-dialog.component.ts @@ -1,6 +1,7 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { Component, Inject } from "@angular/core"; +import { firstValueFrom, map, Observable, switchMap } from "rxjs"; import { OrganizationUserBulkPublicKeyResponse, @@ -12,10 +13,14 @@ import { ProviderUserBulkConfirmRequest } from "@bitwarden/common/admin-console/ import { ProviderUserBulkRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-user-bulk.request"; import { ProviderUserBulkPublicKeyResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-user-bulk-public-key.response"; import { ProviderUserBulkResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-user-bulk.response"; +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"; import { ListResponse } from "@bitwarden/common/models/response/list.response"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; +import { ProviderId } from "@bitwarden/common/types/guid"; +import { ProviderKey } from "@bitwarden/common/types/key"; import { DIALOG_DATA, DialogConfig, DialogService } from "@bitwarden/components"; import { KeyService } from "@bitwarden/key-management"; import { BaseBulkConfirmComponent } from "@bitwarden/web-vault/app/admin-console/organizations/members/components/bulk/base-bulk-confirm.component"; @@ -35,6 +40,7 @@ type BulkConfirmDialogParams = { }) export class BulkConfirmDialogComponent extends BaseBulkConfirmComponent { providerId: string; + providerKey$: Observable<ProviderKey>; constructor( private apiService: ApiService, @@ -42,15 +48,21 @@ export class BulkConfirmDialogComponent extends BaseBulkConfirmComponent { protected encryptService: EncryptService, @Inject(DIALOG_DATA) protected dialogParams: BulkConfirmDialogParams, protected i18nService: I18nService, + private accountService: AccountService, ) { super(keyService, encryptService, i18nService); this.providerId = dialogParams.providerId; + this.providerKey$ = this.accountService.activeAccount$.pipe( + getUserId, + switchMap((userId) => this.keyService.providerKeys$(userId)), + map((providerKeysById) => providerKeysById?.[this.providerId as ProviderId]), + ); this.users = dialogParams.users; } - protected getCryptoKey = (): Promise<SymmetricCryptoKey> => - this.keyService.getProviderKey(this.providerId); + protected getCryptoKey = async (): Promise<SymmetricCryptoKey> => + await firstValueFrom(this.providerKey$); protected getPublicKeys = async (): Promise< ListResponse<OrganizationUserBulkPublicKeyResponse | ProviderUserBulkPublicKeyResponse> diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/members.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/members.component.ts index b1cd52cf8a6..268a82ac12f 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/members.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/members.component.ts @@ -4,7 +4,7 @@ import { Component } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { ActivatedRoute, Router } from "@angular/router"; import { combineLatest, firstValueFrom, lastValueFrom, switchMap } from "rxjs"; -import { first } from "rxjs/operators"; +import { first, map } from "rxjs/operators"; import { UserNamePipe } from "@bitwarden/angular/pipes/user-name.pipe"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; @@ -16,11 +16,13 @@ import { ProviderUserConfirmRequest } from "@bitwarden/common/admin-console/mode import { ProviderUserUserDetailsResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-user.response"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; +import { assertNonNullish } from "@bitwarden/common/auth/utils"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { ListResponse } from "@bitwarden/common/models/response/list.response"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; +import { ProviderId } from "@bitwarden/common/types/guid"; import { DialogRef, DialogService, ToastService } from "@bitwarden/components"; import { KeyService } from "@bitwarden/key-management"; import { BaseMembersComponent } from "@bitwarden/web-vault/app/admin-console/common/base-members.component"; @@ -204,7 +206,15 @@ export class MembersComponent extends BaseMembersComponent<ProviderUser> { async confirmUser(user: ProviderUser, publicKey: Uint8Array): Promise<MemberActionResult> { try { - const providerKey = await this.keyService.getProviderKey(this.providerId); + const providerKey = await firstValueFrom( + this.accountService.activeAccount$.pipe( + getUserId, + switchMap((userId) => this.keyService.providerKeys$(userId)), + map((providerKeys) => providerKeys?.[this.providerId as ProviderId] ?? null), + ), + ); + assertNonNullish(providerKey, "Provider key not found"); + const key = await this.encryptService.encapsulateKeyUnsigned(providerKey, publicKey); const request = new ProviderUserConfirmRequest(); request.key = key.encryptedString; diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/services/web-provider.service.spec.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/services/web-provider.service.spec.ts index b2da18dd047..2accd760fcb 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/services/web-provider.service.spec.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/services/web-provider.service.spec.ts @@ -1,4 +1,5 @@ import { MockProxy, mock } from "jest-mock-extended"; +import { of } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { ProviderApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/provider/provider-api.service.abstraction"; @@ -8,7 +9,6 @@ import { EncryptService } from "@bitwarden/common/key-management/crypto/abstract import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; -import { StateProvider } from "@bitwarden/common/platform/state"; import { OrgKey, ProviderKey } from "@bitwarden/common/types/key"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; import { newGuid } from "@bitwarden/guid"; @@ -24,16 +24,22 @@ describe("WebProviderService", () => { let apiService: MockProxy<ApiService>; let i18nService: MockProxy<I18nService>; let encryptService: MockProxy<EncryptService>; - let stateProvider: MockProxy<StateProvider>; let providerApiService: MockProxy<ProviderApiServiceAbstraction>; + const activeUserId = newGuid() as UserId; + const providerId = "provider-123"; + const mockOrgKey = new SymmetricCryptoKey(new Uint8Array(64)) as OrgKey; + const mockProviderKey = new SymmetricCryptoKey(new Uint8Array(64)) as ProviderKey; + const mockProviderKeysById: Record<string, ProviderKey> = { + [providerId]: mockProviderKey, + }; + beforeEach(() => { keyService = mock(); syncService = mock(); apiService = mock(); i18nService = mock(); encryptService = mock(); - stateProvider = mock(); providerApiService = mock(); sut = new WebProviderService( @@ -42,14 +48,69 @@ describe("WebProviderService", () => { apiService, i18nService, encryptService, - stateProvider, providerApiService, ); }); + describe("addOrganizationToProvider", () => { + const organizationId = "org-789"; + const encryptedOrgKey = new EncString("encrypted-org-key"); + const mockOrgKeysById: Record<string, OrgKey> = { + [organizationId]: mockOrgKey, + }; + + beforeEach(() => { + keyService.orgKeys$.mockReturnValue(of(mockOrgKeysById)); + keyService.providerKeys$.mockReturnValue(of(mockProviderKeysById)); + encryptService.wrapSymmetricKey.mockResolvedValue(encryptedOrgKey); + }); + + it("adds an organization to a provider with correct encryption", async () => { + await sut.addOrganizationToProvider(providerId, organizationId, activeUserId); + + expect(keyService.orgKeys$).toHaveBeenCalledWith(activeUserId); + expect(keyService.providerKeys$).toHaveBeenCalledWith(activeUserId); + expect(encryptService.wrapSymmetricKey).toHaveBeenCalledWith(mockOrgKey, mockProviderKey); + expect(providerApiService.addOrganizationToProvider).toHaveBeenCalledWith(providerId, { + key: encryptedOrgKey.encryptedString, + organizationId, + }); + expect(syncService.fullSync).toHaveBeenCalledWith(true); + }); + + it("throws an error if organization key is not found", async () => { + const invalidOrgId = "invalid-org"; + + await expect( + sut.addOrganizationToProvider(providerId, invalidOrgId, activeUserId), + ).rejects.toThrow("Organization key not found"); + }); + + it("throws an error if no organization keys are available", async () => { + keyService.orgKeys$.mockReturnValue(of(null)); + + await expect( + sut.addOrganizationToProvider(providerId, organizationId, activeUserId), + ).rejects.toThrow("Organization key not found"); + }); + + it("throws an error if provider key is not found", async () => { + const invalidProviderId = "invalid-provider"; + await expect( + sut.addOrganizationToProvider(invalidProviderId, organizationId, activeUserId), + ).rejects.toThrow("Provider key not found"); + }); + + it("throws an error if no provider keys are available", async () => { + keyService.providerKeys$.mockReturnValue(of(null)); + + await expect( + sut.addOrganizationToProvider(providerId, organizationId, activeUserId), + ).rejects.toThrow("Provider key not found"); + }); + }); + describe("createClientOrganization", () => { - const activeUserId = newGuid() as UserId; - const providerId = "provider-123"; const name = "Test Org"; const ownerEmail = "owner@example.com"; const planType = PlanType.EnterpriseAnnually; @@ -59,15 +120,13 @@ describe("WebProviderService", () => { const encryptedProviderKey = new EncString("encrypted-provider-key"); const encryptedCollectionName = new EncString("encrypted-collection-name"); const defaultCollectionTranslation = "Default Collection"; - const mockOrgKey = new SymmetricCryptoKey(new Uint8Array(64)) as OrgKey; - const mockProviderKey = new SymmetricCryptoKey(new Uint8Array(64)) as ProviderKey; beforeEach(() => { keyService.makeOrgKey.mockResolvedValue([new EncString("mockEncryptedKey"), mockOrgKey]); keyService.makeKeyPair.mockResolvedValue([publicKey, encryptedPrivateKey]); i18nService.t.mockReturnValue(defaultCollectionTranslation); encryptService.encryptString.mockResolvedValue(encryptedCollectionName); - keyService.getProviderKey.mockResolvedValue(mockProviderKey); + keyService.providerKeys$.mockReturnValue(of(mockProviderKeysById)); encryptService.wrapSymmetricKey.mockResolvedValue(encryptedProviderKey); }); @@ -88,7 +147,7 @@ describe("WebProviderService", () => { defaultCollectionTranslation, mockOrgKey, ); - expect(keyService.getProviderKey).toHaveBeenCalledWith(providerId); + expect(keyService.providerKeys$).toHaveBeenCalledWith(activeUserId); expect(encryptService.wrapSymmetricKey).toHaveBeenCalledWith(mockOrgKey, mockProviderKey); expect(providerApiService.createProviderOrganization).toHaveBeenCalledWith( @@ -107,5 +166,27 @@ describe("WebProviderService", () => { expect(apiService.refreshIdentityToken).toHaveBeenCalled(); expect(syncService.fullSync).toHaveBeenCalledWith(true); }); + + it("throws an error if provider key is not found", async () => { + const invalidProviderId = "invalid-provider"; + await expect( + sut.createClientOrganization( + invalidProviderId, + name, + ownerEmail, + planType, + seats, + activeUserId, + ), + ).rejects.toThrow("Provider key not found"); + }); + + it("throws an error if no provider keys are available", async () => { + keyService.providerKeys$.mockReturnValue(of(null)); + + await expect( + sut.createClientOrganization(providerId, name, ownerEmail, planType, seats, activeUserId), + ).rejects.toThrow("Provider key not found"); + }); }); }); diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/services/web-provider.service.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/services/web-provider.service.ts index 78931f9c445..e1eea78d26a 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/services/web-provider.service.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/services/web-provider.service.ts @@ -1,18 +1,17 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { Injectable } from "@angular/core"; -import { firstValueFrom, map } from "rxjs"; -import { switchMap } from "rxjs/operators"; +import { combineLatest, firstValueFrom, map } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { ProviderApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/provider/provider-api.service.abstraction"; import { CreateProviderOrganizationRequest } from "@bitwarden/common/admin-console/models/request/create-provider-organization.request"; import { OrganizationKeysRequest } from "@bitwarden/common/admin-console/models/request/organization-keys.request"; +import { assertNonNullish } from "@bitwarden/common/auth/utils"; import { PlanType } from "@bitwarden/common/billing/enums"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { StateProvider } from "@bitwarden/common/platform/state"; -import { OrganizationId, UserId } from "@bitwarden/common/types/guid"; +import { OrganizationId, ProviderId, UserId } from "@bitwarden/common/types/guid"; import { OrgKey } from "@bitwarden/common/types/key"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; import { KeyService } from "@bitwarden/key-management"; @@ -25,18 +24,26 @@ export class WebProviderService { private apiService: ApiService, private i18nService: I18nService, private encryptService: EncryptService, - private stateProvider: StateProvider, private providerApiService: ProviderApiServiceAbstraction, ) {} - async addOrganizationToProvider(providerId: string, organizationId: string): Promise<void> { - const orgKey = await firstValueFrom( - this.stateProvider.activeUserId$.pipe( - switchMap((userId) => this.keyService.orgKeys$(userId)), - map((organizationKeysById) => organizationKeysById[organizationId as OrganizationId]), - ), + async addOrganizationToProvider( + providerId: string, + organizationId: string, + activeUserId: UserId, + ): Promise<void> { + const [orgKeysById, providerKeys] = await firstValueFrom( + combineLatest([ + this.keyService.orgKeys$(activeUserId), + this.keyService.providerKeys$(activeUserId), + ]), ); - const providerKey = await this.keyService.getProviderKey(providerId); + + const orgKey = orgKeysById?.[organizationId as OrganizationId]; + const providerKey = providerKeys?.[providerId as ProviderId]; + assertNonNullish(orgKey, "Organization key not found"); + assertNonNullish(providerKey, "Provider key not found"); + const encryptedOrgKey = await this.encryptService.wrapSymmetricKey(orgKey, providerKey); await this.providerApiService.addOrganizationToProvider(providerId, { key: encryptedOrgKey.encryptedString, @@ -62,7 +69,12 @@ export class WebProviderService { organizationKey, ); - const providerKey = await this.keyService.getProviderKey(providerId); + const providerKey = await firstValueFrom( + this.keyService + .providerKeys$(activeUserId) + .pipe(map((providerKeys) => providerKeys?.[providerId as ProviderId])), + ); + assertNonNullish(providerKey, "Provider key not found"); const encryptedProviderKey = await this.encryptService.wrapSymmetricKey( organizationKey, diff --git a/libs/key-management/src/abstractions/key.service.ts b/libs/key-management/src/abstractions/key.service.ts index abd4dcc1563..7891c9952b2 100644 --- a/libs/key-management/src/abstractions/key.service.ts +++ b/libs/key-management/src/abstractions/key.service.ts @@ -10,7 +10,7 @@ import { import { WrappedSigningKey } from "@bitwarden/common/key-management/types"; import { KeySuffixOptions, HashPurpose } from "@bitwarden/common/platform/enums"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; -import { OrganizationId, UserId } from "@bitwarden/common/types/guid"; +import { OrganizationId, ProviderId, UserId } from "@bitwarden/common/types/guid"; import { UserKey, MasterKey, @@ -248,17 +248,19 @@ export abstract class KeyService { /** * Stores the provider keys for a given user. - * @param orgs The provider orgs for which to save the keys from. + * @param providers The provider orgs for which to save the keys from. * @param userId The user id of the user for which to store the keys for. */ - abstract setProviderKeys(orgs: ProfileProviderResponse[], userId: UserId): Promise<void>; + abstract setProviderKeys(providers: ProfileProviderResponse[], userId: UserId): Promise<void>; + /** - * - * @throws Error when providerId is null or no active user - * @param providerId The desired provider - * @returns The provider's symmetric key + * Gets an observable of provider keys for the given user. + * @param userId The user to get provider keys for. + * @return An observable stream of the users providers keys if they are unlocked, or null if the user is not unlocked. + * @throws If an invalid user id is passed in. */ - abstract getProviderKey(providerId: string): Promise<ProviderKey | null>; + abstract providerKeys$(userId: UserId): Observable<Record<ProviderId, ProviderKey> | null>; + /** * Creates a new organization key and encrypts it with the user's public key. * This method can also return Provider keys for creating new Provider users. diff --git a/libs/key-management/src/key.service.spec.ts b/libs/key-management/src/key.service.spec.ts index 0dd9f3603f5..5d5340d4900 100644 --- a/libs/key-management/src/key.service.spec.ts +++ b/libs/key-management/src/key.service.spec.ts @@ -39,7 +39,7 @@ import { FakeSingleUserState, } from "@bitwarden/common/spec"; import { CsprngArray } from "@bitwarden/common/types/csprng"; -import { OrganizationId, UserId } from "@bitwarden/common/types/guid"; +import { OrganizationId, ProviderId, UserId } from "@bitwarden/common/types/guid"; import { UserKey, MasterKey, @@ -1314,6 +1314,49 @@ describe("keyService", () => { }); }); + describe("providerKeys$", () => { + let mockUserPrivateKey: Uint8Array; + let mockProviderKeys: Record<ProviderId, ProviderKey>; + + beforeEach(() => { + mockUserPrivateKey = makeStaticByteArray(64, 1); + mockProviderKeys = { + ["provider1" as ProviderId]: makeSymmetricCryptoKey<ProviderKey>(64), + ["provider2" as ProviderId]: makeSymmetricCryptoKey<ProviderKey>(64), + }; + }); + + it("returns null when userPrivateKey is null", async () => { + jest.spyOn(keyService, "userPrivateKey$").mockReturnValue(of(null)); + + const result = await firstValueFrom(keyService.providerKeys$(mockUserId)); + + expect(result).toBeNull(); + }); + + it("returns provider keys when userPrivateKey is available", async () => { + jest.spyOn(keyService, "userPrivateKey$").mockReturnValue(of(mockUserPrivateKey as any)); + jest.spyOn(keyService as any, "providerKeysHelper$").mockReturnValue(of(mockProviderKeys)); + + const result = await firstValueFrom(keyService.providerKeys$(mockUserId)); + + expect(result).toEqual(mockProviderKeys); + expect((keyService as any).providerKeysHelper$).toHaveBeenCalledWith( + mockUserId, + mockUserPrivateKey, + ); + }); + + it("returns null when providerKeysHelper$ returns null", async () => { + jest.spyOn(keyService, "userPrivateKey$").mockReturnValue(of(mockUserPrivateKey as any)); + jest.spyOn(keyService as any, "providerKeysHelper$").mockReturnValue(of(null)); + + const result = await firstValueFrom(keyService.providerKeys$(mockUserId)); + + expect(result).toBeNull(); + }); + }); + describe("makeKeyPair", () => { test.each([null as unknown as SymmetricCryptoKey, undefined as unknown as SymmetricCryptoKey])( "throws when the provided key is %s", diff --git a/libs/key-management/src/key.service.ts b/libs/key-management/src/key.service.ts index fc340410124..032faeaf42e 100644 --- a/libs/key-management/src/key.service.ts +++ b/libs/key-management/src/key.service.ts @@ -426,20 +426,16 @@ export class DefaultKeyService implements KeyServiceAbstraction { }); } - // TODO: Deprecate in favor of observable - async getProviderKey(providerId: ProviderId): Promise<ProviderKey | null> { - if (providerId == null) { - return null; - } + providerKeys$(userId: UserId): Observable<Record<ProviderId, ProviderKey> | null> { + return this.userPrivateKey$(userId).pipe( + switchMap((userPrivateKey) => { + if (userPrivateKey == null) { + return of(null); + } - const activeUserId = await firstValueFrom(this.stateProvider.activeUserId$); - if (activeUserId == null) { - throw new Error("No active user found."); - } - - const providerKeys = await firstValueFrom(this.providerKeys$(activeUserId)); - - return providerKeys?.[providerId] ?? null; + return this.providerKeysHelper$(userId, userPrivateKey); + }), + ); } private async clearProviderKeys(userId: UserId): Promise<void> { @@ -829,18 +825,6 @@ export class DefaultKeyService implements KeyServiceAbstraction { )) as UserPrivateKey; } - providerKeys$(userId: UserId) { - return this.userPrivateKey$(userId).pipe( - switchMap((userPrivateKey) => { - if (userPrivateKey == null) { - return of(null); - } - - return this.providerKeysHelper$(userId, userPrivateKey); - }), - ); - } - /** * A helper for decrypting provider keys that requires a user id and that users decrypted private key * this is helpful for when you may have already grabbed the user private key and don't want to redo From 42377a1533903c7f82dcb51d8837b09ae64bf950 Mon Sep 17 00:00:00 2001 From: Oscar Hinton <Hinton@users.noreply.github.com> Date: Mon, 27 Oct 2025 17:24:50 +0100 Subject: [PATCH 17/18] [PM-27341] Chrome importer refactors (#16720) Various refactors to the chrome importer --- .github/CODEOWNERS | 2 +- apps/desktop/desktop_native/Cargo.lock | 54 +++++++++---------- apps/desktop/desktop_native/Cargo.toml | 2 +- .../bitwarden_chromium_importer/src/crypto.rs | 48 ----------------- .../bitwarden_chromium_importer/src/lib.rs | 8 --- .../Cargo.toml | 5 +- .../README.md | 23 +++++--- .../src/chromium/mod.rs} | 48 +++++++---------- .../src/chromium/platform}/linux.rs | 4 +- .../src/chromium/platform}/macos.rs | 4 +- .../src/chromium/platform/mod.rs | 7 +++ .../src/chromium/platform}/windows.rs | 5 +- .../chromium_importer/src/lib.rs | 5 ++ .../src/metadata.rs | 9 ++-- .../src/util.rs | 54 +++++++++++-------- apps/desktop/desktop_native/napi/Cargo.toml | 2 +- apps/desktop/desktop_native/napi/index.d.ts | 14 ++--- apps/desktop/desktop_native/napi/src/lib.rs | 42 +++++++++++---- .../import/desktop-import-metadata.service.ts | 6 ++- apps/desktop/src/app/tools/preload.ts | 4 +- 20 files changed, 161 insertions(+), 185 deletions(-) delete mode 100644 apps/desktop/desktop_native/bitwarden_chromium_importer/src/crypto.rs delete mode 100644 apps/desktop/desktop_native/bitwarden_chromium_importer/src/lib.rs rename apps/desktop/desktop_native/{bitwarden_chromium_importer => chromium_importer}/Cargo.toml (92%) rename apps/desktop/desktop_native/{bitwarden_chromium_importer => chromium_importer}/README.md (94%) rename apps/desktop/desktop_native/{bitwarden_chromium_importer/src/chromium.rs => chromium_importer/src/chromium/mod.rs} (89%) rename apps/desktop/desktop_native/{bitwarden_chromium_importer/src => chromium_importer/src/chromium/platform}/linux.rs (97%) rename apps/desktop/desktop_native/{bitwarden_chromium_importer/src => chromium_importer/src/chromium/platform}/macos.rs (97%) create mode 100644 apps/desktop/desktop_native/chromium_importer/src/chromium/platform/mod.rs rename apps/desktop/desktop_native/{bitwarden_chromium_importer/src => chromium_importer/src/chromium/platform}/windows.rs (97%) create mode 100644 apps/desktop/desktop_native/chromium_importer/src/lib.rs rename apps/desktop/desktop_native/{bitwarden_chromium_importer => chromium_importer}/src/metadata.rs (96%) rename apps/desktop/desktop_native/{bitwarden_chromium_importer => chromium_importer}/src/util.rs (77%) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f784f375086..8affac3387b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -30,7 +30,7 @@ libs/common/src/auth @bitwarden/team-auth-dev apps/browser/src/tools @bitwarden/team-tools-dev apps/cli/src/tools @bitwarden/team-tools-dev apps/desktop/src/app/tools @bitwarden/team-tools-dev -apps/desktop/desktop_native/bitwarden_chromium_importer @bitwarden/team-tools-dev +apps/desktop/desktop_native/chromium_importer @bitwarden/team-tools-dev apps/web/src/app/tools @bitwarden/team-tools-dev libs/angular/src/tools @bitwarden/team-tools-dev libs/common/src/models/export @bitwarden/team-tools-dev diff --git a/apps/desktop/desktop_native/Cargo.lock b/apps/desktop/desktop_native/Cargo.lock index 5e658546671..a0cd1b3dcbf 100644 --- a/apps/desktop/desktop_native/Cargo.lock +++ b/apps/desktop/desktop_native/Cargo.lock @@ -440,33 +440,6 @@ dependencies = [ "tokio-util", ] -[[package]] -name = "bitwarden_chromium_importer" -version = "0.0.0" -dependencies = [ - "aes", - "aes-gcm", - "anyhow", - "async-trait", - "base64", - "cbc", - "hex", - "homedir", - "napi", - "napi-derive", - "oo7", - "pbkdf2", - "rand 0.9.1", - "rusqlite", - "security-framework", - "serde", - "serde_json", - "sha1", - "tokio", - "winapi", - "windows 0.61.1", -] - [[package]] name = "block-buffer" version = "0.10.4" @@ -606,6 +579,31 @@ dependencies = [ "zeroize", ] +[[package]] +name = "chromium_importer" +version = "0.0.0" +dependencies = [ + "aes", + "aes-gcm", + "anyhow", + "async-trait", + "base64", + "cbc", + "hex", + "homedir", + "oo7", + "pbkdf2", + "rand 0.9.1", + "rusqlite", + "security-framework", + "serde", + "serde_json", + "sha1", + "tokio", + "winapi", + "windows 0.61.1", +] + [[package]] name = "cipher" version = "0.4.4" @@ -968,7 +966,7 @@ dependencies = [ "anyhow", "autotype", "base64", - "bitwarden_chromium_importer", + "chromium_importer", "desktop_core", "hex", "napi", diff --git a/apps/desktop/desktop_native/Cargo.toml b/apps/desktop/desktop_native/Cargo.toml index 2168eaa0068..6a366316328 100644 --- a/apps/desktop/desktop_native/Cargo.toml +++ b/apps/desktop/desktop_native/Cargo.toml @@ -2,7 +2,7 @@ resolver = "2" members = [ "autotype", - "bitwarden_chromium_importer", + "chromium_importer", "core", "macos_provider", "napi", diff --git a/apps/desktop/desktop_native/bitwarden_chromium_importer/src/crypto.rs b/apps/desktop/desktop_native/bitwarden_chromium_importer/src/crypto.rs deleted file mode 100644 index e6442e21742..00000000000 --- a/apps/desktop/desktop_native/bitwarden_chromium_importer/src/crypto.rs +++ /dev/null @@ -1,48 +0,0 @@ -//! Cryptographic primitives used in the SDK - -use anyhow::{anyhow, Result}; - -use aes::cipher::{ - block_padding::Pkcs7, generic_array::GenericArray, typenum::U32, BlockDecryptMut, KeyIvInit, -}; - -pub fn decrypt_aes256(iv: &[u8; 16], data: &[u8], key: GenericArray<u8, U32>) -> Result<Vec<u8>> { - let iv = GenericArray::from_slice(iv); - let mut data = data.to_vec(); - cbc::Decryptor::<aes::Aes256>::new(&key, iv) - .decrypt_padded_mut::<Pkcs7>(&mut data) - .map_err(|_| anyhow!("Failed to decrypt data"))?; - - Ok(data) -} - -#[cfg(test)] -mod tests { - use aes::cipher::{ - generic_array::{sequence::GenericSequence, GenericArray}, - ArrayLength, - }; - use base64::{engine::general_purpose::STANDARD, Engine}; - - pub fn generate_vec(length: usize, offset: u8, increment: u8) -> Vec<u8> { - (0..length).map(|i| offset + i as u8 * increment).collect() - } - pub fn generate_generic_array<N: ArrayLength<u8>>( - offset: u8, - increment: u8, - ) -> GenericArray<u8, N> { - GenericArray::generate(|i| offset + i as u8 * increment) - } - - #[test] - fn test_decrypt_aes256() { - let iv = generate_vec(16, 0, 1); - let iv: &[u8; 16] = iv.as_slice().try_into().unwrap(); - let key = generate_generic_array(0, 1); - let data: Vec<u8> = STANDARD.decode("ByUF8vhyX4ddU9gcooznwA==").unwrap(); - - let decrypted = super::decrypt_aes256(iv, &data, key).unwrap(); - - assert_eq!(String::from_utf8(decrypted).unwrap(), "EncryptMe!\u{6}\u{6}\u{6}\u{6}\u{6}\u{6}"); - } -} diff --git a/apps/desktop/desktop_native/bitwarden_chromium_importer/src/lib.rs b/apps/desktop/desktop_native/bitwarden_chromium_importer/src/lib.rs deleted file mode 100644 index 84f140d2341..00000000000 --- a/apps/desktop/desktop_native/bitwarden_chromium_importer/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -#[macro_use] -extern crate napi_derive; - -pub mod chromium; -pub mod metadata; -pub mod util; - -pub use crate::chromium::platform::SUPPORTED_BROWSERS as PLATFORM_SUPPORTED_BROWSERS; diff --git a/apps/desktop/desktop_native/bitwarden_chromium_importer/Cargo.toml b/apps/desktop/desktop_native/chromium_importer/Cargo.toml similarity index 92% rename from apps/desktop/desktop_native/bitwarden_chromium_importer/Cargo.toml rename to apps/desktop/desktop_native/chromium_importer/Cargo.toml index 656c3ad1504..648a36543c2 100644 --- a/apps/desktop/desktop_native/bitwarden_chromium_importer/Cargo.toml +++ b/apps/desktop/desktop_native/chromium_importer/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "bitwarden_chromium_importer" +name = "chromium_importer" edition = { workspace = true } license = { workspace = true } version = { workspace = true } @@ -14,8 +14,6 @@ base64 = { workspace = true } cbc = { workspace = true, features = ["alloc"] } hex = { workspace = true } homedir = { workspace = true } -napi = { workspace = true } -napi-derive = { workspace = true } pbkdf2 = "=0.12.2" rand = { workspace = true } rusqlite = { version = "=0.37.0", features = ["bundled"] } @@ -36,4 +34,3 @@ oo7 = { workspace = true } [lints] workspace = true - diff --git a/apps/desktop/desktop_native/bitwarden_chromium_importer/README.md b/apps/desktop/desktop_native/chromium_importer/README.md similarity index 94% rename from apps/desktop/desktop_native/bitwarden_chromium_importer/README.md rename to apps/desktop/desktop_native/chromium_importer/README.md index 498dd3ac67d..dd563697e5b 100644 --- a/apps/desktop/desktop_native/bitwarden_chromium_importer/README.md +++ b/apps/desktop/desktop_native/chromium_importer/README.md @@ -1,6 +1,13 @@ -# Windows ABE Architecture +# Chromium Direct Importer -## Overview +A rust library that allows you to directly import credentials from Chromium-based browsers. + +## Windows ABE Architecture + +On Windows chrome has additional protection measurements which needs to be circumvented in order to +get access to the passwords. + +### Overview The Windows Application Bound Encryption (ABE) consists of three main components that work together: @@ -10,7 +17,7 @@ The Windows Application Bound Encryption (ABE) consists of three main components _(The names of the binaries will be changed for the released product.)_ -## The goal +### The goal The goal of this subsystem is to decrypt the master encryption key with which the login information is encrypted on the local system in Windows. This applies to the most recent versions of Chrome and @@ -24,7 +31,7 @@ Protection API at the system level on top of that. This triply encrypted key is The next paragraphs describe what is done at each level to decrypt the key. -## 1. Client library +### 1. Client library This is a Rust module that is part of the Chromium importer. It only compiles and runs on Windows (see `abe.rs` and `abe_config.rs`). Its main task is to launch `admin.exe` with elevated privileges @@ -52,7 +59,7 @@ admin.exe --service-exe "c:\temp\service.exe" --encrypted "QVBQQgEAAADQjJ3fARXRE **At this point, the user must permit the action to be performed on the UAC screen.** -## 2. Admin executable +### 2. Admin executable This executable receives the full path of `service.exe` and the data to be decrypted. @@ -67,7 +74,7 @@ is sent to the named pipe server created by the user. The user responds with `ok After that, the executable stops and uninstalls the service and then exits. -## 3. System service +### 3. System service The service starts and creates a named pipe server for communication between `admin.exe` and the system service. Please note that it is not possible to communicate between the user and the system @@ -83,7 +90,7 @@ removed from the system. Even though we send only one request, the service is de many clients with as many messages as needed and could be installed on the system permanently if necessary. -## 4. Back to client library +### 4. Back to client library The decrypted base64-encoded string comes back from the admin executable to the named pipe server at the user level. At this point, it has been decrypted only once at the system level. @@ -99,7 +106,7 @@ itself), it's either AES-256-GCM or ChaCha20Poly1305 encryption scheme. The deta After all of these steps, we have the master key which can be used to decrypt the password information stored in the local database. -## Summary +### Summary The Windows ABE decryption process involves a three-tier architecture with named pipe communication: diff --git a/apps/desktop/desktop_native/bitwarden_chromium_importer/src/chromium.rs b/apps/desktop/desktop_native/chromium_importer/src/chromium/mod.rs similarity index 89% rename from apps/desktop/desktop_native/bitwarden_chromium_importer/src/chromium.rs rename to apps/desktop/desktop_native/chromium_importer/src/chromium/mod.rs index 094500e6d42..55728460436 100644 --- a/apps/desktop/desktop_native/bitwarden_chromium_importer/src/chromium.rs +++ b/apps/desktop/desktop_native/chromium_importer/src/chromium/mod.rs @@ -7,11 +7,9 @@ use hex::decode; use homedir::my_home; use rusqlite::{params, Connection}; -// Platform-specific code -#[cfg_attr(target_os = "linux", path = "linux.rs")] -#[cfg_attr(target_os = "windows", path = "windows.rs")] -#[cfg_attr(target_os = "macos", path = "macos.rs")] -pub mod platform; +mod platform; + +pub(crate) use platform::SUPPORTED_BROWSERS as PLATFORM_SUPPORTED_BROWSERS; // // Public API @@ -22,10 +20,7 @@ pub struct ProfileInfo { pub name: String, pub folder: String, - #[allow(dead_code)] pub account_name: Option<String>, - - #[allow(dead_code)] pub account_email: Option<String>, } @@ -113,12 +108,12 @@ pub async fn import_logins( // #[derive(Debug, Clone, Copy)] -pub struct BrowserConfig { +pub(crate) struct BrowserConfig { pub name: &'static str, pub data_dir: &'static str, } -pub static SUPPORTED_BROWSER_MAP: LazyLock< +pub(crate) static SUPPORTED_BROWSER_MAP: LazyLock< std::collections::HashMap<&'static str, &'static BrowserConfig>, > = LazyLock::new(|| { platform::SUPPORTED_BROWSERS @@ -140,12 +135,12 @@ fn get_browser_data_dir(config: &BrowserConfig) -> Result<PathBuf> { // #[async_trait] -pub trait CryptoService: Send { +pub(crate) trait CryptoService: Send { async fn decrypt_to_string(&mut self, encrypted: &[u8]) -> Result<String>; } #[derive(serde::Deserialize, Clone)] -pub struct LocalState { +pub(crate) struct LocalState { profile: AllProfiles, #[allow(dead_code)] os_crypt: Option<OsCrypt>, @@ -198,16 +193,17 @@ fn load_local_state(browser_dir: &Path) -> Result<LocalState> { } fn get_profile_info(local_state: &LocalState) -> Vec<ProfileInfo> { - let mut profile_infos = Vec::new(); - for (name, info) in local_state.profile.info_cache.iter() { - profile_infos.push(ProfileInfo { + local_state + .profile + .info_cache + .iter() + .map(|(name, info)| ProfileInfo { name: info.name.clone(), folder: name.clone(), account_name: info.gaia_name.clone(), account_email: info.user_name.clone(), - }); - } - profile_infos + }) + .collect() } struct EncryptedLogin { @@ -264,17 +260,16 @@ fn hex_to_bytes(hex: &str) -> Vec<u8> { decode(hex).unwrap_or_default() } -fn does_table_exist(conn: &Connection, table_name: &str) -> Result<bool, rusqlite::Error> { - let mut stmt = conn.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name=?1")?; - let exists = stmt.exists(params![table_name])?; - Ok(exists) +fn table_exist(conn: &Connection, table_name: &str) -> Result<bool, rusqlite::Error> { + conn.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name=?1")? + .exists(params![table_name]) } fn query_logins(db_path: &str) -> Result<Vec<EncryptedLogin>, rusqlite::Error> { let conn = Connection::open(db_path)?; - let have_logins = does_table_exist(&conn, "logins")?; - let have_password_notes = does_table_exist(&conn, "password_notes")?; + let have_logins = table_exist(&conn, "logins")?; + let have_password_notes = table_exist(&conn, "password_notes")?; if !have_logins || !have_password_notes { return Ok(vec![]); } @@ -308,10 +303,7 @@ fn query_logins(db_path: &str) -> Result<Vec<EncryptedLogin>, rusqlite::Error> { }) })?; - let mut logins = Vec::new(); - for login in logins_iter { - logins.push(login?); - } + let logins = logins_iter.collect::<Result<Vec<_>, _>>()?; Ok(logins) } diff --git a/apps/desktop/desktop_native/bitwarden_chromium_importer/src/linux.rs b/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/linux.rs similarity index 97% rename from apps/desktop/desktop_native/bitwarden_chromium_importer/src/linux.rs rename to apps/desktop/desktop_native/chromium_importer/src/chromium/platform/linux.rs index be3bcdb1e1d..227dffdcca7 100644 --- a/apps/desktop/desktop_native/bitwarden_chromium_importer/src/linux.rs +++ b/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/linux.rs @@ -13,7 +13,7 @@ use crate::util; // // TODO: It's possible that there might be multiple possible data directories, depending on the installation method (e.g., snap, flatpak, etc.). -pub const SUPPORTED_BROWSERS: [BrowserConfig; 4] = [ +pub(crate) const SUPPORTED_BROWSERS: &[BrowserConfig] = &[ BrowserConfig { name: "Chrome", data_dir: ".config/google-chrome", @@ -32,7 +32,7 @@ pub const SUPPORTED_BROWSERS: [BrowserConfig; 4] = [ }, ]; -pub fn get_crypto_service( +pub(crate) fn get_crypto_service( browser_name: &String, _local_state: &LocalState, ) -> Result<Box<dyn CryptoService>> { diff --git a/apps/desktop/desktop_native/bitwarden_chromium_importer/src/macos.rs b/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/macos.rs similarity index 97% rename from apps/desktop/desktop_native/bitwarden_chromium_importer/src/macos.rs rename to apps/desktop/desktop_native/chromium_importer/src/chromium/platform/macos.rs index bcb2c005000..c0e770c161b 100644 --- a/apps/desktop/desktop_native/bitwarden_chromium_importer/src/macos.rs +++ b/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/macos.rs @@ -10,7 +10,7 @@ use crate::util; // Public API // -pub const SUPPORTED_BROWSERS: [BrowserConfig; 7] = [ +pub(crate) const SUPPORTED_BROWSERS: &[BrowserConfig] = &[ BrowserConfig { name: "Chrome", data_dir: "Library/Application Support/Google/Chrome", @@ -41,7 +41,7 @@ pub const SUPPORTED_BROWSERS: [BrowserConfig; 7] = [ }, ]; -pub fn get_crypto_service( +pub(crate) fn get_crypto_service( browser_name: &String, _local_state: &LocalState, ) -> Result<Box<dyn CryptoService>> { diff --git a/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/mod.rs b/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/mod.rs new file mode 100644 index 00000000000..2a21ef23d82 --- /dev/null +++ b/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/mod.rs @@ -0,0 +1,7 @@ +// Platform-specific code +#[cfg_attr(target_os = "linux", path = "linux.rs")] +#[cfg_attr(target_os = "windows", path = "windows.rs")] +#[cfg_attr(target_os = "macos", path = "macos.rs")] +mod native; + +pub(crate) use native::*; diff --git a/apps/desktop/desktop_native/bitwarden_chromium_importer/src/windows.rs b/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/windows.rs similarity index 97% rename from apps/desktop/desktop_native/bitwarden_chromium_importer/src/windows.rs rename to apps/desktop/desktop_native/chromium_importer/src/chromium/platform/windows.rs index 096808aafb6..79c462c29a1 100644 --- a/apps/desktop/desktop_native/bitwarden_chromium_importer/src/windows.rs +++ b/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/windows.rs @@ -15,8 +15,7 @@ use crate::util; // Public API // -// IMPORTANT adjust array size when enabling / disabling chromium importers here -pub const SUPPORTED_BROWSERS: [BrowserConfig; 6] = [ +pub(crate) const SUPPORTED_BROWSERS: &[BrowserConfig] = &[ BrowserConfig { name: "Brave", data_dir: "AppData/Local/BraveSoftware/Brave-Browser/User Data", @@ -43,7 +42,7 @@ pub const SUPPORTED_BROWSERS: [BrowserConfig; 6] = [ }, ]; -pub fn get_crypto_service( +pub(crate) fn get_crypto_service( _browser_name: &str, local_state: &LocalState, ) -> Result<Box<dyn CryptoService>> { diff --git a/apps/desktop/desktop_native/chromium_importer/src/lib.rs b/apps/desktop/desktop_native/chromium_importer/src/lib.rs new file mode 100644 index 00000000000..d92515c39f9 --- /dev/null +++ b/apps/desktop/desktop_native/chromium_importer/src/lib.rs @@ -0,0 +1,5 @@ +#![doc = include_str!("../README.md")] + +pub mod chromium; +pub mod metadata; +mod util; diff --git a/apps/desktop/desktop_native/bitwarden_chromium_importer/src/metadata.rs b/apps/desktop/desktop_native/chromium_importer/src/metadata.rs similarity index 96% rename from apps/desktop/desktop_native/bitwarden_chromium_importer/src/metadata.rs rename to apps/desktop/desktop_native/chromium_importer/src/metadata.rs index 28f13cd9863..bfd7f184621 100644 --- a/apps/desktop/desktop_native/bitwarden_chromium_importer/src/metadata.rs +++ b/apps/desktop/desktop_native/chromium_importer/src/metadata.rs @@ -1,8 +1,7 @@ use std::collections::{HashMap, HashSet}; -use crate::{chromium::InstalledBrowserRetriever, PLATFORM_SUPPORTED_BROWSERS}; +use crate::chromium::{InstalledBrowserRetriever, PLATFORM_SUPPORTED_BROWSERS}; -#[napi(object)] /// Mechanisms that load data into the importer pub struct NativeImporterMetadata { /// Identifies the importer @@ -24,7 +23,7 @@ pub fn get_supported_importers<T: InstalledBrowserRetriever>( // Check for installed browsers let installed_browsers = T::get_installed_browsers().unwrap_or_default(); - const IMPORTERS: [(&str, &str); 6] = [ + const IMPORTERS: &[(&str, &str)] = &[ ("chromecsv", "Chrome"), ("chromiumcsv", "Chromium"), ("bravecsv", "Brave"), @@ -57,9 +56,7 @@ pub fn get_supported_importers<T: InstalledBrowserRetriever>( map } -/* - Tests are cfg-gated based upon OS, and must be compiled/run on each OS for full coverage -*/ +// Tests are cfg-gated based upon OS, and must be compiled/run on each OS for full coverage #[cfg(test)] mod tests { use super::*; diff --git a/apps/desktop/desktop_native/bitwarden_chromium_importer/src/util.rs b/apps/desktop/desktop_native/chromium_importer/src/util.rs similarity index 77% rename from apps/desktop/desktop_native/bitwarden_chromium_importer/src/util.rs rename to apps/desktop/desktop_native/chromium_importer/src/util.rs index e9c20ab621d..f346d7e6dd0 100644 --- a/apps/desktop/desktop_native/bitwarden_chromium_importer/src/util.rs +++ b/apps/desktop/desktop_native/chromium_importer/src/util.rs @@ -1,9 +1,6 @@ -use aes::cipher::{block_padding::Pkcs7, BlockDecryptMut, KeyIvInit}; use anyhow::{anyhow, Result}; -use pbkdf2::{hmac::Hmac, pbkdf2}; -use sha1::Sha1; -pub fn split_encrypted_string(encrypted: &[u8]) -> Result<(&str, &[u8])> { +fn split_encrypted_string(encrypted: &[u8]) -> Result<(&str, &[u8])> { if encrypted.len() < 3 { return Err(anyhow!( "Corrupted entry: invalid encrypted string length, expected at least 3 bytes, got {}", @@ -15,7 +12,14 @@ pub fn split_encrypted_string(encrypted: &[u8]) -> Result<(&str, &[u8])> { Ok((std::str::from_utf8(version)?, password)) } -pub fn split_encrypted_string_and_validate<'a>( +/// A Chromium password consists of three parts: +/// - Version (3 bytes): "v10", "v11", etc. +/// - Cipher text (chunks of 16 bytes) +/// - Padding (1-15 bytes) +/// +/// This function splits the encrypted byte slice into version and cipher text. +/// Padding is included and handled by the underlying cryptographic library. +pub(crate) fn split_encrypted_string_and_validate<'a>( encrypted: &'a [u8], supported_versions: &[&str], ) -> Result<(&'a str, &'a [u8])> { @@ -27,15 +31,22 @@ pub fn split_encrypted_string_and_validate<'a>( Ok((version, password)) } -pub fn decrypt_aes_128_cbc(key: &[u8], iv: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>> { - let decryptor = cbc::Decryptor::<aes::Aes128>::new_from_slices(key, iv)?; - let plaintext: Vec<u8> = decryptor +/// Decrypt using AES-128 in CBC mode. +#[cfg(any(target_os = "linux", target_os = "macos", test))] +pub(crate) fn decrypt_aes_128_cbc(key: &[u8], iv: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>> { + use aes::cipher::{block_padding::Pkcs7, BlockDecryptMut, KeyIvInit}; + + cbc::Decryptor::<aes::Aes128>::new_from_slices(key, iv)? .decrypt_padded_vec_mut::<Pkcs7>(ciphertext) - .map_err(|e| anyhow!("Failed to decrypt: {}", e))?; - Ok(plaintext) + .map_err(|e| anyhow!("Failed to decrypt: {}", e)) } -pub fn derive_saltysalt(password: &[u8], iterations: u32) -> Result<Vec<u8>> { +/// Derives a PBKDF2 key from the static "saltysalt" salt with the given password and iteration count. +#[cfg(any(target_os = "linux", target_os = "macos"))] +pub(crate) fn derive_saltysalt(password: &[u8], iterations: u32) -> Result<Vec<u8>> { + use pbkdf2::{hmac::Hmac, pbkdf2}; + use sha1::Sha1; + let mut key = vec![0u8; 16]; pbkdf2::<Hmac<Sha1>>(password, b"saltysalt", iterations, &mut key) .map_err(|e| anyhow!("Failed to derive master key: {}", e))?; @@ -44,16 +55,6 @@ pub fn derive_saltysalt(password: &[u8], iterations: u32) -> Result<Vec<u8>> { #[cfg(test)] mod tests { - pub fn generate_vec(length: usize, offset: u8, increment: u8) -> Vec<u8> { - (0..length).map(|i| offset + i as u8 * increment).collect() - } - pub fn generate_generic_array<N: ArrayLength<u8>>( - offset: u8, - increment: u8, - ) -> GenericArray<u8, N> { - GenericArray::generate(|i| offset + i as u8 * increment) - } - use aes::cipher::{ block_padding::Pkcs7, generic_array::{sequence::GenericSequence, GenericArray}, @@ -64,6 +65,17 @@ mod tests { const LENGTH10: usize = 10; const LENGTH0: usize = 0; + fn generate_vec(length: usize, offset: u8, increment: u8) -> Vec<u8> { + (0..length).map(|i| offset + i as u8 * increment).collect() + } + + fn generate_generic_array<N: ArrayLength<u8>>( + offset: u8, + increment: u8, + ) -> GenericArray<u8, N> { + GenericArray::generate(|i| offset + i as u8 * increment) + } + fn run_split_encrypted_string_test<'a, const N: usize>( successfully_split: bool, plaintext_to_encrypt: &'a str, diff --git a/apps/desktop/desktop_native/napi/Cargo.toml b/apps/desktop/desktop_native/napi/Cargo.toml index 5e2e42b463f..4198baa4b5a 100644 --- a/apps/desktop/desktop_native/napi/Cargo.toml +++ b/apps/desktop/desktop_native/napi/Cargo.toml @@ -17,7 +17,7 @@ manual_test = [] anyhow = { workspace = true } autotype = { path = "../autotype" } base64 = { workspace = true } -bitwarden_chromium_importer = { path = "../bitwarden_chromium_importer" } +chromium_importer = { path = "../chromium_importer" } desktop_core = { path = "../core" } hex = { workspace = true } napi = { workspace = true, features = ["async"] } diff --git a/apps/desktop/desktop_native/napi/index.d.ts b/apps/desktop/desktop_native/napi/index.d.ts index 59751cd3246..cd49e5ac27a 100644 --- a/apps/desktop/desktop_native/napi/index.d.ts +++ b/apps/desktop/desktop_native/napi/index.d.ts @@ -3,15 +3,6 @@ /* auto-generated by NAPI-RS */ -/** Mechanisms that load data into the importer */ -export interface NativeImporterMetadata { - /** Identifies the importer */ - id: string - /** Describes the strategies used to obtain imported data */ - loaders: Array<string> - /** Identifies the instructions for the importer */ - instructions: string -} export declare namespace passwords { /** The error message returned when a password is not found during retrieval or deletion. */ export const PASSWORD_NOT_FOUND: string @@ -249,6 +240,11 @@ export declare namespace chromium_importer { login?: Login failure?: LoginImportFailure } + export interface NativeImporterMetadata { + id: string + loaders: Array<string> + instructions: string + } /** Returns OS aware metadata describing supported Chromium based importers as a JSON string. */ export function getMetadata(): Record<string, NativeImporterMetadata> export function getInstalledBrowsers(): Array<string> diff --git a/apps/desktop/desktop_native/napi/src/lib.rs b/apps/desktop/desktop_native/napi/src/lib.rs index 09f63f7854b..61453994d72 100644 --- a/apps/desktop/desktop_native/napi/src/lib.rs +++ b/apps/desktop/desktop_native/napi/src/lib.rs @@ -1064,11 +1064,13 @@ pub mod logging { #[napi] pub mod chromium_importer { - use bitwarden_chromium_importer::chromium::DefaultInstalledBrowserRetriever; - use bitwarden_chromium_importer::chromium::InstalledBrowserRetriever; - use bitwarden_chromium_importer::chromium::LoginImportResult as _LoginImportResult; - use bitwarden_chromium_importer::chromium::ProfileInfo as _ProfileInfo; - use bitwarden_chromium_importer::metadata::NativeImporterMetadata; + use chromium_importer::{ + chromium::{ + DefaultInstalledBrowserRetriever, InstalledBrowserRetriever, + LoginImportResult as _LoginImportResult, ProfileInfo as _ProfileInfo, + }, + metadata::NativeImporterMetadata as _NativeImporterMetadata, + }; use std::collections::HashMap; #[napi(object)] @@ -1098,6 +1100,13 @@ pub mod chromium_importer { pub failure: Option<LoginImportFailure>, } + #[napi(object)] + pub struct NativeImporterMetadata { + pub id: String, + pub loaders: Vec<&'static str>, + pub instructions: &'static str, + } + impl From<_LoginImportResult> for LoginImportResult { fn from(l: _LoginImportResult) -> Self { match l { @@ -1131,23 +1140,34 @@ pub mod chromium_importer { } } + impl From<_NativeImporterMetadata> for NativeImporterMetadata { + fn from(m: _NativeImporterMetadata) -> Self { + NativeImporterMetadata { + id: m.id, + loaders: m.loaders, + instructions: m.instructions, + } + } + } + #[napi] /// Returns OS aware metadata describing supported Chromium based importers as a JSON string. pub fn get_metadata() -> HashMap<String, NativeImporterMetadata> { - bitwarden_chromium_importer::metadata::get_supported_importers::< - DefaultInstalledBrowserRetriever, - >() + chromium_importer::metadata::get_supported_importers::<DefaultInstalledBrowserRetriever>() + .into_iter() + .map(|(browser, metadata)| (browser, NativeImporterMetadata::from(metadata))) + .collect() } #[napi] pub fn get_installed_browsers() -> napi::Result<Vec<String>> { - bitwarden_chromium_importer::chromium::DefaultInstalledBrowserRetriever::get_installed_browsers() + chromium_importer::chromium::DefaultInstalledBrowserRetriever::get_installed_browsers() .map_err(|e| napi::Error::from_reason(e.to_string())) } #[napi] pub fn get_available_profiles(browser: String) -> napi::Result<Vec<ProfileInfo>> { - bitwarden_chromium_importer::chromium::get_available_profiles(&browser) + chromium_importer::chromium::get_available_profiles(&browser) .map(|profiles| profiles.into_iter().map(ProfileInfo::from).collect()) .map_err(|e| napi::Error::from_reason(e.to_string())) } @@ -1157,7 +1177,7 @@ pub mod chromium_importer { browser: String, profile_id: String, ) -> napi::Result<Vec<LoginImportResult>> { - bitwarden_chromium_importer::chromium::import_logins(&browser, &profile_id) + chromium_importer::chromium::import_logins(&browser, &profile_id) .await .map(|logins| logins.into_iter().map(LoginImportResult::from).collect()) .map_err(|e| napi::Error::from_reason(e.to_string())) diff --git a/apps/desktop/src/app/tools/import/desktop-import-metadata.service.ts b/apps/desktop/src/app/tools/import/desktop-import-metadata.service.ts index fc2c2ff1183..0c29cd9f44a 100644 --- a/apps/desktop/src/app/tools/import/desktop-import-metadata.service.ts +++ b/apps/desktop/src/app/tools/import/desktop-import-metadata.service.ts @@ -1,5 +1,5 @@ import { SystemServiceProvider } from "@bitwarden/common/tools/providers"; -import type { NativeImporterMetadata } from "@bitwarden/desktop-napi"; +import type { chromium_importer } from "@bitwarden/desktop-napi"; import { ImportType, DefaultImportMetadataService, @@ -25,7 +25,9 @@ export class DesktopImportMetadataService await super.init(); } - private async parseNativeMetaData(raw: Record<string, NativeImporterMetadata>): Promise<void> { + private async parseNativeMetaData( + raw: Record<string, chromium_importer.NativeImporterMetadata>, + ): Promise<void> { const entries = Object.entries(raw).map(([id, meta]) => { const loaders = meta.loaders.map(this.mapLoader); const instructions = this.mapInstructions(meta.instructions); diff --git a/apps/desktop/src/app/tools/preload.ts b/apps/desktop/src/app/tools/preload.ts index 4d629c992ad..b872f108551 100644 --- a/apps/desktop/src/app/tools/preload.ts +++ b/apps/desktop/src/app/tools/preload.ts @@ -1,9 +1,9 @@ import { ipcRenderer } from "electron"; -import type { NativeImporterMetadata } from "@bitwarden/desktop-napi"; +import type { chromium_importer } from "@bitwarden/desktop-napi"; const chromiumImporter = { - getMetadata: (): Promise<Record<string, NativeImporterMetadata>> => + getMetadata: (): Promise<Record<string, chromium_importer.NativeImporterMetadata>> => ipcRenderer.invoke("chromium_importer.getMetadata"), getInstalledBrowsers: (): Promise<string[]> => ipcRenderer.invoke("chromium_importer.getInstalledBrowsers"), From 2ff9c23dc4d96b5c038e8468e37a4791a0aca9a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mauritz=20Sj=C3=B6din?= <67279312+Mauritz8@users.noreply.github.com> Date: Mon, 27 Oct 2025 17:34:22 +0100 Subject: [PATCH 18/18] fix: prevent action buttons from overflowing for long passwords (#17027) Co-authored-by: Bryan Cunningham <bcunningham@bitwarden.com> --- libs/components/src/form-field/form-field.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/components/src/form-field/form-field.component.html b/libs/components/src/form-field/form-field.component.html index c2c92104727..a4af25a2492 100644 --- a/libs/components/src/form-field/form-field.component.html +++ b/libs/components/src/form-field/form-field.component.html @@ -97,7 +97,7 @@ <ng-container *ngTemplateOutlet="prefixContent"></ng-container> </div> <div - class="tw-w-full tw-pb-0 tw-relative [&>*]:tw-p-0 [&>*::selection]:tw-bg-primary-700 [&>*::selection]:tw-text-contrast" + class="tw-w-full tw-min-w-0 tw-pb-0 tw-relative [&>*]:tw-p-0 [&>*::selection]:tw-bg-primary-700 [&>*::selection]:tw-text-contrast" data-default-content > <ng-container *ngTemplateOutlet="defaultContent"></ng-container>