From 1457a97640430d6ccb951e3156dee9ce51f07cae Mon Sep 17 00:00:00 2001 From: cyprain-okeke <108260115+cyprain-okeke@users.noreply.github.com> Date: Tue, 29 Apr 2025 09:21:37 +0100 Subject: [PATCH 001/116] Fix the message display (#14419) --- .../organizations/manage/events.component.html | 6 +++--- apps/web/src/locales/en/messages.json | 17 ++++------------- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/apps/web/src/app/admin-console/organizations/manage/events.component.html b/apps/web/src/app/admin-console/organizations/manage/events.component.html index 2079d592a28..02be3476ad5 100644 --- a/apps/web/src/app/admin-console/organizations/manage/events.component.html +++ b/apps/web/src/app/admin-console/organizations/manage/events.component.html @@ -63,7 +63,7 @@ {{ "upgradeEventLogMessage" | i18n }} @@ -125,10 +125,10 @@

- {{ "limitedEventLogs" | i18n: ProductTierType[organization?.productTierType] }} + {{ "upgradeEventLogTitleMessage" | i18n }}

- {{ "upgradeForFullEvents" | i18n }} + {{ "upgradeForFullEventsMessage" | i18n }}

+ + - - - - - - - - - {{ "addNewOrganization" | i18n }} - - + + diff --git a/bitwarden_license/bit-web/src/app/billing/providers/clients/manage-clients.component.ts b/bitwarden_license/bit-web/src/app/billing/providers/clients/manage-clients.component.ts index 3322fd3a85b..a57e6351349 100644 --- a/bitwarden_license/bit-web/src/app/billing/providers/clients/manage-clients.component.ts +++ b/bitwarden_license/bit-web/src/app/billing/providers/clients/manage-clients.component.ts @@ -15,8 +15,6 @@ import { Provider } from "@bitwarden/common/admin-console/models/domain/provider import { ProviderOrganizationOrganizationDetailsResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-organization.response"; import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions"; import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; import { @@ -73,9 +71,6 @@ export class ManageClientsComponent { protected searchControl = new FormControl("", { nonNullable: true }); protected plans: PlanResponse[] = []; - protected addExistingOrgsFromProviderPortal$ = this.configService.getFeatureFlag$( - FeatureFlag.PM15179_AddExistingOrgsFromProviderPortal, - ); pageTitle = this.i18nService.t("clients"); clientColumnHeader = this.i18nService.t("client"); @@ -91,7 +86,6 @@ export class ManageClientsComponent { private toastService: ToastService, private validationService: ValidationService, private webProviderService: WebProviderService, - private configService: ConfigService, private billingNotificationService: BillingNotificationService, ) { this.activatedRoute.queryParams.pipe(first(), takeUntilDestroyed()).subscribe((queryParams) => { diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index 4eabb8ea114..0e0956b0460 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -31,7 +31,6 @@ export enum FeatureFlag { /* Billing */ TrialPaymentOptional = "PM-8163-trial-payment", - PM15179_AddExistingOrgsFromProviderPortal = "pm-15179-add-existing-orgs-from-provider-portal", PM12276_BreadcrumbEventLogs = "pm-12276-breadcrumbing-for-business-features", PM18794_ProviderPaymentMethod = "pm-18794-provider-payment-method", PM17772_AdminInitiatedSponsorships = "pm-17772-admin-initiated-sponsorships", @@ -119,7 +118,6 @@ export const DefaultFeatureFlagValue = { /* Billing */ [FeatureFlag.TrialPaymentOptional]: FALSE, - [FeatureFlag.PM15179_AddExistingOrgsFromProviderPortal]: FALSE, [FeatureFlag.PM12276_BreadcrumbEventLogs]: FALSE, [FeatureFlag.PM18794_ProviderPaymentMethod]: FALSE, [FeatureFlag.PM17772_AdminInitiatedSponsorships]: FALSE, From 438f90c987a6d49397ce84935e09eadd823984e3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 29 Apr 2025 12:20:29 -0400 Subject: [PATCH 015/116] [deps] Autofill: Update tldts to v7 (#14510) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- apps/cli/package.json | 2 +- package-lock.json | 36 +++++++++++++++++++++++++++--------- package.json | 2 +- 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/apps/cli/package.json b/apps/cli/package.json index daec6593543..79d4786a23c 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -88,7 +88,7 @@ "papaparse": "5.5.2", "proper-lockfile": "4.1.2", "rxjs": "7.8.1", - "tldts": "6.1.77", + "tldts": "7.0.1", "zxcvbn": "4.4.2" } } diff --git a/package-lock.json b/package-lock.json index 3a66efd8092..0db5832feca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -68,7 +68,7 @@ "qrious": "4.0.2", "rxjs": "7.8.1", "tabbable": "6.2.0", - "tldts": "6.1.77", + "tldts": "7.0.1", "utf-8-validate": "6.0.5", "zone.js": "0.14.10", "zxcvbn": "4.4.2" @@ -222,7 +222,7 @@ "papaparse": "5.5.2", "proper-lockfile": "4.1.2", "rxjs": "7.8.1", - "tldts": "6.1.77", + "tldts": "7.0.1", "zxcvbn": "4.4.2" }, "bin": { @@ -24747,6 +24747,24 @@ "node": ">= 14" } }, + "node_modules/jsdom/node_modules/tldts": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", + "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.86" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/jsdom/node_modules/tldts-core": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", + "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", + "license": "MIT" + }, "node_modules/jsdom/node_modules/tough-cookie": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", @@ -34580,21 +34598,21 @@ } }, "node_modules/tldts": { - "version": "6.1.77", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.77.tgz", - "integrity": "sha512-lBpoWgy+kYmuXWQ83+R7LlJCnsd9YW8DGpZSHhrMl4b8Ly/1vzOie3OdtmUJDkKxcgRGOehDu5btKkty+JEe+g==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.1.tgz", + "integrity": "sha512-C3TdHZKykiDkxPIKUYCDWyYpcLQ8bDYvF/RGfH66UikQX3Kro7ij2/WGNYgp5EfxXB4+Tu5H728uAgYGNE1eaQ==", "license": "MIT", "dependencies": { - "tldts-core": "^6.1.77" + "tldts-core": "^7.0.1" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "6.1.78", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.78.tgz", - "integrity": "sha512-jS0svNsB99jR6AJBmfmEWuKIgz91Haya91Z43PATaeHJ24BkMoNRb/jlaD37VYjb0mYf6gRL/HOnvS1zEnYBiw==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.4.tgz", + "integrity": "sha512-9/IRbnIvUENGD6rg7m6Q9h/jH5ZL28hwjAhxrJx0AmcBue1FSsc84XZFaV748EsDVflid86aGDR11eSz6sbQjA==", "license": "MIT" }, "node_modules/tmp": { diff --git a/package.json b/package.json index 8aa0b618aec..0af4c01e450 100644 --- a/package.json +++ b/package.json @@ -200,7 +200,7 @@ "qrious": "4.0.2", "rxjs": "7.8.1", "tabbable": "6.2.0", - "tldts": "6.1.77", + "tldts": "7.0.1", "utf-8-validate": "6.0.5", "zone.js": "0.14.10", "zxcvbn": "4.4.2" From bcbce8385c8acb5059a7d560f24d1ab6564122ea Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 29 Apr 2025 12:25:49 -0400 Subject: [PATCH 016/116] [deps]: Update uuid to v11.1.0 (#14508) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .../native-messaging-test-runner/package-lock.json | 8 ++++---- apps/desktop/native-messaging-test-runner/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/desktop/native-messaging-test-runner/package-lock.json b/apps/desktop/native-messaging-test-runner/package-lock.json index 46cb92051b0..d506e109e94 100644 --- a/apps/desktop/native-messaging-test-runner/package-lock.json +++ b/apps/desktop/native-messaging-test-runner/package-lock.json @@ -13,7 +13,7 @@ "@bitwarden/node": "file:../../../libs/node", "module-alias": "2.2.3", "ts-node": "10.9.2", - "uuid": "11.0.5", + "uuid": "11.1.0", "yargs": "17.7.2" }, "devDependencies": { @@ -353,9 +353,9 @@ "license": "MIT" }, "node_modules/uuid": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.5.tgz", - "integrity": "sha512-508e6IcKLrhxKdBbcA2b4KQZlLVp2+J5UwQ6F7Drckkc5N9ZJwFa4TgWtsww9UG8fGHbm6gbV19TdM5pQ4GaIA==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" diff --git a/apps/desktop/native-messaging-test-runner/package.json b/apps/desktop/native-messaging-test-runner/package.json index 8a91c48eb11..f67ab259d3b 100644 --- a/apps/desktop/native-messaging-test-runner/package.json +++ b/apps/desktop/native-messaging-test-runner/package.json @@ -18,7 +18,7 @@ "@bitwarden/node": "file:../../../libs/node", "module-alias": "2.2.3", "ts-node": "10.9.2", - "uuid": "11.0.5", + "uuid": "11.1.0", "yargs": "17.7.2" }, "devDependencies": { From 751169d728e2a884e4066157dc81b596c54df1c3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 29 Apr 2025 10:03:38 -0700 Subject: [PATCH 017/116] [deps] Platform: Update Rust crate libc to v0.2.172 (#14456) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- apps/desktop/desktop_native/Cargo.lock | 4 ++-- apps/desktop/desktop_native/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/desktop/desktop_native/Cargo.lock b/apps/desktop/desktop_native/Cargo.lock index bf7fd9dcc66..c225dc49f73 100644 --- a/apps/desktop/desktop_native/Cargo.lock +++ b/apps/desktop/desktop_native/Cargo.lock @@ -1491,9 +1491,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.169" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libloading" diff --git a/apps/desktop/desktop_native/Cargo.toml b/apps/desktop/desktop_native/Cargo.toml index e6022d2c347..d9e61124864 100644 --- a/apps/desktop/desktop_native/Cargo.toml +++ b/apps/desktop/desktop_native/Cargo.toml @@ -28,7 +28,7 @@ hex = "=0.4.3" homedir = "=0.3.4" interprocess = "=2.2.1" keytar = "=0.1.6" -libc = "=0.2.169" +libc = "=0.2.172" log = "=0.4.25" napi = "=2.16.15" napi-build = "=2.1.4" From d0b93c871a1396ed9c027ccd098073e9527a0ce9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 29 Apr 2025 13:30:05 -0400 Subject: [PATCH 018/116] [deps] Autofill: Update wait-on to v8.0.3 (#14450) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package-lock.json | 22 ++++++++++++++++------ package.json | 2 +- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0db5832feca..dcfc0c475bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -178,7 +178,7 @@ "typescript-strict-plugin": "2.4.4", "url": "0.11.4", "util": "0.12.5", - "wait-on": "8.0.2", + "wait-on": "8.0.3", "webpack": "5.97.1", "webpack-cli": "6.0.1", "webpack-dev-server": "5.2.0", @@ -36915,17 +36915,17 @@ } }, "node_modules/wait-on": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-8.0.2.tgz", - "integrity": "sha512-qHlU6AawrgAIHlueGQHQ+ETcPLAauXbnoTKl3RKq20W0T8x0DKVAo5xWIYjHSyvHxQlcYbFdR0jp4T9bDVITFA==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-8.0.3.tgz", + "integrity": "sha512-nQFqAFzZDeRxsu7S3C7LbuxslHhk+gnJZHyethuGKAn2IVleIbTB9I3vJSQiSR+DifUqmdzfPMoMPJfLqMF2vw==", "dev": true, "license": "MIT", "dependencies": { - "axios": "^1.7.9", + "axios": "^1.8.2", "joi": "^17.13.3", "lodash": "^4.17.21", "minimist": "^1.2.8", - "rxjs": "^7.8.1" + "rxjs": "^7.8.2" }, "bin": { "wait-on": "bin/wait-on" @@ -36934,6 +36934,16 @@ "node": ">=12.0.0" } }, + "node_modules/wait-on/node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/walk-up-path": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-3.0.1.tgz", diff --git a/package.json b/package.json index 0af4c01e450..ad30bae428f 100644 --- a/package.json +++ b/package.json @@ -140,7 +140,7 @@ "typescript-strict-plugin": "2.4.4", "url": "0.11.4", "util": "0.12.5", - "wait-on": "8.0.2", + "wait-on": "8.0.3", "webpack": "5.97.1", "webpack-cli": "6.0.1", "webpack-dev-server": "5.2.0", From 7f4b2cba4871d7e6be6219ae6d3b1f5baa07e384 Mon Sep 17 00:00:00 2001 From: Vicki League Date: Tue, 29 Apr 2025 13:59:45 -0400 Subject: [PATCH 019/116] [PM-19437] Realign multiselect with button for the access selector in SM (#14324) --- .../access-policy-selector.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/access-policy-selector.component.html b/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/access-policy-selector.component.html index 454b497fcdb..c8a50175781 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/access-policy-selector.component.html +++ b/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/access-policy-selector.component.html @@ -1,4 +1,4 @@ -
+
{{ label }} @@ -97,7 +97,7 @@ {{ hint }} -
+
diff --git a/apps/browser/src/vault/popup/settings/folders-v2.component.html b/apps/browser/src/vault/popup/settings/folders-v2.component.html index 35a0fbec0a9..552547c0230 100644 --- a/apps/browser/src/vault/popup/settings/folders-v2.component.html +++ b/apps/browser/src/vault/popup/settings/folders-v2.component.html @@ -1,7 +1,13 @@ - diff --git a/libs/tools/send/send-ui/src/new-send-dropdown/new-send-dropdown.component.html b/libs/tools/send/send-ui/src/new-send-dropdown/new-send-dropdown.component.html index 344348f6a90..c39d95616f6 100644 --- a/libs/tools/send/send-ui/src/new-send-dropdown/new-send-dropdown.component.html +++ b/libs/tools/send/send-ui/src/new-send-dropdown/new-send-dropdown.component.html @@ -1,4 +1,4 @@ - From e596584e87109a1e96c311d550e06ff48ee88562 Mon Sep 17 00:00:00 2001 From: Vijay Oommen Date: Thu, 1 May 2025 08:03:32 -0500 Subject: [PATCH 027/116] [PM-20505] Weak-passwords-report: refresh rows after edit (#14401) --- .../reports/pages/cipher-report.component.ts | 8 +- .../pages/weak-passwords-report.component.ts | 146 ++++++++++++------ ...dmin-console-cipher-form-config.service.ts | 2 +- 3 files changed, 107 insertions(+), 49 deletions(-) diff --git a/apps/web/src/app/tools/reports/pages/cipher-report.component.ts b/apps/web/src/app/tools/reports/pages/cipher-report.component.ts index 0ad8a0a519c..ceda7b1c480 100644 --- a/apps/web/src/app/tools/reports/pages/cipher-report.component.ts +++ b/apps/web/src/app/tools/reports/pages/cipher-report.component.ts @@ -67,7 +67,7 @@ export class CipherReportComponent implements OnDestroy { protected i18nService: I18nService, private syncService: SyncService, private cipherFormConfigService: CipherFormConfigService, - private adminConsoleCipherFormConfigService: AdminConsoleCipherFormConfigService, + protected adminConsoleCipherFormConfigService: AdminConsoleCipherFormConfigService, ) { this.organizations$ = this.accountService.activeAccount$.pipe( getUserId, @@ -207,7 +207,7 @@ export class CipherReportComponent implements OnDestroy { // If the dialog was closed by deleting the cipher, refresh the report. if (result === VaultItemDialogResult.Deleted || result === VaultItemDialogResult.Saved) { - await this.load(); + await this.refresh(result, cipher); } } @@ -215,6 +215,10 @@ export class CipherReportComponent implements OnDestroy { this.allCiphers = []; } + protected async refresh(result: VaultItemDialogResult, cipher: CipherView) { + await this.load(); + } + protected async repromptCipher(c: CipherView) { return ( c.reprompt === CipherRepromptType.None || diff --git a/apps/web/src/app/tools/reports/pages/weak-passwords-report.component.ts b/apps/web/src/app/tools/reports/pages/weak-passwords-report.component.ts index f7631e37a7d..4144c9ac20f 100644 --- a/apps/web/src/app/tools/reports/pages/weak-passwords-report.component.ts +++ b/apps/web/src/app/tools/reports/pages/weak-passwords-report.component.ts @@ -1,18 +1,22 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { Component, OnInit } from "@angular/core"; +import { firstValueFrom } from "rxjs"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; 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 { Utils } from "@bitwarden/common/platform/misc/utils"; import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength"; +import { CipherId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { BadgeVariant, DialogService } from "@bitwarden/components"; import { CipherFormConfigService, PasswordRepromptService } from "@bitwarden/vault"; +import { VaultItemDialogResult } from "@bitwarden/web-vault/app/vault/components/vault-item-dialog/vault-item-dialog.component"; import { AdminConsoleCipherFormConfigService } from "../../../vault/org-vault/services/admin-console-cipher-form-config.service"; @@ -40,7 +44,7 @@ export class WeakPasswordsReportComponent extends CipherReportComponent implemen i18nService: I18nService, syncService: SyncService, cipherFormConfigService: CipherFormConfigService, - adminConsoleCipherFormConfigService: AdminConsoleCipherFormConfigService, + protected adminConsoleCipherFormConfigService: AdminConsoleCipherFormConfigService, ) { super( cipherService, @@ -66,62 +70,112 @@ export class WeakPasswordsReportComponent extends CipherReportComponent implemen this.findWeakPasswords(allCiphers); } - protected findWeakPasswords(ciphers: CipherView[]): void { - ciphers.forEach((ciph) => { - const { type, login, isDeleted, edit, viewPassword } = ciph; - if ( - type !== CipherType.Login || - login.password == null || - login.password === "" || - isDeleted || - (!this.organization && !edit) || - !viewPassword - ) { + protected async refresh(result: VaultItemDialogResult, cipher: CipherView) { + if (result === VaultItemDialogResult.Deleted) { + // remove the cipher from the list + this.weakPasswordCiphers = this.weakPasswordCiphers.filter((c) => c.id !== cipher.id); + this.filterCiphersByOrg(this.weakPasswordCiphers); + return; + } + + if (result == VaultItemDialogResult.Saved) { + const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); + let updatedCipher = await this.cipherService.get(cipher.id, activeUserId); + + if (this.isAdminConsoleActive) { + updatedCipher = await this.adminConsoleCipherFormConfigService.getCipher( + cipher.id as CipherId, + this.organization, + ); + } + + const updatedCipherView = await updatedCipher.decrypt( + await this.cipherService.getKeyForCipherKeyDecryption(updatedCipher, activeUserId), + ); + // update the cipher views + const updatedReportResult = this.determineWeakPasswordScore(updatedCipherView); + const index = this.weakPasswordCiphers.findIndex((c) => c.id === updatedCipherView.id); + + if (updatedReportResult == null) { + // the password is no longer weak + // remove the cipher from the list + this.weakPasswordCiphers.splice(index, 1); + this.filterCiphersByOrg(this.weakPasswordCiphers); return; } - const hasUserName = this.isUserNameNotEmpty(ciph); - let userInput: string[] = []; - if (hasUserName) { - const atPosition = login.username.indexOf("@"); - if (atPosition > -1) { - userInput = userInput - .concat( - login.username - .substr(0, atPosition) - .trim() - .toLowerCase() - .split(/[^A-Za-z0-9]/), - ) - .filter((i) => i.length >= 3); - } else { - userInput = login.username - .trim() - .toLowerCase() - .split(/[^A-Za-z0-9]/) - .filter((i) => i.length >= 3); - } + if (index > -1) { + // update the existing cipher + this.weakPasswordCiphers[index] = updatedReportResult; + this.filterCiphersByOrg(this.weakPasswordCiphers); } - const result = this.passwordStrengthService.getPasswordStrength( - login.password, - null, - userInput.length > 0 ? userInput : null, - ); + } + } - if (result.score != null && result.score <= 2) { - const scoreValue = this.scoreKey(result.score); - const row = { - ...ciph, - score: result.score, - reportValue: scoreValue, - scoreKey: scoreValue.sortOrder, - } as ReportResult; + protected findWeakPasswords(ciphers: CipherView[]): void { + ciphers.forEach((ciph) => { + const row = this.determineWeakPasswordScore(ciph); + if (row != null) { this.weakPasswordCiphers.push(row); } }); this.filterCiphersByOrg(this.weakPasswordCiphers); } + protected determineWeakPasswordScore(ciph: CipherView): ReportResult | null { + const { type, login, isDeleted, edit, viewPassword } = ciph; + if ( + type !== CipherType.Login || + login.password == null || + login.password === "" || + isDeleted || + (!this.organization && !edit) || + !viewPassword + ) { + return; + } + + const hasUserName = this.isUserNameNotEmpty(ciph); + let userInput: string[] = []; + if (hasUserName) { + const atPosition = login.username.indexOf("@"); + if (atPosition > -1) { + userInput = userInput + .concat( + login.username + .substr(0, atPosition) + .trim() + .toLowerCase() + .split(/[^A-Za-z0-9]/), + ) + .filter((i) => i.length >= 3); + } else { + userInput = login.username + .trim() + .toLowerCase() + .split(/[^A-Za-z0-9]/) + .filter((i) => i.length >= 3); + } + } + const result = this.passwordStrengthService.getPasswordStrength( + login.password, + null, + userInput.length > 0 ? userInput : null, + ); + + if (result.score != null && result.score <= 2) { + const scoreValue = this.scoreKey(result.score); + return { + ...ciph, + score: result.score, + reportValue: scoreValue, + scoreKey: scoreValue.sortOrder, + } as ReportResult; + } + + return null; + } + protected canManageCipher(c: CipherView): boolean { // this will only ever be false from the org view; return true; diff --git a/apps/web/src/app/vault/org-vault/services/admin-console-cipher-form-config.service.ts b/apps/web/src/app/vault/org-vault/services/admin-console-cipher-form-config.service.ts index dd9cef91a54..15af27ba8d0 100644 --- a/apps/web/src/app/vault/org-vault/services/admin-console-cipher-form-config.service.ts +++ b/apps/web/src/app/vault/org-vault/services/admin-console-cipher-form-config.service.ts @@ -100,7 +100,7 @@ export class AdminConsoleCipherFormConfigService implements CipherFormConfigServ }; } - private async getCipher(id: CipherId | null, organization: Organization): Promise { + async getCipher(id: CipherId | null, organization: Organization): Promise { if (id == null) { return null; } From abf7c949d98e9213cf37ae7339b9ab88031bb6ef Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Thu, 1 May 2025 15:22:18 +0200 Subject: [PATCH 028/116] Move additional linting to architecture (#14580) --- .github/renovate.json5 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index ee97f16b0a9..91b4ac86328 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -149,6 +149,8 @@ { matchPackageNames: [ "@angular-eslint/schematics", + "@typescript-eslint/rule-tester", + "@typescript-eslint/utils", "angular-eslint", "eslint-config-prettier", "eslint-import-resolver-typescript", @@ -313,8 +315,6 @@ "@storybook/angular", "@storybook/manager-api", "@storybook/theming", - "@typescript-eslint/utils", - "@typescript-eslint/rule-tester", "@types/react", "autoprefixer", "bootstrap", From 1d004950785d9b003fd475a7e3e7f2c0f1e22640 Mon Sep 17 00:00:00 2001 From: Justin Baur <19896123+justindbaur@users.noreply.github.com> Date: Thu, 1 May 2025 09:32:10 -0400 Subject: [PATCH 029/116] [PM-20032] Give option to skip token refresh on `fullSync` (#14423) * Give option to skip token refresh on fullSync * Fix listener --- .../sync/foreground-sync.service.spec.ts | 78 ++++++- .../platform/sync/foreground-sync.service.ts | 20 +- .../sync/sync-service.listener.spec.ts | 22 +- .../platform/sync/sync-service.listener.ts | 9 +- .../src/platform/sync/core-sync.service.ts | 3 + .../sync/default-sync.service.spec.ts | 199 ++++++++++++++++++ .../src/platform/sync/default-sync.service.ts | 15 +- libs/common/src/platform/sync/sync.service.ts | 29 ++- 8 files changed, 355 insertions(+), 20 deletions(-) create mode 100644 libs/common/src/platform/sync/default-sync.service.spec.ts diff --git a/apps/browser/src/platform/sync/foreground-sync.service.spec.ts b/apps/browser/src/platform/sync/foreground-sync.service.spec.ts index f5daff93815..34ee4fa0f77 100644 --- a/apps/browser/src/platform/sync/foreground-sync.service.spec.ts +++ b/apps/browser/src/platform/sync/foreground-sync.service.spec.ts @@ -8,6 +8,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { MessageListener, MessageSender } from "@bitwarden/common/platform/messaging"; import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { SyncOptions } from "@bitwarden/common/platform/sync/sync.service"; import { FakeStateProvider, mockAccountServiceWith } from "@bitwarden/common/spec"; import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; import { InternalSendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; @@ -80,7 +81,72 @@ describe("ForegroundSyncService", () => { const fullSyncPromise = sut.fullSync(true, false); expect(sut.syncInProgress).toBe(true); - const requestId = getAndAssertRequestId({ forceSync: true, allowThrowOnError: false }); + const requestId = getAndAssertRequestId({ + forceSync: true, + options: { allowThrowOnError: false, skipTokenRefresh: false }, + }); + + // Pretend the sync has finished + messages.next({ successfully: true, errorMessage: null, requestId: requestId }); + + const result = await fullSyncPromise; + + expect(sut.syncInProgress).toBe(false); + expect(result).toBe(true); + }); + + const testData: { + input: boolean | SyncOptions | undefined; + normalized: Required; + }[] = [ + { + input: undefined, + normalized: { allowThrowOnError: false, skipTokenRefresh: false }, + }, + { + input: true, + normalized: { allowThrowOnError: true, skipTokenRefresh: false }, + }, + { + input: false, + normalized: { allowThrowOnError: false, skipTokenRefresh: false }, + }, + { + input: { allowThrowOnError: false }, + normalized: { allowThrowOnError: false, skipTokenRefresh: false }, + }, + { + input: { allowThrowOnError: true }, + normalized: { allowThrowOnError: true, skipTokenRefresh: false }, + }, + { + input: { allowThrowOnError: false, skipTokenRefresh: false }, + normalized: { allowThrowOnError: false, skipTokenRefresh: false }, + }, + { + input: { allowThrowOnError: true, skipTokenRefresh: false }, + normalized: { allowThrowOnError: true, skipTokenRefresh: false }, + }, + { + input: { allowThrowOnError: true, skipTokenRefresh: true }, + normalized: { allowThrowOnError: true, skipTokenRefresh: true }, + }, + { + input: { allowThrowOnError: false, skipTokenRefresh: true }, + normalized: { allowThrowOnError: false, skipTokenRefresh: true }, + }, + ]; + + it.each(testData)("normalize input $input options correctly", async ({ input, normalized }) => { + const messages = new Subject(); + messageListener.messages$.mockReturnValue(messages); + const fullSyncPromise = sut.fullSync(true, input); + expect(sut.syncInProgress).toBe(true); + + const requestId = getAndAssertRequestId({ + forceSync: true, + options: normalized, + }); // Pretend the sync has finished messages.next({ successfully: true, errorMessage: null, requestId: requestId }); @@ -97,7 +163,10 @@ describe("ForegroundSyncService", () => { const fullSyncPromise = sut.fullSync(false, false); expect(sut.syncInProgress).toBe(true); - const requestId = getAndAssertRequestId({ forceSync: false, allowThrowOnError: false }); + const requestId = getAndAssertRequestId({ + forceSync: false, + options: { allowThrowOnError: false, skipTokenRefresh: false }, + }); // Pretend the sync has finished messages.next({ @@ -118,7 +187,10 @@ describe("ForegroundSyncService", () => { const fullSyncPromise = sut.fullSync(true, true); expect(sut.syncInProgress).toBe(true); - const requestId = getAndAssertRequestId({ forceSync: true, allowThrowOnError: true }); + const requestId = getAndAssertRequestId({ + forceSync: true, + options: { allowThrowOnError: true, skipTokenRefresh: false }, + }); // Pretend the sync has finished messages.next({ diff --git a/apps/browser/src/platform/sync/foreground-sync.service.ts b/apps/browser/src/platform/sync/foreground-sync.service.ts index a6ed7281851..ce776f53685 100644 --- a/apps/browser/src/platform/sync/foreground-sync.service.ts +++ b/apps/browser/src/platform/sync/foreground-sync.service.ts @@ -14,6 +14,7 @@ import { import { Utils } from "@bitwarden/common/platform/misc/utils"; import { StateProvider } from "@bitwarden/common/platform/state"; import { CoreSyncService } from "@bitwarden/common/platform/sync/internal"; +import { SyncOptions } from "@bitwarden/common/platform/sync/sync.service"; import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; import { InternalSendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; @@ -22,7 +23,7 @@ import { InternalFolderService } from "@bitwarden/common/vault/abstractions/fold import { FULL_SYNC_FINISHED } from "./sync-service.listener"; -export type FullSyncMessage = { forceSync: boolean; allowThrowOnError: boolean; requestId: string }; +export type FullSyncMessage = { forceSync: boolean; options: SyncOptions; requestId: string }; export const DO_FULL_SYNC = new CommandDefinition("doFullSync"); @@ -60,9 +61,20 @@ export class ForegroundSyncService extends CoreSyncService { ); } - async fullSync(forceSync: boolean, allowThrowOnError: boolean = false): Promise { + async fullSync( + forceSync: boolean, + allowThrowOnErrorOrOptions?: boolean | SyncOptions, + ): Promise { this.syncInProgress = true; try { + // Normalize options + const options = + typeof allowThrowOnErrorOrOptions === "boolean" + ? { allowThrowOnError: allowThrowOnErrorOrOptions, skipTokenRefresh: false } + : { + allowThrowOnError: allowThrowOnErrorOrOptions?.allowThrowOnError ?? false, + skipTokenRefresh: allowThrowOnErrorOrOptions?.skipTokenRefresh ?? false, + }; const requestId = Utils.newGuid(); const syncCompletedPromise = firstValueFrom( this.messageListener.messages$(FULL_SYNC_FINISHED).pipe( @@ -79,10 +91,10 @@ export class ForegroundSyncService extends CoreSyncService { }), ), ); - this.messageSender.send(DO_FULL_SYNC, { forceSync, allowThrowOnError, requestId }); + this.messageSender.send(DO_FULL_SYNC, { forceSync, options, requestId }); const result = await syncCompletedPromise; - if (allowThrowOnError && result.errorMessage != null) { + if (options.allowThrowOnError && result.errorMessage != null) { throw new Error(result.errorMessage); } diff --git a/apps/browser/src/platform/sync/sync-service.listener.spec.ts b/apps/browser/src/platform/sync/sync-service.listener.spec.ts index 51f97e9f879..9682e2cdb57 100644 --- a/apps/browser/src/platform/sync/sync-service.listener.spec.ts +++ b/apps/browser/src/platform/sync/sync-service.listener.spec.ts @@ -27,11 +27,18 @@ describe("SyncServiceListener", () => { const emissionPromise = firstValueFrom(listener); syncService.fullSync.mockResolvedValueOnce(value); - messages.next({ forceSync: true, allowThrowOnError: false, requestId: "1" }); + messages.next({ + forceSync: true, + options: { allowThrowOnError: false, skipTokenRefresh: false }, + requestId: "1", + }); await emissionPromise; - expect(syncService.fullSync).toHaveBeenCalledWith(true, false); + expect(syncService.fullSync).toHaveBeenCalledWith(true, { + allowThrowOnError: false, + skipTokenRefresh: false, + }); expect(messageSender.send).toHaveBeenCalledWith(FULL_SYNC_FINISHED, { successfully: value, errorMessage: null, @@ -45,11 +52,18 @@ describe("SyncServiceListener", () => { const emissionPromise = firstValueFrom(listener); syncService.fullSync.mockRejectedValueOnce(new Error("SyncError")); - messages.next({ forceSync: true, allowThrowOnError: false, requestId: "1" }); + messages.next({ + forceSync: true, + options: { allowThrowOnError: false, skipTokenRefresh: false }, + requestId: "1", + }); await emissionPromise; - expect(syncService.fullSync).toHaveBeenCalledWith(true, false); + expect(syncService.fullSync).toHaveBeenCalledWith(true, { + allowThrowOnError: false, + skipTokenRefresh: false, + }); expect(messageSender.send).toHaveBeenCalledWith(FULL_SYNC_FINISHED, { successfully: false, errorMessage: "SyncError", diff --git a/apps/browser/src/platform/sync/sync-service.listener.ts b/apps/browser/src/platform/sync/sync-service.listener.ts index b7171528648..4274eafcf6a 100644 --- a/apps/browser/src/platform/sync/sync-service.listener.ts +++ b/apps/browser/src/platform/sync/sync-service.listener.ts @@ -9,6 +9,7 @@ import { MessageSender, isExternalMessage, } from "@bitwarden/common/platform/messaging"; +import { SyncOptions } from "@bitwarden/common/platform/sync/sync.service"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; import { DO_FULL_SYNC } from "./foreground-sync.service"; @@ -34,15 +35,15 @@ export class SyncServiceListener { listener$(): Observable { return this.messageListener.messages$(DO_FULL_SYNC).pipe( filter((message) => isExternalMessage(message)), - concatMap(async ({ forceSync, allowThrowOnError, requestId }) => { - await this.doFullSync(forceSync, allowThrowOnError, requestId); + concatMap(async ({ forceSync, options, requestId }) => { + await this.doFullSync(forceSync, options, requestId); }), ); } - private async doFullSync(forceSync: boolean, allowThrowOnError: boolean, requestId: string) { + private async doFullSync(forceSync: boolean, options: SyncOptions, requestId: string) { try { - const result = await this.syncService.fullSync(forceSync, allowThrowOnError); + const result = await this.syncService.fullSync(forceSync, options); this.messageSender.send(FULL_SYNC_FINISHED, { successfully: result, errorMessage: null, diff --git a/libs/common/src/platform/sync/core-sync.service.ts b/libs/common/src/platform/sync/core-sync.service.ts index 1865ffb852f..4020c75f764 100644 --- a/libs/common/src/platform/sync/core-sync.service.ts +++ b/libs/common/src/platform/sync/core-sync.service.ts @@ -28,6 +28,8 @@ import { StateService } from "../abstractions/state.service"; import { MessageSender } from "../messaging"; import { StateProvider, SYNC_DISK, UserKeyDefinition } from "../state"; +import { SyncOptions } from "./sync.service"; + const LAST_SYNC_DATE = new UserKeyDefinition(SYNC_DISK, "lastSync", { deserializer: (d) => (d != null ? new Date(d) : null), clearOn: ["logout"], @@ -55,6 +57,7 @@ export abstract class CoreSyncService implements SyncService { protected readonly stateProvider: StateProvider, ) {} + abstract fullSync(forceSync: boolean, syncOptions?: SyncOptions): Promise; abstract fullSync(forceSync: boolean, allowThrowOnError?: boolean): Promise; async getLastSync(): Promise { diff --git a/libs/common/src/platform/sync/default-sync.service.spec.ts b/libs/common/src/platform/sync/default-sync.service.spec.ts new file mode 100644 index 00000000000..ded06c8be6b --- /dev/null +++ b/libs/common/src/platform/sync/default-sync.service.spec.ts @@ -0,0 +1,199 @@ +import { mock, MockProxy } from "jest-mock-extended"; +import { of } from "rxjs"; + +import { CollectionService } from "@bitwarden/admin-console/common"; +import { + LogoutReason, + UserDecryptionOptions, + UserDecryptionOptionsServiceAbstraction, +} from "@bitwarden/auth/common"; +import { KeyService } from "@bitwarden/key-management"; + +import { Matrix } from "../../../spec/matrix"; +import { ApiService } from "../../abstractions/api.service"; +import { InternalOrganizationServiceAbstraction } from "../../admin-console/abstractions/organization/organization.service.abstraction"; +import { InternalPolicyService } from "../../admin-console/abstractions/policy/policy.service.abstraction"; +import { ProviderService } from "../../admin-console/abstractions/provider.service"; +import { Account, AccountService } from "../../auth/abstractions/account.service"; +import { AuthService } from "../../auth/abstractions/auth.service"; +import { AvatarService } from "../../auth/abstractions/avatar.service"; +import { TokenService } from "../../auth/abstractions/token.service"; +import { AuthenticationStatus } from "../../auth/enums/authentication-status"; +import { DomainSettingsService } from "../../autofill/services/domain-settings.service"; +import { BillingAccountProfileStateService } from "../../billing/abstractions"; +import { KeyConnectorService } from "../../key-management/key-connector/abstractions/key-connector.service"; +import { InternalMasterPasswordServiceAbstraction } from "../../key-management/master-password/abstractions/master-password.service.abstraction"; +import { SendApiService } from "../../tools/send/services/send-api.service.abstraction"; +import { InternalSendService } from "../../tools/send/services/send.service.abstraction"; +import { UserId } from "../../types/guid"; +import { CipherService } from "../../vault/abstractions/cipher.service"; +import { FolderApiServiceAbstraction } from "../../vault/abstractions/folder/folder-api.service.abstraction"; +import { InternalFolderService } from "../../vault/abstractions/folder/folder.service.abstraction"; +import { LogService } from "../abstractions/log.service"; +import { StateService } from "../abstractions/state.service"; +import { MessageSender } from "../messaging"; +import { StateProvider } from "../state"; + +import { DefaultSyncService } from "./default-sync.service"; +import { SyncResponse } from "./sync.response"; + +describe("DefaultSyncService", () => { + let masterPasswordAbstraction: MockProxy; + let accountService: MockProxy; + let apiService: MockProxy; + let domainSettingsService: MockProxy; + let folderService: MockProxy; + let cipherService: MockProxy; + let keyService: MockProxy; + let collectionService: MockProxy; + let messageSender: MockProxy; + let policyService: MockProxy; + let sendService: MockProxy; + let logService: MockProxy; + let keyConnectorService: MockProxy; + let stateService: MockProxy; + let providerService: MockProxy; + let folderApiService: MockProxy; + let organizationService: MockProxy; + let sendApiService: MockProxy; + let userDecryptionOptionsService: MockProxy; + let avatarService: MockProxy; + let logoutCallback: jest.Mock, [logoutReason: LogoutReason, userId?: UserId]>; + let billingAccountProfileStateService: MockProxy; + let tokenService: MockProxy; + let authService: MockProxy; + let stateProvider: MockProxy; + + let sut: DefaultSyncService; + + beforeEach(() => { + masterPasswordAbstraction = mock(); + accountService = mock(); + apiService = mock(); + domainSettingsService = mock(); + folderService = mock(); + cipherService = mock(); + keyService = mock(); + collectionService = mock(); + messageSender = mock(); + policyService = mock(); + sendService = mock(); + logService = mock(); + keyConnectorService = mock(); + stateService = mock(); + providerService = mock(); + folderApiService = mock(); + organizationService = mock(); + sendApiService = mock(); + userDecryptionOptionsService = mock(); + avatarService = mock(); + logoutCallback = jest.fn(); + billingAccountProfileStateService = mock(); + tokenService = mock(); + authService = mock(); + stateProvider = mock(); + + sut = new DefaultSyncService( + masterPasswordAbstraction, + accountService, + apiService, + domainSettingsService, + folderService, + cipherService, + keyService, + collectionService, + messageSender, + policyService, + sendService, + logService, + keyConnectorService, + stateService, + providerService, + folderApiService, + organizationService, + sendApiService, + userDecryptionOptionsService, + avatarService, + logoutCallback, + billingAccountProfileStateService, + tokenService, + authService, + stateProvider, + ); + }); + + const user1 = "user1" as UserId; + + describe("fullSync", () => { + beforeEach(() => { + accountService.activeAccount$ = of({ id: user1 } as Account); + Matrix.autoMockMethod(authService.authStatusFor$, () => of(AuthenticationStatus.Unlocked)); + apiService.getSync.mockResolvedValue( + new SyncResponse({ + profile: { + id: user1, + }, + folders: [], + collections: [], + ciphers: [], + sends: [], + domains: [], + policies: [], + }), + ); + Matrix.autoMockMethod(userDecryptionOptionsService.userDecryptionOptionsById$, () => + of({ hasMasterPassword: true } satisfies UserDecryptionOptions), + ); + stateProvider.getUser.mockReturnValue(mock()); + }); + + it("does a token refresh when option missing from options", async () => { + await sut.fullSync(true, { allowThrowOnError: false }); + + expect(apiService.refreshIdentityToken).toHaveBeenCalledTimes(1); + expect(apiService.getSync).toHaveBeenCalledTimes(1); + }); + + it("does a token refresh when boolean passed in", async () => { + await sut.fullSync(true, false); + + expect(apiService.refreshIdentityToken).toHaveBeenCalledTimes(1); + expect(apiService.getSync).toHaveBeenCalledTimes(1); + }); + + it("does a token refresh when skipTokenRefresh option passed in with false and allowThrowOnError also passed in", async () => { + await sut.fullSync(true, { allowThrowOnError: false, skipTokenRefresh: false }); + + expect(apiService.refreshIdentityToken).toHaveBeenCalledTimes(1); + expect(apiService.getSync).toHaveBeenCalledTimes(1); + }); + + it("does a token refresh when skipTokenRefresh option passed in with false by itself", async () => { + await sut.fullSync(true, { skipTokenRefresh: false }); + + expect(apiService.refreshIdentityToken).toHaveBeenCalledTimes(1); + expect(apiService.getSync).toHaveBeenCalledTimes(1); + }); + + it("does not do a token refresh when skipTokenRefresh passed in as true", async () => { + await sut.fullSync(true, { skipTokenRefresh: true }); + + expect(apiService.refreshIdentityToken).not.toHaveBeenCalled(); + expect(apiService.getSync).toHaveBeenCalledTimes(1); + }); + + it("does not do a token refresh when skipTokenRefresh passed in as true and allowThrowOnError also passed in", async () => { + await sut.fullSync(true, { allowThrowOnError: false, skipTokenRefresh: true }); + + expect(apiService.refreshIdentityToken).not.toHaveBeenCalled(); + expect(apiService.getSync).toHaveBeenCalledTimes(1); + }); + + it("does a token refresh when nothing passed in", async () => { + await sut.fullSync(true); + + expect(apiService.refreshIdentityToken).toHaveBeenCalledTimes(1); + expect(apiService.getSync).toHaveBeenCalledTimes(1); + }); + }); +}); diff --git a/libs/common/src/platform/sync/default-sync.service.ts b/libs/common/src/platform/sync/default-sync.service.ts index a6b1b974645..faf54f11912 100644 --- a/libs/common/src/platform/sync/default-sync.service.ts +++ b/libs/common/src/platform/sync/default-sync.service.ts @@ -54,6 +54,7 @@ import { MessageSender } from "../messaging"; import { StateProvider } from "../state"; import { CoreSyncService } from "./core-sync.service"; +import { SyncOptions } from "./sync.service"; export class DefaultSyncService extends CoreSyncService { syncInProgress = false; @@ -102,7 +103,15 @@ export class DefaultSyncService extends CoreSyncService { ); } - override async fullSync(forceSync: boolean, allowThrowOnError = false): Promise { + override async fullSync( + forceSync: boolean, + allowThrowOnErrorOrOptions?: boolean | SyncOptions, + ): Promise { + const { allowThrowOnError = false, skipTokenRefresh = false } = + typeof allowThrowOnErrorOrOptions === "boolean" + ? { allowThrowOnError: allowThrowOnErrorOrOptions } + : (allowThrowOnErrorOrOptions ?? {}); + const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.id))); this.syncStarted(); const authStatus = await firstValueFrom(this.authService.authStatusFor$(userId)); @@ -127,7 +136,9 @@ export class DefaultSyncService extends CoreSyncService { } try { - await this.apiService.refreshIdentityToken(); + if (!skipTokenRefresh) { + await this.apiService.refreshIdentityToken(); + } const response = await this.apiService.getSync(); await this.syncProfile(response.profile); diff --git a/libs/common/src/platform/sync/sync.service.ts b/libs/common/src/platform/sync/sync.service.ts index 967e4db27a5..6ef62fc9cb8 100644 --- a/libs/common/src/platform/sync/sync.service.ts +++ b/libs/common/src/platform/sync/sync.service.ts @@ -7,6 +7,26 @@ import { } from "../../models/response/notification.response"; import { UserId } from "../../types/guid"; +/** + * A set of options for configuring how a {@link SyncService.fullSync} call should behave. + */ +export type SyncOptions = { + /** + * A boolean dictating whether or not caught errors should be rethrown. + * `true` if they can be rethrown, `false` if they should not be rethrown. + * @default false + */ + allowThrowOnError?: boolean; + /** + * A boolean dictating whether or not to do a token refresh before doing the sync. + * `true` if the refresh can be skipped, likely because one was done soon before the call to + * `fullSync`. `false` if the token refresh should be done before getting data. + * + * @default false + */ + skipTokenRefresh?: boolean; +}; + /** * A class encapsulating sync operations and data. */ @@ -47,9 +67,12 @@ export abstract class SyncService { * as long as the current user is authenticated. If `false` it will only sync if either a sync * has not happened before or the last sync date for the active user is before their account * revision date. Try to always use `false` if possible. - * - * @param allowThrowOnError A boolean dictating whether or not caught errors should be rethrown. - * `true` if they can be rethrown, `false` if they should not be rethrown. + * @param syncOptions Options for customizing how the sync call should behave. + */ + abstract fullSync(forceSync: boolean, syncOptions?: SyncOptions): Promise; + + /** + * @deprecated Use the overload taking {@link SyncOptions} instead. */ abstract fullSync(forceSync: boolean, allowThrowOnError?: boolean): Promise; From 8090586b52006078250397916e67b9ad32526181 Mon Sep 17 00:00:00 2001 From: Matt Bishop Date: Thu, 1 May 2025 07:18:09 -0700 Subject: [PATCH 030/116] Fix some references to master (#14578) * Fix some references to master * Fix broken links --- LICENSE.txt | 6 +++--- LICENSE_BITWARDEN.txt | 2 +- apps/browser/README.md | 4 ++-- apps/cli/README.md | 2 +- apps/desktop/README.md | 2 +- apps/web/README.md | 6 +++--- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/LICENSE.txt b/LICENSE.txt index 55bf3b3f736..8ad59f788b3 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -5,13 +5,13 @@ specifies another license. Bitwarden Licensed code is found only in the /bitwarden_license directory. GPL v3.0: -https://github.com/bitwarden/web/blob/master/LICENSE_GPL.txt +https://github.com/bitwarden/clients/blob/main/LICENSE_GPL.txt Bitwarden License v1.0: -https://github.com/bitwarden/web/blob/master/LICENSE_BITWARDEN.txt +https://github.com/bitwarden/clients/blob/main/LICENSE_BITWARDEN.txt No grant of any rights in the trademarks, service marks, or logos of Bitwarden is made (except as may be necessary to comply with the notice requirements as applicable), and use of any Bitwarden trademarks must comply with Bitwarden Trademark Guidelines -. +. diff --git a/LICENSE_BITWARDEN.txt b/LICENSE_BITWARDEN.txt index 08e09f28639..938946a09a1 100644 --- a/LICENSE_BITWARDEN.txt +++ b/LICENSE_BITWARDEN.txt @@ -56,7 +56,7 @@ such Open Source Software only. logos of any Contributor (except as may be necessary to comply with the notice requirements in Section 2.3), and use of any Bitwarden trademarks must comply with Bitwarden Trademark Guidelines -. +. 3. TERMINATION diff --git a/apps/browser/README.md b/apps/browser/README.md index c99d0844a09..fdeb1307567 100644 --- a/apps/browser/README.md +++ b/apps/browser/README.md @@ -1,4 +1,4 @@ -[![Github Workflow build browser on master](https://github.com/bitwarden/clients/actions/workflows/build-browser.yml/badge.svg?branch=master)](https://github.com/bitwarden/clients/actions/workflows/build-browser.yml?query=branch:master) +[![Github Workflow build browser on main](https://github.com/bitwarden/clients/actions/workflows/build-browser.yml/badge.svg?branch=main)](https://github.com/bitwarden/clients/actions/workflows/build-browser.yml?query=branch:main) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/bitwarden-browser/localized.svg)](https://crowdin.com/project/bitwarden-browser) [![Join the chat at https://gitter.im/bitwarden/Lobby](https://badges.gitter.im/bitwarden/Lobby.svg)](https://gitter.im/bitwarden/Lobby) @@ -15,7 +15,7 @@ The Bitwarden browser extension is written using the Web Extension API and Angular. -![My Vault](https://raw.githubusercontent.com/bitwarden/brand/master/screenshots/browser-chrome.png) +![My Vault](https://raw.githubusercontent.com/bitwarden/brand/main/screenshots/web-browser-extension-generator.png) ## Documentation diff --git a/apps/cli/README.md b/apps/cli/README.md index d39c0e39c8f..2b13270cdba 100644 --- a/apps/cli/README.md +++ b/apps/cli/README.md @@ -1,4 +1,4 @@ -[![Github Workflow build on master](https://github.com/bitwarden/clients/actions/workflows/build-cli.yml/badge.svg?branch=master)](https://github.com/bitwarden/clients/actions/workflows/build-cli.yml?query=branch:master) +[![Github Workflow build on main](https://github.com/bitwarden/clients/actions/workflows/build-cli.yml/badge.svg?branch=main)](https://github.com/bitwarden/clients/actions/workflows/build-cli.yml?query=branch:main) [![Join the chat at https://gitter.im/bitwarden/Lobby](https://badges.gitter.im/bitwarden/Lobby.svg)](https://gitter.im/bitwarden/Lobby) # Bitwarden Command-line Interface diff --git a/apps/desktop/README.md b/apps/desktop/README.md index 6578699369b..ee13f451641 100644 --- a/apps/desktop/README.md +++ b/apps/desktop/README.md @@ -1,4 +1,4 @@ -[![Github Workflow build on master](https://github.com/bitwarden/clients/actions/workflows/build-desktop.yml/badge.svg?branch=master)](https://github.com/bitwarden/clients/actions/workflows/build-desktop.yml?query=branch:master) +[![Github Workflow build on main](https://github.com/bitwarden/clients/actions/workflows/build-desktop.yml/badge.svg?branch=main)](https://github.com/bitwarden/clients/actions/workflows/build-desktop.yml?query=branch:main) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/bitwarden-desktop/localized.svg)](https://crowdin.com/project/bitwarden-desktop) [![Join the chat at https://gitter.im/bitwarden/Lobby](https://badges.gitter.im/bitwarden/Lobby.svg)](https://gitter.im/bitwarden/Lobby) diff --git a/apps/web/README.md b/apps/web/README.md index f43a9dc1614..c5e03eebb59 100644 --- a/apps/web/README.md +++ b/apps/web/README.md @@ -1,12 +1,12 @@

- +

The Bitwarden web project is an Angular application that powers the web vault (https://vault.bitwarden.com/).

- - Github Workflow build on master + + Github Workflow build on main Crowdin From 1b66f0f06b449444a0f81522c1fe8a28d998d88c Mon Sep 17 00:00:00 2001 From: Github Actions Date: Thu, 1 May 2025 14:22:26 +0000 Subject: [PATCH 031/116] Bumped Desktop client to 2025.5.0 --- apps/desktop/package.json | 2 +- apps/desktop/src/package-lock.json | 4 ++-- apps/desktop/src/package.json | 2 +- package-lock.json | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/desktop/package.json b/apps/desktop/package.json index e00df0b26df..21892cd1df8 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -1,7 +1,7 @@ { "name": "@bitwarden/desktop", "description": "A secure and free password manager for all of your devices.", - "version": "2025.4.2", + "version": "2025.5.0", "keywords": [ "bitwarden", "password", diff --git a/apps/desktop/src/package-lock.json b/apps/desktop/src/package-lock.json index f6449bd9626..b3a33dc75e3 100644 --- a/apps/desktop/src/package-lock.json +++ b/apps/desktop/src/package-lock.json @@ -1,12 +1,12 @@ { "name": "@bitwarden/desktop", - "version": "2025.4.2", + "version": "2025.5.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@bitwarden/desktop", - "version": "2025.4.2", + "version": "2025.5.0", "license": "GPL-3.0", "dependencies": { "@bitwarden/desktop-napi": "file:../desktop_native/napi" diff --git a/apps/desktop/src/package.json b/apps/desktop/src/package.json index 45a6f6b90af..c180ed8c744 100644 --- a/apps/desktop/src/package.json +++ b/apps/desktop/src/package.json @@ -2,7 +2,7 @@ "name": "@bitwarden/desktop", "productName": "Bitwarden", "description": "A secure and free password manager for all of your devices.", - "version": "2025.4.2", + "version": "2025.5.0", "author": "Bitwarden Inc. (https://bitwarden.com)", "homepage": "https://bitwarden.com", "license": "GPL-3.0", diff --git a/package-lock.json b/package-lock.json index 671951c0349..25322b844b7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -231,7 +231,7 @@ }, "apps/desktop": { "name": "@bitwarden/desktop", - "version": "2025.4.2", + "version": "2025.5.0", "hasInstallScript": true, "license": "GPL-3.0" }, From a7d04dc21276c5152ca6ab5d6f55159ec356c4c5 Mon Sep 17 00:00:00 2001 From: cyprain-okeke <108260115+cyprain-okeke@users.noreply.github.com> Date: Thu, 1 May 2025 16:36:00 +0100 Subject: [PATCH 032/116] [PM-17775] Allow admin to send f4 e sponsorship (#14390) * Added nav item for f4e in org admin console * shotgun surgery for adding "useAdminSponsoredFamilies" feature from the org table * Resolved issue with members nav item also being selected when f4e is selected * Separated out billing's logic from the org layout component * Removed unused observable * Moved logic to existing f4e policy service and added unit tests * Resolved script typescript error * Resolved goofy switchMap * Add changes for the issue orgs * Added changes for the dialog * Rename the files properly * Remove the commented code * Change the implement to align with design * Add todo comments * Remove the comment todo * Fix the uni test error * Resolve the unit test * Resolve the unit test issue * Resolve the pr comments on any and route * remove the any * remove the generic validator * Resolve the unit test * add validations for email * Add changes for the autoscale * Changes to allow admin to send F4E sponsorship * Fix the lint errors * Resolve the lint errors * Fix the revokeAccount message * Fix the lint runtime error * Resolve the lint issues * Remove unused components * Changes to add isadminInitiated * remove the FIXME comment * Resolve the failing test * Fix the pr comments * Resolve the orgkey and other comments * Resolve the lint error * Resolve the lint error * resolve the spelling error * refactor the getStatus method * Remove the deprecated method * Resolve the unusual type casting * revert the change --------- Co-authored-by: Conner Turnbull Co-authored-by: Conner Turnbull <133619638+cturnbull-bitwarden@users.noreply.github.com> --- .../add-sponsorship-dialog.component.html | 10 +- .../add-sponsorship-dialog.component.ts | 131 +++++---- .../free-bitwarden-families.component.html | 104 ++++++- .../free-bitwarden-families.component.ts | 261 +++++++++++++++--- ...rganization-member-families.component.html | 47 ---- .../organization-member-families.component.ts | 34 --- ...nization-sponsored-families.component.html | 87 ------ ...ganization-sponsored-families.component.ts | 39 --- .../sponsored-families.component.html | 2 +- .../sponsoring-org-row.component.html | 2 +- .../src/app/shared/loose-components.module.ts | 6 - apps/web/src/locales/en/messages.json | 21 +- .../src/services/jslib-services.module.ts | 7 + ...organization-sponsorship-create.request.ts | 1 + ...ion-sponsorship-api.service.abstraction.ts | 8 + ...ganization-sponsorship-invites.response.ts | 31 +++ .../organization-sponsorship-api.service.ts | 22 ++ 17 files changed, 491 insertions(+), 322 deletions(-) delete mode 100644 apps/web/src/app/billing/members/organization-member-families.component.html delete mode 100644 apps/web/src/app/billing/members/organization-member-families.component.ts delete mode 100644 apps/web/src/app/billing/members/organization-sponsored-families.component.html delete mode 100644 apps/web/src/app/billing/members/organization-sponsored-families.component.ts create mode 100644 libs/common/src/billing/abstractions/organizations/organization-sponsorship-api.service.abstraction.ts create mode 100644 libs/common/src/billing/models/response/organization-sponsorship-invites.response.ts create mode 100644 libs/common/src/billing/services/organization/organization-sponsorship-api.service.ts diff --git a/apps/web/src/app/billing/members/add-sponsorship-dialog.component.html b/apps/web/src/app/billing/members/add-sponsorship-dialog.component.html index 2dbcc577e54..405211d6ecb 100644 --- a/apps/web/src/app/billing/members/add-sponsorship-dialog.component.html +++ b/apps/web/src/app/billing/members/add-sponsorship-dialog.component.html @@ -34,7 +34,15 @@

- - - - - + + +

+ {{ "sponsorshipFreeBitwardenFamilies" | i18n }} +

+
+ {{ "sponsoredFamiliesInclude" | i18n }}: +
    +
  • {{ "sponsoredFamiliesPremiumAccess" | i18n }}
  • +
  • {{ "sponsoredFamiliesSharedCollectionsMessage" | i18n }}
  • +
+
- - - -
+

{{ "sponsoredBitwardenFamilies" | i18n }}

-

{{ "sponsoredFamiliesRemoveActiveSponsorship" | i18n }}

+ @if (loading()) { + + + {{ "loading" | i18n }} + + } + + @if (!loading() && sponsoredFamilies?.length > 0) { + + + + + {{ "recipient" | i18n }} + {{ "status" | i18n }} + {{ "notes" | i18n }} + + + + + @for (o of sponsoredFamilies; let i = $index; track i) { + + + {{ o.friendlyName }} + {{ o.statusMessage }} + {{ o.notes }} + + + + + +
+ + +
+ + +
+ } +
+
+
+
+ } @else if (!loading()) { +
+ Search +

{{ "noSponsoredFamiliesMessage" | i18n }}

+

{{ "nosponsoredFamiliesDetails" | i18n }}

+
+ } + + @if (!loading() && sponsoredFamilies.length > 0) { +

{{ "sponsoredFamiliesRemoveActiveSponsorship" | i18n }}

+ } +
+ diff --git a/apps/web/src/app/billing/members/free-bitwarden-families.component.ts b/apps/web/src/app/billing/members/free-bitwarden-families.component.ts index af43e5a4bc1..c141eaebd78 100644 --- a/apps/web/src/app/billing/members/free-bitwarden-families.component.ts +++ b/apps/web/src/app/billing/members/free-bitwarden-families.component.ts @@ -1,62 +1,259 @@ import { DialogRef } from "@angular/cdk/dialog"; -import { Component, OnInit } from "@angular/core"; -import { Router } from "@angular/router"; -import { firstValueFrom } from "rxjs"; +import { formatDate } from "@angular/common"; +import { Component, OnInit, signal } from "@angular/core"; +import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; +import { ActivatedRoute } from "@angular/router"; +import { firstValueFrom, map, Observable, switchMap } from "rxjs"; -import { DialogService } from "@bitwarden/components"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { OrganizationSponsorshipApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/organizations/organization-sponsorship-api.service.abstraction"; +import { OrganizationSponsorshipInvitesResponse } from "@bitwarden/common/billing/models/response/organization-sponsorship-invites.response"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; +import { StateProvider } from "@bitwarden/common/platform/state"; +import { OrganizationId, UserId } from "@bitwarden/common/types/guid"; +import { OrgKey } from "@bitwarden/common/types/key"; +import { DialogService, ToastService } from "@bitwarden/components"; +import { KeyService } from "@bitwarden/key-management"; -import { FreeFamiliesPolicyService } from "../services/free-families-policy.service"; - -import { - AddSponsorshipDialogComponent, - AddSponsorshipDialogResult, -} from "./add-sponsorship-dialog.component"; -import { SponsoredFamily } from "./types/sponsored-family"; +import { AddSponsorshipDialogComponent } from "./add-sponsorship-dialog.component"; @Component({ selector: "app-free-bitwarden-families", templateUrl: "free-bitwarden-families.component.html", }) export class FreeBitwardenFamiliesComponent implements OnInit { + loading = signal(true); tabIndex = 0; - sponsoredFamilies: SponsoredFamily[] = []; + sponsoredFamilies: OrganizationSponsorshipInvitesResponse[] = []; + + organizationId = ""; + organizationKey$: Observable; + + private locale: string = ""; constructor( - private router: Router, + private route: ActivatedRoute, private dialogService: DialogService, - private freeFamiliesPolicyService: FreeFamiliesPolicyService, - ) {} + private apiService: ApiService, + private encryptService: EncryptService, + private keyService: KeyService, + private platformUtilsService: PlatformUtilsService, + private i18nService: I18nService, + private logService: LogService, + private toastService: ToastService, + private organizationSponsorshipApiService: OrganizationSponsorshipApiServiceAbstraction, + private stateProvider: StateProvider, + ) { + this.organizationId = this.route.snapshot.params.organizationId || ""; + this.organizationKey$ = this.stateProvider.activeUserId$.pipe( + switchMap( + (userId) => + this.keyService.orgKeys$(userId as UserId) as Observable>, + ), + map((organizationKeysById) => organizationKeysById[this.organizationId as OrganizationId]), + takeUntilDestroyed(), + ); + } async ngOnInit() { - await this.preventAccessToFreeFamiliesPage(); + this.locale = await firstValueFrom(this.i18nService.locale$); + await this.loadSponsorships(); + + this.loading.set(false); + } + + async loadSponsorships() { + if (!this.organizationId) { + return; + } + + const [response, orgKey] = await Promise.all([ + this.organizationSponsorshipApiService.getOrganizationSponsorship(this.organizationId), + firstValueFrom(this.organizationKey$), + ]); + + if (!orgKey) { + this.logService.error("Organization key not found"); + return; + } + + const organizationFamilies = response.data; + + this.sponsoredFamilies = await Promise.all( + organizationFamilies.map(async (family) => { + let decryptedNote = ""; + try { + decryptedNote = await this.encryptService.decryptString( + new EncString(family.notes), + orgKey, + ); + } catch (e) { + this.logService.error(e); + } + + const { statusMessage, statusClass } = this.getStatus( + this.isSelfHosted, + family.toDelete, + family.validUntil, + family.lastSyncDate, + this.locale, + ); + + const newFamily = { + ...family, + notes: decryptedNote, + statusMessage: statusMessage || "", + statusClass: statusClass || "tw-text-success", + status: statusMessage || "", + }; + + return new OrganizationSponsorshipInvitesResponse(newFamily); + }), + ); } async addSponsorship() { - const addSponsorshipDialogRef: DialogRef = - AddSponsorshipDialogComponent.open(this.dialogService); + const addSponsorshipDialogRef: DialogRef = AddSponsorshipDialogComponent.open( + this.dialogService, + { + data: { + organizationId: this.organizationId, + organizationKey: await firstValueFrom(this.organizationKey$), + }, + }, + ); - const dialogRef = await firstValueFrom(addSponsorshipDialogRef.closed); + await firstValueFrom(addSponsorshipDialogRef.closed); - if (dialogRef?.value) { - this.sponsoredFamilies = [dialogRef.value, ...this.sponsoredFamilies]; + await this.loadSponsorships(); + } + + async removeSponsorship(sponsorship: OrganizationSponsorshipInvitesResponse) { + try { + await this.doRevokeSponsorship(sponsorship); + } catch (e) { + this.logService.error(e); } } - removeSponsorhip(sponsorship: any) { - const index = this.sponsoredFamilies.findIndex( - (e) => e.sponsorshipEmail == sponsorship.sponsorshipEmail, - ); - this.sponsoredFamilies.splice(index, 1); + get isSelfHosted(): boolean { + return this.platformUtilsService.isSelfHost(); } - private async preventAccessToFreeFamiliesPage() { - const showFreeFamiliesPage = await firstValueFrom( - this.freeFamiliesPolicyService.showFreeFamilies$, - ); + async resendEmail(sponsorship: OrganizationSponsorshipInvitesResponse) { + await this.apiService.postResendSponsorshipOffer(sponsorship.sponsoringOrganizationUserId); + this.toastService.showToast({ + variant: "success", + title: undefined, + message: this.i18nService.t("emailSent"), + }); + } - if (!showFreeFamiliesPage) { - await this.router.navigate(["/"]); + private async doRevokeSponsorship(sponsorship: OrganizationSponsorshipInvitesResponse) { + const content = sponsorship.validUntil + ? this.i18nService.t( + "updatedRevokeSponsorshipConfirmationForAcceptedSponsorship", + sponsorship.friendlyName, + formatDate(sponsorship.validUntil, "MM/dd/yyyy", this.locale), + ) + : this.i18nService.t( + "updatedRevokeSponsorshipConfirmationForSentSponsorship", + sponsorship.friendlyName, + ); + + const confirmed = await this.dialogService.openSimpleDialog({ + title: `${this.i18nService.t("removeSponsorship")}?`, + content, + acceptButtonText: { key: "remove" }, + type: "warning", + }); + + if (!confirmed) { return; } + + await this.apiService.deleteRevokeSponsorship(sponsorship.sponsoringOrganizationUserId); + + this.toastService.showToast({ + variant: "success", + title: undefined, + message: this.i18nService.t("reclaimedFreePlan"), + }); + + await this.loadSponsorships(); + } + + private getStatus( + selfHosted: boolean, + toDelete?: boolean, + validUntil?: Date, + lastSyncDate?: Date, + locale: string = "", + ): { statusMessage: string; statusClass: "tw-text-success" | "tw-text-danger" } { + /* + * Possible Statuses: + * Requested (self-hosted only) + * Sent + * Active + * RequestRevoke + * RevokeWhenExpired + */ + + if (toDelete && validUntil) { + // They want to delete but there is a valid until date which means there is an active sponsorship + return { + statusMessage: this.i18nService.t( + "revokeWhenExpired", + formatDate(validUntil, "MM/dd/yyyy", locale), + ), + statusClass: "tw-text-danger", + }; + } + + if (toDelete) { + // They want to delete and we don't have a valid until date so we can + // this should only happen on a self-hosted install + return { + statusMessage: this.i18nService.t("requestRemoved"), + statusClass: "tw-text-danger", + }; + } + + if (validUntil) { + // They don't want to delete and they have a valid until date + // that means they are actively sponsoring someone + return { + statusMessage: this.i18nService.t("active"), + statusClass: "tw-text-success", + }; + } + + if (selfHosted && lastSyncDate) { + // We are on a self-hosted install and it has been synced but we have not gotten + // a valid until date so we can't know if they are actively sponsoring someone + return { + statusMessage: this.i18nService.t("sent"), + statusClass: "tw-text-success", + }; + } + + if (!selfHosted) { + // We are in cloud and all other status checks have been false therefore we have + // sent the request but it hasn't been accepted yet + return { + statusMessage: this.i18nService.t("sent"), + statusClass: "tw-text-success", + }; + } + + // We are on a self-hosted install and we have not synced yet + return { + statusMessage: this.i18nService.t("requested"), + statusClass: "tw-text-success", + }; } } diff --git a/apps/web/src/app/billing/members/organization-member-families.component.html b/apps/web/src/app/billing/members/organization-member-families.component.html deleted file mode 100644 index c5b7283d9d9..00000000000 --- a/apps/web/src/app/billing/members/organization-member-families.component.html +++ /dev/null @@ -1,47 +0,0 @@ - - -

- {{ "membersWithSponsoredFamilies" | i18n }} -

- -

{{ "memberFamilies" | i18n }}

- - @if (loading) { - - - {{ "loading" | i18n }} - - } - - @if (!loading && memberFamilies?.length > 0) { - - - - - {{ "member" | i18n }} - {{ "status" | i18n }} - - - - - @for (o of memberFamilies; let i = $index; track i) { - - - {{ o.sponsorshipEmail }} - {{ o.status }} - - - } - - -
-
- } @else { -
- Search -

{{ "noMemberFamilies" | i18n }}

-

{{ "noMemberFamiliesDescription" | i18n }}

-
- } -
-
diff --git a/apps/web/src/app/billing/members/organization-member-families.component.ts b/apps/web/src/app/billing/members/organization-member-families.component.ts deleted file mode 100644 index 52c95646a11..00000000000 --- a/apps/web/src/app/billing/members/organization-member-families.component.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Component, Input, OnDestroy, OnInit } from "@angular/core"; -import { Subject } from "rxjs"; - -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; - -import { SponsoredFamily } from "./types/sponsored-family"; - -@Component({ - selector: "app-organization-member-families", - templateUrl: "organization-member-families.component.html", -}) -export class OrganizationMemberFamiliesComponent implements OnInit, OnDestroy { - tabIndex = 0; - loading = false; - - @Input() memberFamilies: SponsoredFamily[] = []; - - private _destroy = new Subject(); - - constructor(private platformUtilsService: PlatformUtilsService) {} - - async ngOnInit() { - this.loading = false; - } - - ngOnDestroy(): void { - this._destroy.next(); - this._destroy.complete(); - } - - get isSelfHosted(): boolean { - return this.platformUtilsService.isSelfHost(); - } -} diff --git a/apps/web/src/app/billing/members/organization-sponsored-families.component.html b/apps/web/src/app/billing/members/organization-sponsored-families.component.html deleted file mode 100644 index 7db96deb4ab..00000000000 --- a/apps/web/src/app/billing/members/organization-sponsored-families.component.html +++ /dev/null @@ -1,87 +0,0 @@ - - -

- {{ "sponsorFreeBitwardenFamilies" | i18n }} -

-
- {{ "sponsoredFamiliesInclude" | i18n }}: -
    -
  • {{ "sponsoredFamiliesPremiumAccess" | i18n }}
  • -
  • {{ "sponsoredFamiliesSharedCollections" | i18n }}
  • -
-
- -

{{ "sponsoredBitwardenFamilies" | i18n }}

- - @if (loading) { - - - {{ "loading" | i18n }} - - } - - @if (!loading && sponsoredFamilies?.length > 0) { - - - - - {{ "recipient" | i18n }} - {{ "status" | i18n }} - {{ "notes" | i18n }} - - - - - @for (o of sponsoredFamilies; let i = $index; track i) { - - - {{ o.sponsorshipEmail }} - {{ o.status }} - {{ o.sponsorshipNote }} - - - - - -
- - -
- - -
- } -
-
-
-
- } @else { -
- Search -

{{ "noSponsoredFamilies" | i18n }}

-

{{ "noSponsoredFamiliesDescription" | i18n }}

-
- } -
-
diff --git a/apps/web/src/app/billing/members/organization-sponsored-families.component.ts b/apps/web/src/app/billing/members/organization-sponsored-families.component.ts deleted file mode 100644 index 7cc46634a38..00000000000 --- a/apps/web/src/app/billing/members/organization-sponsored-families.component.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from "@angular/core"; -import { Subject } from "rxjs"; - -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; - -import { SponsoredFamily } from "./types/sponsored-family"; - -@Component({ - selector: "app-organization-sponsored-families", - templateUrl: "organization-sponsored-families.component.html", -}) -export class OrganizationSponsoredFamiliesComponent implements OnInit, OnDestroy { - loading = false; - tabIndex = 0; - - @Input() sponsoredFamilies: SponsoredFamily[] = []; - @Output() removeSponsorshipEvent = new EventEmitter(); - - private _destroy = new Subject(); - - constructor(private platformUtilsService: PlatformUtilsService) {} - - async ngOnInit() { - this.loading = false; - } - - get isSelfHosted(): boolean { - return this.platformUtilsService.isSelfHost(); - } - - remove(sponsorship: SponsoredFamily) { - this.removeSponsorshipEvent.emit(sponsorship); - } - - ngOnDestroy(): void { - this._destroy.next(); - this._destroy.complete(); - } -} diff --git a/apps/web/src/app/billing/settings/sponsored-families.component.html b/apps/web/src/app/billing/settings/sponsored-families.component.html index 12e942aaf18..8e829ae70ef 100644 --- a/apps/web/src/app/billing/settings/sponsored-families.component.html +++ b/apps/web/src/app/billing/settings/sponsored-families.component.html @@ -13,7 +13,7 @@ {{ "sponsoredFamiliesInclude" | i18n }}:
  • {{ "sponsoredFamiliesPremiumAccess" | i18n }}
  • -
  • {{ "sponsoredFamiliesSharedCollections" | i18n }}
  • +
  • {{ "sponsoredFamiliesSharedCollectionsMessage" | i18n }}
diff --git a/apps/web/src/app/billing/settings/sponsoring-org-row.component.html b/apps/web/src/app/billing/settings/sponsoring-org-row.component.html index eeeaa256049..1e5690cd85a 100644 --- a/apps/web/src/app/billing/settings/sponsoring-org-row.component.html +++ b/apps/web/src/app/billing/settings/sponsoring-org-row.component.html @@ -32,7 +32,7 @@ type="button" bitMenuItem (click)="revokeSponsorship()" - [attr.aria-label]="'revokeAccount' | i18n: sponsoringOrg.familySponsorshipFriendlyName" + [attr.aria-label]="'revokeAccountMessage' | i18n: sponsoringOrg.familySponsorshipFriendlyName" > {{ "remove" | i18n }} diff --git a/apps/web/src/app/shared/loose-components.module.ts b/apps/web/src/app/shared/loose-components.module.ts index bec06888c57..90e4c6ba9c3 100644 --- a/apps/web/src/app/shared/loose-components.module.ts +++ b/apps/web/src/app/shared/loose-components.module.ts @@ -62,8 +62,6 @@ import { PipesModule } from "../vault/individual-vault/pipes/pipes.module"; import { PurgeVaultComponent } from "../vault/settings/purge-vault.component"; import { FreeBitwardenFamiliesComponent } from "./../billing/members/free-bitwarden-families.component"; -import { OrganizationMemberFamiliesComponent } from "./../billing/members/organization-member-families.component"; -import { OrganizationSponsoredFamiliesComponent } from "./../billing/members/organization-sponsored-families.component"; import { EnvironmentSelectorModule } from "./../components/environment-selector/environment-selector.module"; import { AccountFingerprintComponent } from "./components/account-fingerprint/account-fingerprint.component"; import { SharedModule } from "./shared.module"; @@ -128,8 +126,6 @@ import { SharedModule } from "./shared.module"; SelectableAvatarComponent, SetPasswordComponent, SponsoredFamiliesComponent, - OrganizationSponsoredFamiliesComponent, - OrganizationMemberFamiliesComponent, FreeBitwardenFamiliesComponent, SponsoringOrgRowComponent, UpdatePasswordComponent, @@ -176,8 +172,6 @@ import { SharedModule } from "./shared.module"; SelectableAvatarComponent, SetPasswordComponent, SponsoredFamiliesComponent, - OrganizationSponsoredFamiliesComponent, - OrganizationMemberFamiliesComponent, FreeBitwardenFamiliesComponent, SponsoringOrgRowComponent, UpdateTempPasswordComponent, diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 4650ded54bb..c81954ef9ca 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -6303,13 +6303,13 @@ "sponsoredBitwardenFamilies": { "message": "Sponsored families" }, - "noSponsoredFamilies": { + "noSponsoredFamiliesMessage": { "message": "No sponsored families" }, - "noSponsoredFamiliesDescription": { + "nosponsoredFamiliesDetails": { "message": "Sponsored non-member families plans will display here" }, - "sponsorFreeBitwardenFamilies": { + "sponsorshipFreeBitwardenFamilies": { "message": "Members of your organization are eligible for Free Bitwarden Families. You can sponsor Free Bitwarden Families for employees who are not a member of your Bitwarden organization. Sponsoring a non-member requires an available seat within your organization." }, "sponsoredFamiliesRemoveActiveSponsorship": { @@ -6327,8 +6327,8 @@ "sponsoredFamiliesPremiumAccess": { "message": "Premium access for up to 6 users" }, - "sponsoredFamiliesSharedCollections": { - "message": "Shared collections for Family secrets" + "sponsoredFamiliesSharedCollectionsMessage": { + "message": "Shared collections for family members" }, "memberFamilies": { "message": "Member families" @@ -6342,6 +6342,15 @@ "membersWithSponsoredFamilies": { "message": "Members of your organization are eligible for Free Bitwarden Families. Here you can see members who have sponsored a Families organization." }, + "organizationHasMemberMessage": { + "message": "A sponsorship cannot be sent to $EMAIL$ because they are a member of your organization.", + "placeholders": { + "email": { + "content": "$1", + "example": "mail@example.com" + } + } + }, "badToken": { "message": "The link is no longer valid. Please have the sponsor resend the offer." }, @@ -6393,7 +6402,7 @@ "redeemedAccount": { "message": "Account redeemed" }, - "revokeAccount": { + "revokeAccountMessage": { "message": "Revoke account $NAME$", "placeholders": { "name": { diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 1cc2b591412..0d59f4a6547 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -136,11 +136,13 @@ import { import { AccountBillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/account/account-billing-api.service.abstraction"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { OrganizationBillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/organizations/organization-billing-api.service.abstraction"; +import { OrganizationSponsorshipApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/organizations/organization-sponsorship-api.service.abstraction"; import { TaxServiceAbstraction } from "@bitwarden/common/billing/abstractions/tax.service.abstraction"; import { AccountBillingApiService } from "@bitwarden/common/billing/services/account/account-billing-api.service"; import { DefaultBillingAccountProfileStateService } from "@bitwarden/common/billing/services/account/billing-account-profile-state.service"; import { BillingApiService } from "@bitwarden/common/billing/services/billing-api.service"; import { OrganizationBillingApiService } from "@bitwarden/common/billing/services/organization/organization-billing-api.service"; +import { OrganizationSponsorshipApiService } from "@bitwarden/common/billing/services/organization/organization-sponsorship-api.service"; import { OrganizationBillingService } from "@bitwarden/common/billing/services/organization-billing.service"; import { TaxService } from "@bitwarden/common/billing/services/tax.service"; import { BulkEncryptService } from "@bitwarden/common/key-management/crypto/abstractions/bulk-encrypt.service"; @@ -1063,6 +1065,11 @@ const safeProviders: SafeProvider[] = [ // subscribes to sync notifications and will update itself based on that. deps: [ApiServiceAbstraction, SyncService], }), + safeProvider({ + provide: OrganizationSponsorshipApiServiceAbstraction, + useClass: OrganizationSponsorshipApiService, + deps: [ApiServiceAbstraction], + }), safeProvider({ provide: OrganizationBillingApiServiceAbstraction, useClass: OrganizationBillingApiService, diff --git a/libs/common/src/admin-console/models/request/organization/organization-sponsorship-create.request.ts b/libs/common/src/admin-console/models/request/organization/organization-sponsorship-create.request.ts index 19e993487c2..726bd6a85e1 100644 --- a/libs/common/src/admin-console/models/request/organization/organization-sponsorship-create.request.ts +++ b/libs/common/src/admin-console/models/request/organization/organization-sponsorship-create.request.ts @@ -6,5 +6,6 @@ export class OrganizationSponsorshipCreateRequest { sponsoredEmail: string; planSponsorshipType: PlanSponsorshipType; friendlyName: string; + isAdminInitiated?: boolean; notes?: string; } diff --git a/libs/common/src/billing/abstractions/organizations/organization-sponsorship-api.service.abstraction.ts b/libs/common/src/billing/abstractions/organizations/organization-sponsorship-api.service.abstraction.ts new file mode 100644 index 00000000000..e6e395c69df --- /dev/null +++ b/libs/common/src/billing/abstractions/organizations/organization-sponsorship-api.service.abstraction.ts @@ -0,0 +1,8 @@ +import { ListResponse } from "../../../models/response/list.response"; +import { OrganizationSponsorshipInvitesResponse } from "../../models/response/organization-sponsorship-invites.response"; + +export abstract class OrganizationSponsorshipApiServiceAbstraction { + abstract getOrganizationSponsorship( + sponsoredOrgId: string, + ): Promise>; +} diff --git a/libs/common/src/billing/models/response/organization-sponsorship-invites.response.ts b/libs/common/src/billing/models/response/organization-sponsorship-invites.response.ts new file mode 100644 index 00000000000..87a2cae4699 --- /dev/null +++ b/libs/common/src/billing/models/response/organization-sponsorship-invites.response.ts @@ -0,0 +1,31 @@ +import { BaseResponse } from "../../../models/response/base.response"; +import { PlanSponsorshipType } from "../../enums"; + +export class OrganizationSponsorshipInvitesResponse extends BaseResponse { + sponsoringOrganizationUserId: string; + friendlyName: string; + offeredToEmail: string; + planSponsorshipType: PlanSponsorshipType; + lastSyncDate?: Date; + validUntil?: Date; + toDelete = false; + isAdminInitiated: boolean; + notes: string; + statusMessage?: string; + statusClass?: string; + + constructor(response: any) { + super(response); + this.sponsoringOrganizationUserId = this.getResponseProperty("SponsoringOrganizationUserId"); + this.friendlyName = this.getResponseProperty("FriendlyName"); + this.offeredToEmail = this.getResponseProperty("OfferedToEmail"); + this.planSponsorshipType = this.getResponseProperty("PlanSponsorshipType"); + this.lastSyncDate = this.getResponseProperty("LastSyncDate"); + this.validUntil = this.getResponseProperty("ValidUntil"); + this.toDelete = this.getResponseProperty("ToDelete") ?? false; + this.isAdminInitiated = this.getResponseProperty("IsAdminInitiated"); + this.notes = this.getResponseProperty("Notes"); + this.statusMessage = this.getResponseProperty("StatusMessage"); + this.statusClass = this.getResponseProperty("StatusClass"); + } +} diff --git a/libs/common/src/billing/services/organization/organization-sponsorship-api.service.ts b/libs/common/src/billing/services/organization/organization-sponsorship-api.service.ts new file mode 100644 index 00000000000..bb420377439 --- /dev/null +++ b/libs/common/src/billing/services/organization/organization-sponsorship-api.service.ts @@ -0,0 +1,22 @@ +import { ApiService } from "../../../abstractions/api.service"; +import { ListResponse } from "../../../models/response/list.response"; +import { OrganizationSponsorshipApiServiceAbstraction } from "../../abstractions/organizations/organization-sponsorship-api.service.abstraction"; +import { OrganizationSponsorshipInvitesResponse } from "../../models/response/organization-sponsorship-invites.response"; + +export class OrganizationSponsorshipApiService + implements OrganizationSponsorshipApiServiceAbstraction +{ + constructor(private apiService: ApiService) {} + async getOrganizationSponsorship( + sponsoredOrgId: string, + ): Promise> { + const r = await this.apiService.send( + "GET", + "/organization/sponsorship/" + sponsoredOrgId + "/sponsored", + null, + true, + true, + ); + return new ListResponse(r, OrganizationSponsorshipInvitesResponse); + } +} From c9dcba2506b1d811cfcfd91b58f0d794b7b570a4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 1 May 2025 09:00:40 -0700 Subject: [PATCH 033/116] [deps]: Update docker/setup-qemu-action action to v3.6.0 (#14504) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build-web.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-web.yml b/.github/workflows/build-web.yml index 4ca6dc25aab..630e1e55682 100644 --- a/.github/workflows/build-web.yml +++ b/.github/workflows/build-web.yml @@ -196,7 +196,7 @@ jobs: } - name: Set up QEMU emulators - uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0 + uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0 - name: Set up Docker Buildx uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 From a50b45c505bd839d965856429543b5310c2f647b Mon Sep 17 00:00:00 2001 From: cyprain-okeke <108260115+cyprain-okeke@users.noreply.github.com> Date: Thu, 1 May 2025 17:12:28 +0100 Subject: [PATCH 034/116] Resolve the typo (#14584) --- .../billing/members/free-bitwarden-families.component.html | 2 +- .../app/billing/settings/sponsored-families.component.html | 2 +- apps/web/src/locales/en/messages.json | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/web/src/app/billing/members/free-bitwarden-families.component.html b/apps/web/src/app/billing/members/free-bitwarden-families.component.html index a156eb608d5..9e32fb925a8 100644 --- a/apps/web/src/app/billing/members/free-bitwarden-families.component.html +++ b/apps/web/src/app/billing/members/free-bitwarden-families.component.html @@ -11,7 +11,7 @@ {{ "sponsorshipFreeBitwardenFamilies" | i18n }}

- {{ "sponsoredFamiliesInclude" | i18n }}: + {{ "sponsoredFamiliesIncludeMessage" | i18n }}:
  • {{ "sponsoredFamiliesPremiumAccess" | i18n }}
  • {{ "sponsoredFamiliesSharedCollectionsMessage" | i18n }}
  • diff --git a/apps/web/src/app/billing/settings/sponsored-families.component.html b/apps/web/src/app/billing/settings/sponsored-families.component.html index 8e829ae70ef..5a6957718a3 100644 --- a/apps/web/src/app/billing/settings/sponsored-families.component.html +++ b/apps/web/src/app/billing/settings/sponsored-families.component.html @@ -10,7 +10,7 @@ {{ "sponsoredFamiliesEligible" | i18n }}

    - {{ "sponsoredFamiliesInclude" | i18n }}: + {{ "sponsoredFamiliesIncludeMessage" | i18n }}:
    • {{ "sponsoredFamiliesPremiumAccess" | i18n }}
    • {{ "sponsoredFamiliesSharedCollectionsMessage" | i18n }}
    • diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index c81954ef9ca..da71d1cc219 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -6321,8 +6321,8 @@ "sponsoredFamiliesEligibleCard": { "message": "Redeem your Free Bitwarden for Families plan today to keep your data secure even when you are not at work." }, - "sponsoredFamiliesInclude": { - "message": "The Bitwarden for Families plan include" + "sponsoredFamiliesIncludeMessage": { + "message": "The Bitwarden for Families plan includes" }, "sponsoredFamiliesPremiumAccess": { "message": "Premium access for up to 6 users" From 64daf5a889aac740bc626d9435ba6e692902e96d Mon Sep 17 00:00:00 2001 From: Alex Morask <144709477+amorask-bitwarden@users.noreply.github.com> Date: Thu, 1 May 2025 12:12:37 -0400 Subject: [PATCH 035/116] Require provider payment method during setup behind FF (#14550) --- .../shared/payment/payment.component.html | 2 +- .../shared/payment/payment.component.ts | 12 ++++ apps/web/src/locales/en/messages.json | 3 + .../providers/providers.module.ts | 2 + .../providers/setup/setup.component.html | 57 ++++++++++++++----- .../providers/setup/setup.component.ts | 17 +++++- .../provider/provider-setup.request.ts | 2 + libs/common/src/enums/feature-flag.enum.ts | 2 + 8 files changed, 80 insertions(+), 17 deletions(-) diff --git a/apps/web/src/app/billing/shared/payment/payment.component.html b/apps/web/src/app/billing/shared/payment/payment.component.html index c86975cd0e8..0d76d98e334 100644 --- a/apps/web/src/app/billing/shared/payment/payment.component.html +++ b/apps/web/src/app/billing/shared/payment/payment.component.html @@ -81,7 +81,7 @@ - {{ "verifyBankAccountWithStatementDescriptorWarning" | i18n }} + {{ bankAccountWarning }}
      diff --git a/apps/web/src/app/billing/shared/payment/payment.component.ts b/apps/web/src/app/billing/shared/payment/payment.component.ts index c7c3e31c89f..5911e377869 100644 --- a/apps/web/src/app/billing/shared/payment/payment.component.ts +++ b/apps/web/src/app/billing/shared/payment/payment.component.ts @@ -8,6 +8,7 @@ import { takeUntil } from "rxjs/operators"; import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions"; import { PaymentMethodType } from "@bitwarden/common/billing/enums"; import { TokenizedPaymentSourceRequest } from "@bitwarden/common/billing/models/request/tokenized-payment-source.request"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { SharedModule } from "../../../shared"; import { BillingServicesModule, BraintreeService, StripeService } from "../../services"; @@ -37,6 +38,8 @@ export class PaymentComponent implements OnInit, OnDestroy { /** If provided, will be invoked with the tokenized payment source during form submission. */ @Input() protected onSubmit?: (request: TokenizedPaymentSourceRequest) => Promise; + @Input() private bankAccountWarningOverride?: string; + @Output() submitted = new EventEmitter(); private destroy$ = new Subject(); @@ -56,6 +59,7 @@ export class PaymentComponent implements OnInit, OnDestroy { constructor( private billingApiService: BillingApiServiceAbstraction, private braintreeService: BraintreeService, + private i18nService: I18nService, private stripeService: StripeService, ) {} @@ -200,4 +204,12 @@ export class PaymentComponent implements OnInit, OnDestroy { private get usingStripe(): boolean { return this.usingBankAccount || this.usingCard; } + + get bankAccountWarning(): string { + if (this.bankAccountWarningOverride) { + return this.bankAccountWarningOverride; + } else { + return this.i18nService.t("verifyBankAccountWithStatementDescriptorWarning"); + } + } } diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index da71d1cc219..ce45b538bbe 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -10631,5 +10631,8 @@ }, "restart": { "message": "Restart" + }, + "verifyProviderBankAccountWithStatementDescriptorWarning": { + "message": "Payment with a bank account is only available to customers in the United States. You will be required to verify your bank account. We will make a micro-deposit within the next 1-2 business days. Enter the statement descriptor code from this deposit on the provider's subscription page to verify the bank account. Failure to verify the bank account will result in a missed payment and your subscription being suspended." } } diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/providers.module.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/providers.module.ts index dd9baa99948..1c15812edc8 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/providers.module.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/providers.module.ts @@ -7,6 +7,7 @@ import { JslibModule } from "@bitwarden/angular/jslib.module"; import { CardComponent, SearchModule } from "@bitwarden/components"; import { DangerZoneComponent } from "@bitwarden/web-vault/app/auth/settings/account/danger-zone.component"; import { OrganizationPlansComponent } from "@bitwarden/web-vault/app/billing"; +import { PaymentComponent } from "@bitwarden/web-vault/app/billing/shared/payment/payment.component"; import { VerifyBankAccountComponent } from "@bitwarden/web-vault/app/billing/shared/verify-bank-account/verify-bank-account.component"; import { OssModule } from "@bitwarden/web-vault/app/oss.module"; @@ -53,6 +54,7 @@ import { VerifyRecoverDeleteProviderComponent } from "./verify-recover-delete-pr ScrollingModule, VerifyBankAccountComponent, CardComponent, + PaymentComponent, ], declarations: [ AcceptProviderComponent, diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/setup/setup.component.html b/bitwarden_license/bit-web/src/app/admin-console/providers/setup/setup.component.html index 38b4c3bc9de..4c5a35ea58d 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/setup/setup.component.html +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/setup/setup.component.html @@ -12,23 +12,50 @@

      {{ "setupProviderDesc" | i18n }}

      -

      {{ "generalInformation" | i18n }}

      -
      -
      - - {{ "providerName" | i18n }} - - + @if (!(requireProviderPaymentMethodDuringSetup$ | async)) { +

      {{ "generalInformation" | i18n }}

      +
      +
      + + {{ "providerName" | i18n }} + + +
      +
      + + {{ "billingEmail" | i18n }} + + {{ "providerBillingEmailHint" | i18n }} + +
      -
      - - {{ "billingEmail" | i18n }} - - {{ "providerBillingEmailHint" | i18n }} - + + } @else { +

      {{ "billingInformation" | i18n }}

      +
      +
      + + {{ "providerName" | i18n }} + + +
      +
      + + {{ "billingEmail" | i18n }} + + {{ "providerBillingEmailHint" | i18n }} + +
      -
      - +

      {{ "paymentMethod" | i18n }}

      + + + } diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/setup/setup.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/setup/setup.component.ts index ecf649b8f31..0b6483b9f48 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/setup/setup.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/setup/setup.component.ts @@ -3,13 +3,14 @@ import { Component, OnDestroy, OnInit, ViewChild } from "@angular/core"; import { FormBuilder, Validators } from "@angular/forms"; import { ActivatedRoute, Router } from "@angular/router"; -import { Subject, switchMap } from "rxjs"; +import { firstValueFrom, Subject, switchMap } from "rxjs"; import { first, takeUntil } from "rxjs/operators"; import { ManageTaxInformationComponent } from "@bitwarden/angular/billing/components"; import { ProviderApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/provider/provider-api.service.abstraction"; import { ProviderSetupRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-setup.request"; import { ExpandedTaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/expanded-tax-info-update.request"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; @@ -17,12 +18,14 @@ import { ProviderKey } from "@bitwarden/common/types/key"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; import { ToastService } from "@bitwarden/components"; import { KeyService } from "@bitwarden/key-management"; +import { PaymentComponent } from "@bitwarden/web-vault/app/billing/shared/payment/payment.component"; @Component({ selector: "provider-setup", templateUrl: "setup.component.html", }) export class SetupComponent implements OnInit, OnDestroy { + @ViewChild(PaymentComponent) paymentComponent: PaymentComponent; @ViewChild(ManageTaxInformationComponent) taxInformationComponent: ManageTaxInformationComponent; loading = true; @@ -36,6 +39,10 @@ export class SetupComponent implements OnInit, OnDestroy { private destroy$ = new Subject(); + requireProviderPaymentMethodDuringSetup$ = this.configService.getFeatureFlag$( + FeatureFlag.PM19956_RequireProviderPaymentMethodDuringSetup, + ); + constructor( private router: Router, private i18nService: I18nService, @@ -134,6 +141,14 @@ export class SetupComponent implements OnInit, OnDestroy { request.taxInfo.city = taxInformation.city; request.taxInfo.state = taxInformation.state; + const requireProviderPaymentMethodDuringSetup = await firstValueFrom( + this.requireProviderPaymentMethodDuringSetup$, + ); + + if (requireProviderPaymentMethodDuringSetup) { + request.paymentSource = await this.paymentComponent.tokenize(); + } + const provider = await this.providerApiService.postProviderSetup(this.providerId, request); this.toastService.showToast({ diff --git a/libs/common/src/admin-console/models/request/provider/provider-setup.request.ts b/libs/common/src/admin-console/models/request/provider/provider-setup.request.ts index d4992e969dc..5c9ea5526a0 100644 --- a/libs/common/src/admin-console/models/request/provider/provider-setup.request.ts +++ b/libs/common/src/admin-console/models/request/provider/provider-setup.request.ts @@ -1,6 +1,7 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { ExpandedTaxInfoUpdateRequest } from "../../../../billing/models/request/expanded-tax-info-update.request"; +import { TokenizedPaymentSourceRequest } from "../../../../billing/models/request/tokenized-payment-source.request"; export class ProviderSetupRequest { name: string; @@ -9,4 +10,5 @@ export class ProviderSetupRequest { token: string; key: string; taxInfo: ExpandedTaxInfoUpdateRequest; + paymentSource?: TokenizedPaymentSourceRequest; } diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index 0e0956b0460..3644ceefa9a 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -34,6 +34,7 @@ export enum FeatureFlag { PM12276_BreadcrumbEventLogs = "pm-12276-breadcrumbing-for-business-features", PM18794_ProviderPaymentMethod = "pm-18794-provider-payment-method", PM17772_AdminInitiatedSponsorships = "pm-17772-admin-initiated-sponsorships", + PM19956_RequireProviderPaymentMethodDuringSetup = "pm-19956-require-provider-payment-method-during-setup", /* Data Insights and Reporting */ CriticalApps = "pm-14466-risk-insights-critical-application", @@ -121,6 +122,7 @@ export const DefaultFeatureFlagValue = { [FeatureFlag.PM12276_BreadcrumbEventLogs]: FALSE, [FeatureFlag.PM18794_ProviderPaymentMethod]: FALSE, [FeatureFlag.PM17772_AdminInitiatedSponsorships]: FALSE, + [FeatureFlag.PM19956_RequireProviderPaymentMethodDuringSetup]: FALSE, /* Key Management */ [FeatureFlag.PrivateKeyRegeneration]: FALSE, From a62d269a8910be6f8f34930610389b6ccdec0daa Mon Sep 17 00:00:00 2001 From: Jason Ng Date: Thu, 1 May 2025 12:43:55 -0400 Subject: [PATCH 036/116] [PM-18803] nudges new items (#14523) * Added new-items-nudge service and component to show spotlight for new item nudges --- apps/browser/src/_locales/en/messages.json | 32 +++++- apps/desktop/src/locales/en/messages.json | 30 ++++++ apps/web/src/locales/en/messages.json | 30 ++++++ .../src/platform/state/state-definitions.ts | 2 +- .../src/cipher-form/cipher-form.stories.ts | 20 +++- .../components/cipher-form.component.html | 1 + .../components/cipher-form.component.ts | 2 + .../new-item-nudge.component.html | 8 ++ .../new-item-nudge.component.spec.ts | 101 ++++++++++++++++++ .../new-item-nudge.component.ts | 90 ++++++++++++++++ .../has-nudge.service.ts | 2 - .../services/custom-nudges-services/index.ts | 1 + .../new-item-nudge.service.ts | 65 +++++++++++ .../src/services/vault-nudges.service.spec.ts | 2 + .../src/services/vault-nudges.service.ts | 19 +++- 15 files changed, 398 insertions(+), 7 deletions(-) create mode 100644 libs/vault/src/cipher-form/components/new-item-nudge/new-item-nudge.component.html create mode 100644 libs/vault/src/cipher-form/components/new-item-nudge/new-item-nudge.component.spec.ts create mode 100644 libs/vault/src/cipher-form/components/new-item-nudge/new-item-nudge.component.ts create mode 100644 libs/vault/src/services/custom-nudges-services/new-item-nudge.service.ts diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 5c9e829e82f..6e1e2ef57ac 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -5248,5 +5248,35 @@ }, "hasItemsVaultNudgeBody": { "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json index 81e3a94ff4d..2350e0df4c7 100644 --- a/apps/desktop/src/locales/en/messages.json +++ b/apps/desktop/src/locales/en/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index ce45b538bbe..59ba7961d82 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -10629,6 +10629,36 @@ "newBusinessUnit": { "message": "New business unit" }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." + }, "restart": { "message": "Restart" }, diff --git a/libs/common/src/platform/state/state-definitions.ts b/libs/common/src/platform/state/state-definitions.ts index 70e0c3998dd..587212299df 100644 --- a/libs/common/src/platform/state/state-definitions.ts +++ b/libs/common/src/platform/state/state-definitions.ts @@ -206,7 +206,7 @@ export const VAULT_APPEARANCE = new StateDefinition("vaultAppearance", "disk"); export const SECURITY_TASKS_DISK = new StateDefinition("securityTasks", "disk"); export const AT_RISK_PASSWORDS_PAGE_DISK = new StateDefinition("atRiskPasswordsPage", "disk"); export const NOTIFICATION_DISK = new StateDefinition("notifications", "disk"); -export const VAULT_NUDGES_DISK = new StateDefinition("vaultNudges", "disk"); +export const VAULT_NUDGES_DISK = new StateDefinition("vaultNudges", "disk", { web: "disk-local" }); export const VAULT_BROWSER_INTRO_CAROUSEL = new StateDefinition( "vaultBrowserIntroCarousel", "disk", diff --git a/libs/vault/src/cipher-form/cipher-form.stories.ts b/libs/vault/src/cipher-form/cipher-form.stories.ts index 50577472120..9943f07292d 100644 --- a/libs/vault/src/cipher-form/cipher-form.stories.ts +++ b/libs/vault/src/cipher-form/cipher-form.stories.ts @@ -34,7 +34,9 @@ import { AsyncActionsModule, ButtonModule, ItemModule, ToastService } from "@bit import { CipherFormConfig, CipherFormGenerationService, + NudgeStatus, PasswordRepromptService, + VaultNudgesService, } from "@bitwarden/vault"; // FIXME: remove `/apps` import from `/libs` // FIXME: remove `src` and fix import @@ -47,6 +49,7 @@ import { CipherFormService } from "./abstractions/cipher-form.service"; import { TotpCaptureService } from "./abstractions/totp-capture.service"; import { CipherFormModule } from "./cipher-form.module"; import { CipherFormComponent } from "./components/cipher-form.component"; +import { NewItemNudgeComponent } from "./components/new-item-nudge/new-item-nudge.component"; import { CipherFormCacheService } from "./services/default-cipher-form-cache.service"; const defaultConfig: CipherFormConfig = { @@ -132,8 +135,23 @@ export default { component: CipherFormComponent, decorators: [ moduleMetadata({ - imports: [CipherFormModule, AsyncActionsModule, ButtonModule, ItemModule], + imports: [ + CipherFormModule, + AsyncActionsModule, + ButtonModule, + ItemModule, + NewItemNudgeComponent, + ], providers: [ + { + provide: VaultNudgesService, + useValue: { + showNudge$: new BehaviorSubject({ + hasBadgeDismissed: true, + hasSpotlightDismissed: true, + } as NudgeStatus), + }, + }, { provide: CipherFormService, useClass: TestAddEditFormService, diff --git a/libs/vault/src/cipher-form/components/cipher-form.component.html b/libs/vault/src/cipher-form/components/cipher-form.component.html index 6b327486c47..614c7f3dc7a 100644 --- a/libs/vault/src/cipher-form/components/cipher-form.component.html +++ b/libs/vault/src/cipher-form/components/cipher-form.component.html @@ -1,3 +1,4 @@ + 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 080af489253..96e1328338b 100644 --- a/libs/vault/src/cipher-form/components/cipher-form.component.ts +++ b/libs/vault/src/cipher-form/components/cipher-form.component.ts @@ -45,6 +45,7 @@ import { CardDetailsSectionComponent } from "./card-details-section/card-details import { IdentitySectionComponent } from "./identity/identity.component"; import { ItemDetailsSectionComponent } from "./item-details/item-details-section.component"; import { LoginDetailsSectionComponent } from "./login-details-section/login-details-section.component"; +import { NewItemNudgeComponent } from "./new-item-nudge/new-item-nudge.component"; import { SshKeySectionComponent } from "./sshkey-section/sshkey-section.component"; @Component({ @@ -76,6 +77,7 @@ import { SshKeySectionComponent } from "./sshkey-section/sshkey-section.componen NgIf, AdditionalOptionsSectionComponent, LoginDetailsSectionComponent, + NewItemNudgeComponent, ], }) export class CipherFormComponent implements AfterViewInit, OnInit, OnChanges, CipherFormContainer { diff --git a/libs/vault/src/cipher-form/components/new-item-nudge/new-item-nudge.component.html b/libs/vault/src/cipher-form/components/new-item-nudge/new-item-nudge.component.html new file mode 100644 index 00000000000..5cd1246fd36 --- /dev/null +++ b/libs/vault/src/cipher-form/components/new-item-nudge/new-item-nudge.component.html @@ -0,0 +1,8 @@ + + + + diff --git a/libs/vault/src/cipher-form/components/new-item-nudge/new-item-nudge.component.spec.ts b/libs/vault/src/cipher-form/components/new-item-nudge/new-item-nudge.component.spec.ts new file mode 100644 index 00000000000..073c588690d --- /dev/null +++ b/libs/vault/src/cipher-form/components/new-item-nudge/new-item-nudge.component.spec.ts @@ -0,0 +1,101 @@ +import { CommonModule } from "@angular/common"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { mock, MockProxy } from "jest-mock-extended"; +import { of } from "rxjs"; + +import { AccountService, Account } from "@bitwarden/common/auth/abstractions/account.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { UserId } from "@bitwarden/common/types/guid"; +import { CipherType } from "@bitwarden/sdk-internal"; + +import { VaultNudgesService, VaultNudgeType } from "../../../services/vault-nudges.service"; + +import { NewItemNudgeComponent } from "./new-item-nudge.component"; + +describe("NewItemNudgeComponent", () => { + let component: NewItemNudgeComponent; + let fixture: ComponentFixture; + + let i18nService: MockProxy; + let accountService: MockProxy; + let vaultNudgesService: MockProxy; + + beforeEach(async () => { + i18nService = mock({ t: (key: string) => key }); + accountService = mock(); + vaultNudgesService = mock(); + + await TestBed.configureTestingModule({ + imports: [NewItemNudgeComponent, CommonModule], + providers: [ + { provide: I18nService, useValue: i18nService }, + { provide: AccountService, useValue: accountService }, + { provide: VaultNudgesService, useValue: vaultNudgesService }, + ], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(NewItemNudgeComponent); + component = fixture.componentInstance; + component.configType = null; // Set to null for initial state + fixture.detectChanges(); + }); + + it("should create", () => { + expect(component).toBeTruthy(); + }); + + it("should set nudge title and body for CipherType.Login type", async () => { + component.configType = CipherType.Login; + accountService.activeAccount$ = of({ id: "test-user-id" as UserId } as Account); + jest.spyOn(component, "checkHasSpotlightDismissed").mockResolvedValue(true); + + await component.ngOnInit(); + + expect(component.showNewItemSpotlight).toBe(true); + expect(component.nudgeTitle).toBe("newLoginNudgeTitle"); + expect(component.nudgeBody).toBe("newLoginNudgeBody"); + expect(component.dismissalNudgeType).toBe(VaultNudgeType.newLoginItemStatus); + }); + + it("should set nudge title and body for CipherType.Card type", async () => { + component.configType = CipherType.Card; + accountService.activeAccount$ = of({ id: "test-user-id" as UserId } as Account); + jest.spyOn(component, "checkHasSpotlightDismissed").mockResolvedValue(true); + + await component.ngOnInit(); + + expect(component.showNewItemSpotlight).toBe(true); + expect(component.nudgeTitle).toBe("newCardNudgeTitle"); + expect(component.nudgeBody).toBe("newCardNudgeBody"); + expect(component.dismissalNudgeType).toBe(VaultNudgeType.newCardItemStatus); + }); + + it("should not show anything if spotlight has been dismissed", async () => { + component.configType = CipherType.Identity; + accountService.activeAccount$ = of({ id: "test-user-id" as UserId } as Account); + jest.spyOn(component, "checkHasSpotlightDismissed").mockResolvedValue(false); + + await component.ngOnInit(); + + expect(component.showNewItemSpotlight).toBe(false); + expect(component.dismissalNudgeType).toBe(VaultNudgeType.newIdentityItemStatus); + }); + + it("should set showNewItemSpotlight to false when user dismisses spotlight", async () => { + component.showNewItemSpotlight = true; + component.dismissalNudgeType = VaultNudgeType.newLoginItemStatus; + component.activeUserId = "test-user-id" as UserId; + + const dismissSpy = jest.spyOn(vaultNudgesService, "dismissNudge").mockResolvedValue(); + + await component.dismissNewItemSpotlight(); + + expect(component.showNewItemSpotlight).toBe(false); + expect(dismissSpy).toHaveBeenCalledWith( + VaultNudgeType.newLoginItemStatus, + component.activeUserId, + ); + }); +}); 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 new file mode 100644 index 00000000000..b497585d4fb --- /dev/null +++ b/libs/vault/src/cipher-form/components/new-item-nudge/new-item-nudge.component.ts @@ -0,0 +1,90 @@ +import { NgIf } from "@angular/common"; +import { Component, Input, OnInit } from "@angular/core"; +import { firstValueFrom } from "rxjs"; + +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 { UserId } from "@bitwarden/common/types/guid"; +import { CipherType } from "@bitwarden/sdk-internal"; + +import { SpotlightComponent } from "../../../components/spotlight/spotlight.component"; +import { VaultNudgesService, VaultNudgeType } from "../../../services/vault-nudges.service"; + +@Component({ + selector: "vault-new-item-nudge", + templateUrl: "./new-item-nudge.component.html", + standalone: true, + imports: [NgIf, SpotlightComponent], +}) +export class NewItemNudgeComponent implements OnInit { + @Input({ required: true }) configType: CipherType | null = null; + activeUserId: UserId | null = null; + showNewItemSpotlight: boolean = false; + nudgeTitle: string = ""; + nudgeBody: string = ""; + dismissalNudgeType: VaultNudgeType | null = null; + + constructor( + private i18nService: I18nService, + private accountService: AccountService, + private vaultNudgesService: VaultNudgesService, + ) {} + + async ngOnInit() { + this.activeUserId = await firstValueFrom(getUserId(this.accountService.activeAccount$)); + + switch (this.configType) { + case CipherType.Login: + this.dismissalNudgeType = VaultNudgeType.newLoginItemStatus; + this.nudgeTitle = this.i18nService.t("newLoginNudgeTitle"); + this.nudgeBody = this.i18nService.t("newLoginNudgeBody"); + break; + + case CipherType.Card: + this.dismissalNudgeType = VaultNudgeType.newCardItemStatus; + this.nudgeTitle = this.i18nService.t("newCardNudgeTitle"); + this.nudgeBody = this.i18nService.t("newCardNudgeBody"); + break; + + case CipherType.Identity: + this.dismissalNudgeType = VaultNudgeType.newIdentityItemStatus; + this.nudgeTitle = this.i18nService.t("newIdentityNudgeTitle"); + this.nudgeBody = this.i18nService.t("newIdentityNudgeBody"); + break; + + case CipherType.SecureNote: + this.dismissalNudgeType = VaultNudgeType.newNoteItemStatus; + this.nudgeTitle = this.i18nService.t("newNoteNudgeTitle"); + this.nudgeBody = this.i18nService.t("newNoteNudgeBody"); + break; + + case CipherType.SshKey: + this.dismissalNudgeType = VaultNudgeType.newSshItemStatus; + this.nudgeTitle = this.i18nService.t("newSshNudgeTitle"); + this.nudgeBody = this.i18nService.t("newSshNudgeBody"); + break; + default: + throw new Error("Unsupported cipher type"); + } + this.showNewItemSpotlight = await this.checkHasSpotlightDismissed( + this.dismissalNudgeType as VaultNudgeType, + this.activeUserId, + ); + } + + async dismissNewItemSpotlight() { + if (this.dismissalNudgeType && this.activeUserId) { + await this.vaultNudgesService.dismissNudge( + this.dismissalNudgeType, + this.activeUserId as UserId, + ); + this.showNewItemSpotlight = false; + } + } + + async checkHasSpotlightDismissed(nudgeType: VaultNudgeType, userId: UserId): Promise { + return !(await firstValueFrom(this.vaultNudgesService.showNudge$(nudgeType, userId))) + .hasSpotlightDismissed; + } +} diff --git a/libs/vault/src/services/custom-nudges-services/has-nudge.service.ts b/libs/vault/src/services/custom-nudges-services/has-nudge.service.ts index 0c14cff002f..c9077a7283b 100644 --- a/libs/vault/src/services/custom-nudges-services/has-nudge.service.ts +++ b/libs/vault/src/services/custom-nudges-services/has-nudge.service.ts @@ -18,8 +18,6 @@ export class HasNudgeService extends DefaultSingleNudgeService { private nudgeTypes: VaultNudgeType[] = [ VaultNudgeType.EmptyVaultNudge, - VaultNudgeType.HasVaultItems, - VaultNudgeType.IntroCarouselDismissal, // add additional nudge types here as needed ]; diff --git a/libs/vault/src/services/custom-nudges-services/index.ts b/libs/vault/src/services/custom-nudges-services/index.ts index 9a1f0acd420..131db023175 100644 --- a/libs/vault/src/services/custom-nudges-services/index.ts +++ b/libs/vault/src/services/custom-nudges-services/index.ts @@ -1,3 +1,4 @@ export * from "./has-items-nudge.service"; export * from "./empty-vault-nudge.service"; export * from "./has-nudge.service"; +export * from "./new-item-nudge.service"; diff --git a/libs/vault/src/services/custom-nudges-services/new-item-nudge.service.ts b/libs/vault/src/services/custom-nudges-services/new-item-nudge.service.ts new file mode 100644 index 00000000000..93ef5d81dc4 --- /dev/null +++ b/libs/vault/src/services/custom-nudges-services/new-item-nudge.service.ts @@ -0,0 +1,65 @@ +import { inject, Injectable } from "@angular/core"; +import { combineLatest, Observable, switchMap } from "rxjs"; + +import { UserId } from "@bitwarden/common/types/guid"; +import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { CipherType } from "@bitwarden/common/vault/enums"; + +import { DefaultSingleNudgeService } from "../default-single-nudge.service"; +import { NudgeStatus, VaultNudgeType } from "../vault-nudges.service"; + +/** + * Custom Nudge Service Checking Nudge Status For Vault New Item Types + */ +@Injectable({ + providedIn: "root", +}) +export class NewItemNudgeService extends DefaultSingleNudgeService { + cipherService = inject(CipherService); + + nudgeStatus$(nudgeType: VaultNudgeType, userId: UserId): Observable { + return combineLatest([ + this.getNudgeStatus$(nudgeType, userId), + this.cipherService.cipherViews$(userId), + ]).pipe( + switchMap(async ([nudgeStatus, ciphers]) => { + if (nudgeStatus.hasSpotlightDismissed) { + return nudgeStatus; + } + + let currentType: CipherType; + + switch (nudgeType) { + case VaultNudgeType.newLoginItemStatus: + currentType = CipherType.Login; + break; + case VaultNudgeType.newCardItemStatus: + currentType = CipherType.Card; + break; + case VaultNudgeType.newIdentityItemStatus: + currentType = CipherType.Identity; + break; + case VaultNudgeType.newNoteItemStatus: + currentType = CipherType.SecureNote; + break; + case VaultNudgeType.newSshItemStatus: + currentType = CipherType.SshKey; + break; + } + + const ciphersBoolean = ciphers.some((cipher) => cipher.type === currentType); + + if (ciphersBoolean) { + const dismissedStatus = { + hasSpotlightDismissed: true, + hasBadgeDismissed: true, + }; + await this.setNudgeStatus(nudgeType, dismissedStatus, userId); + return dismissedStatus; + } + + return nudgeStatus; + }), + ); + } +} diff --git a/libs/vault/src/services/vault-nudges.service.spec.ts b/libs/vault/src/services/vault-nudges.service.spec.ts index a01cac94fb1..69ddf1cdaa0 100644 --- a/libs/vault/src/services/vault-nudges.service.spec.ts +++ b/libs/vault/src/services/vault-nudges.service.spec.ts @@ -5,6 +5,7 @@ import { firstValueFrom, of } from "rxjs"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { StateProvider } from "@bitwarden/common/platform/state"; import { UserId } from "@bitwarden/common/types/guid"; +import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FakeStateProvider, mockAccountServiceWith } from "../../../common/spec"; @@ -46,6 +47,7 @@ describe("Vault Nudges Service", () => { provide: EmptyVaultNudgeService, useValue: mock(), }, + { provide: CipherService, useValue: mock() }, ], }); }); diff --git a/libs/vault/src/services/vault-nudges.service.ts b/libs/vault/src/services/vault-nudges.service.ts index 28198d17068..98f28af9954 100644 --- a/libs/vault/src/services/vault-nudges.service.ts +++ b/libs/vault/src/services/vault-nudges.service.ts @@ -6,7 +6,11 @@ import { ConfigService } from "@bitwarden/common/platform/abstractions/config/co import { UserKeyDefinition, VAULT_NUDGES_DISK } from "@bitwarden/common/platform/state"; import { UserId } from "@bitwarden/common/types/guid"; -import { HasItemsNudgeService, EmptyVaultNudgeService } from "./custom-nudges-services"; +import { + HasItemsNudgeService, + EmptyVaultNudgeService, + NewItemNudgeService, +} from "./custom-nudges-services"; import { DefaultSingleNudgeService, SingleNudgeService } from "./default-single-nudge.service"; export type NudgeStatus = { @@ -23,7 +27,11 @@ export enum VaultNudgeType { */ EmptyVaultNudge = "empty-vault-nudge", HasVaultItems = "has-vault-items", - IntroCarouselDismissal = "intro-carousel-dismissal", + newLoginItemStatus = "new-login-item-status", + newCardItemStatus = "new-card-item-status", + newIdentityItemStatus = "new-identity-item-status", + newNoteItemStatus = "new-note-item-status", + newSshItemStatus = "new-ssh-item-status", } export const VAULT_NUDGE_DISMISSED_DISK_KEY = new UserKeyDefinition< @@ -37,6 +45,8 @@ export const VAULT_NUDGE_DISMISSED_DISK_KEY = new UserKeyDefinition< providedIn: "root", }) export class VaultNudgesService { + private newItemNudgeService = inject(NewItemNudgeService); + /** * Custom nudge services to use for specific nudge types * Each nudge type can have its own service to determine when to show the nudge @@ -45,6 +55,11 @@ export class VaultNudgesService { private customNudgeServices: any = { [VaultNudgeType.HasVaultItems]: inject(HasItemsNudgeService), [VaultNudgeType.EmptyVaultNudge]: inject(EmptyVaultNudgeService), + [VaultNudgeType.newLoginItemStatus]: this.newItemNudgeService, + [VaultNudgeType.newCardItemStatus]: this.newItemNudgeService, + [VaultNudgeType.newIdentityItemStatus]: this.newItemNudgeService, + [VaultNudgeType.newNoteItemStatus]: this.newItemNudgeService, + [VaultNudgeType.newSshItemStatus]: this.newItemNudgeService, }; /** From 1123a5993eac31493bd691fc106ceb2062ebaaec Mon Sep 17 00:00:00 2001 From: Jason Ng Date: Thu, 1 May 2025 12:44:13 -0400 Subject: [PATCH 037/116] [PM-20423] Update Empty Vault Button on Browser to follow Web (#14574) * update button flow for new login item in an empty vault on browser --- .../popup/components/vault-v2/vault-v2.component.html | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.html b/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.html index 7d04f23795e..894f27245b2 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.html @@ -15,12 +15,11 @@ {{ "yourVaultIsEmpty" | i18n }} -

      {{ "emptyVaultDescription" | i18n }}

      +

      {{ "emptyVaultDescription" | i18n }}

      - +
      + {{ "newLogin" | i18n }} +
      From de6b58c10ab6e24adbc6bbb8ae625107e4d86ff0 Mon Sep 17 00:00:00 2001 From: Nick Krantz <125900171+nick-livefront@users.noreply.github.com> Date: Thu, 1 May 2025 12:44:08 -0500 Subject: [PATCH 038/116] [PM-20110] Disabled copy buttons on vault (#14549) * export BitIconButtonComponent from component library * manually update the disabled state of the icon button for copy cipher field directive * add tests for `CopyCipherFieldDirective` --- libs/components/src/icon-button/index.ts | 1 + .../copy-cipher-field.directive.spec.ts | 197 ++++++++++++++++++ .../components/copy-cipher-field.directive.ts | 8 +- 3 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 libs/vault/src/components/copy-cipher-field.directive.spec.ts diff --git a/libs/components/src/icon-button/index.ts b/libs/components/src/icon-button/index.ts index 9da4a3162bf..cc52f263252 100644 --- a/libs/components/src/icon-button/index.ts +++ b/libs/components/src/icon-button/index.ts @@ -1 +1,2 @@ export * from "./icon-button.module"; +export { BitIconButtonComponent } from "./icon-button.component"; diff --git a/libs/vault/src/components/copy-cipher-field.directive.spec.ts b/libs/vault/src/components/copy-cipher-field.directive.spec.ts new file mode 100644 index 00000000000..0847e7147a9 --- /dev/null +++ b/libs/vault/src/components/copy-cipher-field.directive.spec.ts @@ -0,0 +1,197 @@ +import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { BitIconButtonComponent, MenuItemDirective } from "@bitwarden/components"; +import { CopyCipherFieldService } from "@bitwarden/vault"; + +import { CopyCipherFieldDirective } from "./copy-cipher-field.directive"; + +describe("CopyCipherFieldDirective", () => { + const copyFieldService = { + copy: jest.fn().mockResolvedValue(null), + totpAllowed: jest.fn().mockResolvedValue(true), + }; + + let copyCipherFieldDirective: CopyCipherFieldDirective; + + beforeEach(() => { + copyFieldService.copy.mockClear(); + copyFieldService.totpAllowed.mockClear(); + + copyCipherFieldDirective = new CopyCipherFieldDirective( + copyFieldService as unknown as CopyCipherFieldService, + ); + copyCipherFieldDirective.cipher = new CipherView(); + }); + + describe("disabled state", () => { + it("should be enabled when the field is available", async () => { + copyCipherFieldDirective.action = "username"; + copyCipherFieldDirective.cipher.login.username = "test-username"; + + await copyCipherFieldDirective.ngOnChanges(); + + expect(copyCipherFieldDirective["disabled"]).toBe(null); + }); + + it("should be disabled when the field is not available", async () => { + // create empty cipher + copyCipherFieldDirective.cipher = new CipherView(); + + copyCipherFieldDirective.action = "username"; + + await copyCipherFieldDirective.ngOnChanges(); + + expect(copyCipherFieldDirective["disabled"]).toBe(true); + }); + + it("updates icon button disabled state", async () => { + const iconButton = { + disabled: { + set: jest.fn(), + }, + }; + + copyCipherFieldDirective = new CopyCipherFieldDirective( + copyFieldService as unknown as CopyCipherFieldService, + undefined, + iconButton as unknown as BitIconButtonComponent, + ); + + copyCipherFieldDirective.action = "password"; + + await copyCipherFieldDirective.ngOnChanges(); + + expect(iconButton.disabled.set).toHaveBeenCalledWith(true); + }); + + it("updates menuItemDirective disabled state", async () => { + const menuItemDirective = { + disabled: false, + }; + + copyCipherFieldDirective = new CopyCipherFieldDirective( + copyFieldService as unknown as CopyCipherFieldService, + menuItemDirective as unknown as MenuItemDirective, + ); + + copyCipherFieldDirective.action = "totp"; + + await copyCipherFieldDirective.ngOnChanges(); + + expect(menuItemDirective.disabled).toBe(true); + }); + }); + + describe("login", () => { + beforeEach(() => { + copyCipherFieldDirective.cipher.login.username = "test-username"; + copyCipherFieldDirective.cipher.login.password = "test-password"; + copyCipherFieldDirective.cipher.login.totp = "test-totp"; + }); + + it.each([ + ["username", "test-username"], + ["password", "test-password"], + ["totp", "test-totp"], + ])("copies %s field from login to clipboard", async (action, value) => { + copyCipherFieldDirective.action = action as CopyCipherFieldDirective["action"]; + + await copyCipherFieldDirective.copy(); + + expect(copyFieldService.copy).toHaveBeenCalledWith( + value, + action, + copyCipherFieldDirective.cipher, + ); + }); + }); + + describe("identity", () => { + beforeEach(() => { + copyCipherFieldDirective.cipher.identity.username = "test-username"; + copyCipherFieldDirective.cipher.identity.email = "test-email"; + copyCipherFieldDirective.cipher.identity.phone = "test-phone"; + copyCipherFieldDirective.cipher.identity.address1 = "test-address-1"; + }); + + it.each([ + ["username", "test-username"], + ["email", "test-email"], + ["phone", "test-phone"], + ["address", "test-address-1"], + ])("copies %s field from identity to clipboard", async (action, value) => { + copyCipherFieldDirective.action = action as CopyCipherFieldDirective["action"]; + + await copyCipherFieldDirective.copy(); + + expect(copyFieldService.copy).toHaveBeenCalledWith( + value, + action, + copyCipherFieldDirective.cipher, + ); + }); + }); + + describe("card", () => { + beforeEach(() => { + copyCipherFieldDirective.cipher.card.number = "test-card-number"; + copyCipherFieldDirective.cipher.card.code = "test-card-code"; + }); + + it.each([ + ["cardNumber", "test-card-number"], + ["securityCode", "test-card-code"], + ])("copies %s field from card to clipboard", async (action, value) => { + copyCipherFieldDirective.action = action as CopyCipherFieldDirective["action"]; + + await copyCipherFieldDirective.copy(); + + expect(copyFieldService.copy).toHaveBeenCalledWith( + value, + action, + copyCipherFieldDirective.cipher, + ); + }); + }); + + describe("secure note", () => { + beforeEach(() => { + copyCipherFieldDirective.cipher.notes = "test-secure-note"; + }); + + it("copies secure note field to clipboard", async () => { + copyCipherFieldDirective.action = "secureNote"; + + await copyCipherFieldDirective.copy(); + + expect(copyFieldService.copy).toHaveBeenCalledWith( + "test-secure-note", + "secureNote", + copyCipherFieldDirective.cipher, + ); + }); + }); + + describe("ssh key", () => { + beforeEach(() => { + copyCipherFieldDirective.cipher.sshKey.privateKey = "test-private-key"; + copyCipherFieldDirective.cipher.sshKey.publicKey = "test-public-key"; + copyCipherFieldDirective.cipher.sshKey.keyFingerprint = "test-key-fingerprint"; + }); + + it.each([ + ["privateKey", "test-private-key"], + ["publicKey", "test-public-key"], + ["keyFingerprint", "test-key-fingerprint"], + ])("copies %s field from ssh key to clipboard", async (action, value) => { + copyCipherFieldDirective.action = action as CopyCipherFieldDirective["action"]; + + await copyCipherFieldDirective.copy(); + + expect(copyFieldService.copy).toHaveBeenCalledWith( + value, + action, + copyCipherFieldDirective.cipher, + ); + }); + }); +}); diff --git a/libs/vault/src/components/copy-cipher-field.directive.ts b/libs/vault/src/components/copy-cipher-field.directive.ts index 1eb96a30449..324b43f12d4 100644 --- a/libs/vault/src/components/copy-cipher-field.directive.ts +++ b/libs/vault/src/components/copy-cipher-field.directive.ts @@ -1,7 +1,7 @@ import { Directive, HostBinding, HostListener, Input, OnChanges, Optional } from "@angular/core"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; -import { MenuItemDirective } from "@bitwarden/components"; +import { MenuItemDirective, BitIconButtonComponent } from "@bitwarden/components"; import { CopyAction, CopyCipherFieldService } from "@bitwarden/vault"; /** @@ -33,6 +33,7 @@ export class CopyCipherFieldDirective implements OnChanges { constructor( private copyCipherFieldService: CopyCipherFieldService, @Optional() private menuItemDirective?: MenuItemDirective, + @Optional() private iconButtonComponent?: BitIconButtonComponent, ) {} @HostBinding("attr.disabled") @@ -65,6 +66,11 @@ export class CopyCipherFieldDirective implements OnChanges { ? true : null; + // When used on an icon button, update the disabled state of the button component + if (this.iconButtonComponent) { + this.iconButtonComponent.disabled.set(this.disabled ?? false); + } + // If the directive is used on a menu item, update the menu item to prevent keyboard navigation if (this.menuItemDirective) { this.menuItemDirective.disabled = this.disabled ?? false; From cba5f826d665f201e9f454344fbec780de0e275c Mon Sep 17 00:00:00 2001 From: Shane Melton Date: Thu, 1 May 2025 11:22:32 -0700 Subject: [PATCH 039/116] [PM-21041] Fix cipher view security tasks fetching (#14569) * [PM-21041] Add taskEnabled$ dependency to tasks$ observable * [PM-21041] Rework cipher view component to only check tasks for organization Login type ciphers - Remove dependency on feature flag check (handled by tasks$ observable now) - Add try/catch in case of request failures to avoid breaking component initialization * [PM-21041] Remove now redundant taskEnabled$ chain * [PM-21041] Fix tests --- .../at-risk-password-callout.component.ts | 20 +------ .../emergency-view-dialog.component.spec.ts | 22 ++++++-- .../services/default-task.service.spec.ts | 56 +++++++++++++++++++ .../tasks/services/default-task.service.ts | 40 +++++++++---- .../cipher-view/cipher-view.component.html | 29 +++++----- .../src/cipher-view/cipher-view.component.ts | 53 ++++++++++-------- 6 files changed, 147 insertions(+), 73 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 fa4137d9849..ed78d9433f1 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 @@ -1,7 +1,7 @@ import { CommonModule } from "@angular/common"; import { Component, inject } from "@angular/core"; import { RouterModule } from "@angular/router"; -import { map, of, switchMap } from "rxjs"; +import { map, switchMap } from "rxjs"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; @@ -20,21 +20,7 @@ export class AtRiskPasswordCalloutComponent { private activeAccount$ = inject(AccountService).activeAccount$.pipe(getUserId); protected pendingTasks$ = this.activeAccount$.pipe( - switchMap((userId) => - this.taskService.tasksEnabled$(userId).pipe( - switchMap((enabled) => { - if (!enabled) { - return of([]); - } - return this.taskService - .pendingTasks$(userId) - .pipe( - map((tasks) => - tasks.filter((t) => t.type === SecurityTaskType.UpdateAtRiskCredential), - ), - ); - }), - ), - ), + switchMap((userId) => this.taskService.pendingTasks$(userId)), + map((tasks) => tasks.filter((t) => t.type === SecurityTaskType.UpdateAtRiskCredential)), ); } diff --git a/apps/web/src/app/auth/settings/emergency-access/view/emergency-view-dialog.component.spec.ts b/apps/web/src/app/auth/settings/emergency-access/view/emergency-view-dialog.component.spec.ts index 3ffb54f5b1c..b341fc4f8e4 100644 --- a/apps/web/src/app/auth/settings/emergency-access/view/emergency-view-dialog.component.spec.ts +++ b/apps/web/src/app/auth/settings/emergency-access/view/emergency-view-dialog.component.spec.ts @@ -8,14 +8,16 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec"; -import { UserId } from "@bitwarden/common/types/guid"; +import { UserId, EmergencyAccessId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { LoginView } from "@bitwarden/common/vault/models/view/login.view"; import { TaskService } from "@bitwarden/common/vault/tasks"; import { DialogService, DialogRef, DIALOG_DATA } from "@bitwarden/components"; import { ChangeLoginPasswordService } from "@bitwarden/vault"; @@ -28,14 +30,15 @@ describe("EmergencyViewDialogComponent", () => { const open = jest.fn(); const close = jest.fn(); + const emergencyAccessId = "emergency-access-id" as EmergencyAccessId; const mockCipher = { id: "cipher1", name: "Cipher", type: CipherType.Login, - login: { uris: [] }, + login: { uris: [] } as Partial, card: {}, - } as CipherView; + } as Partial as CipherView; const accountService: FakeAccountService = mockAccountServiceWith(Utils.newGuid() as UserId); @@ -56,6 +59,7 @@ describe("EmergencyViewDialogComponent", () => { { provide: DIALOG_DATA, useValue: { cipher: mockCipher } }, { provide: AccountService, useValue: accountService }, { provide: TaskService, useValue: mock() }, + { provide: LogService, useValue: mock() }, ], }) .overrideComponent(EmergencyViewDialogComponent, { @@ -94,18 +98,24 @@ describe("EmergencyViewDialogComponent", () => { }); it("opens dialog", () => { - EmergencyViewDialogComponent.open({ open } as unknown as DialogService, { cipher: mockCipher }); + EmergencyViewDialogComponent.open({ open } as unknown as DialogService, { + cipher: mockCipher, + emergencyAccessId, + }); expect(open).toHaveBeenCalled(); }); it("closes the dialog", () => { - EmergencyViewDialogComponent.open({ open } as unknown as DialogService, { cipher: mockCipher }); + EmergencyViewDialogComponent.open({ open } as unknown as DialogService, { + cipher: mockCipher, + emergencyAccessId, + }); fixture.detectChanges(); const cancelButton = fixture.debugElement.queryAll(By.css("button")).pop(); - cancelButton.nativeElement.click(); + cancelButton!.nativeElement.click(); expect(close).toHaveBeenCalled(); }); diff --git a/libs/common/src/vault/tasks/services/default-task.service.spec.ts b/libs/common/src/vault/tasks/services/default-task.service.spec.ts index 4d468d09766..cb22d1296ba 100644 --- a/libs/common/src/vault/tasks/services/default-task.service.spec.ts +++ b/libs/common/src/vault/tasks/services/default-task.service.spec.ts @@ -108,6 +108,34 @@ describe("Default task service", () => { }); describe("tasks$", () => { + beforeEach(() => { + mockGetFeatureFlag$.mockReturnValue(new BehaviorSubject(true)); + mockGetAllOrgs$.mockReturnValue( + new BehaviorSubject([ + { + useRiskInsights: true, + }, + ] as Organization[]), + ); + }); + + it("should return an empty array if tasks are not enabled", async () => { + mockGetAllOrgs$.mockReturnValue( + new BehaviorSubject([ + { + useRiskInsights: false, + }, + ] as Organization[]), + ); + + const { tasks$ } = service; + + const result = await firstValueFrom(tasks$("user-id" as UserId)); + + expect(result.length).toBe(0); + expect(mockApiSend).not.toHaveBeenCalled(); + }); + it("should fetch tasks from the API when the state is null", async () => { mockApiSend.mockResolvedValue({ data: [ @@ -153,6 +181,34 @@ describe("Default task service", () => { }); describe("pendingTasks$", () => { + beforeEach(() => { + mockGetFeatureFlag$.mockReturnValue(new BehaviorSubject(true)); + mockGetAllOrgs$.mockReturnValue( + new BehaviorSubject([ + { + useRiskInsights: true, + }, + ] as Organization[]), + ); + }); + + it("should return an empty array if tasks are not enabled", async () => { + mockGetAllOrgs$.mockReturnValue( + new BehaviorSubject([ + { + useRiskInsights: false, + }, + ] as Organization[]), + ); + + const { pendingTasks$ } = service; + + const result = await firstValueFrom(pendingTasks$("user-id" as UserId)); + + expect(result.length).toBe(0); + expect(mockApiSend).not.toHaveBeenCalled(); + }); + it("should filter tasks to only pending tasks", async () => { fakeStateProvider.singleUser.mockFor("user-id" as UserId, SECURITY_TASKS, [ { diff --git a/libs/common/src/vault/tasks/services/default-task.service.ts b/libs/common/src/vault/tasks/services/default-task.service.ts index 7386102263c..a50f736f7fd 100644 --- a/libs/common/src/vault/tasks/services/default-task.service.ts +++ b/libs/common/src/vault/tasks/services/default-task.service.ts @@ -1,4 +1,14 @@ -import { combineLatest, filter, map, merge, Observable, of, Subscription, switchMap } from "rxjs"; +import { + combineLatest, + filter, + map, + merge, + Observable, + of, + Subscription, + switchMap, + distinctUntilChanged, +} from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; @@ -45,20 +55,30 @@ export class DefaultTaskService implements TaskService { .organizations$(userId) .pipe(map((orgs) => orgs.some((o) => o.useRiskInsights))), this.configService.getFeatureFlag$(FeatureFlag.SecurityTasks), - ]).pipe(map(([atLeastOneOrgEnabled, flagEnabled]) => atLeastOneOrgEnabled && flagEnabled)); + ]).pipe( + map(([atLeastOneOrgEnabled, flagEnabled]) => atLeastOneOrgEnabled && flagEnabled), + distinctUntilChanged(), + ); }); tasks$ = perUserCache$((userId) => { - return this.taskState(userId).state$.pipe( - switchMap(async (tasks) => { - if (tasks == null) { - await this.fetchTasksFromApi(userId); - return null; + return this.tasksEnabled$(userId).pipe( + switchMap((enabled) => { + if (!enabled) { + return of([]); } - return tasks; + return this.taskState(userId).state$.pipe( + switchMap(async (tasks) => { + if (tasks == null) { + await this.fetchTasksFromApi(userId); + return null; + } + return tasks; + }), + filterOutNullish(), + map((tasks) => tasks.map((t) => new SecurityTask(t))), + ); }), - filterOutNullish(), - map((tasks) => tasks.map((t) => new SecurityTask(t))), ); }); diff --git a/libs/vault/src/cipher-view/cipher-view.component.html b/libs/vault/src/cipher-view/cipher-view.component.html index 99f1b1e47f5..68edd265d09 100644 --- a/libs/vault/src/cipher-view/cipher-view.component.html +++ b/libs/vault/src/cipher-view/cipher-view.component.html @@ -3,19 +3,18 @@ {{ "cardExpiredMessage" | i18n }} - - - - - {{ "changeAtRiskPassword" | i18n }} - - - - + + + + {{ "changeAtRiskPassword" | i18n }} + + + +

      diff --git a/libs/vault/src/cipher-view/cipher-view.component.ts b/libs/vault/src/cipher-view/cipher-view.component.ts index e748fa46656..cf78e78a65f 100644 --- a/libs/vault/src/cipher-view/cipher-view.component.ts +++ b/libs/vault/src/cipher-view/cipher-view.component.ts @@ -12,12 +12,12 @@ import { Organization } from "@bitwarden/common/admin-console/models/domain/orga import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { isCardExpired } from "@bitwarden/common/autofill/utils"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { CipherId, CollectionId, EmergencyAccessId, UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; +import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; import { SecurityTaskType, TaskService } from "@bitwarden/common/vault/tasks"; @@ -80,7 +80,6 @@ export class CipherViewComponent implements OnChanges, OnDestroy { private destroyed$: Subject = new Subject(); cardIsExpired: boolean = false; hadPendingChangePasswordTask: boolean = false; - isSecurityTasksEnabled$ = this.configService.getFeatureFlag$(FeatureFlag.SecurityTasks); constructor( private organizationService: OrganizationService, @@ -90,8 +89,8 @@ export class CipherViewComponent implements OnChanges, OnDestroy { private defaultTaskService: TaskService, private platformUtilsService: PlatformUtilsService, private changeLoginPasswordService: ChangeLoginPasswordService, - private configService: ConfigService, private cipherService: CipherService, + private logService: LogService, ) {} async ngOnChanges() { @@ -158,20 +157,15 @@ export class CipherViewComponent implements OnChanges, OnDestroy { const userId = await firstValueFrom(this.activeUserId$); - // Show Tasks for Manage and Edit permissions - // Using cipherService to see if user has access to cipher in a non-AC context to address with Edit Except Password permissions - const allCiphers = await firstValueFrom(this.cipherService.ciphers$(userId)); - const cipherServiceCipher = allCiphers[this.cipher?.id as CipherId]; - - if (cipherServiceCipher?.edit && cipherServiceCipher?.viewPassword) { - await this.checkPendingChangePasswordTasks(userId); - } - - if (this.cipher.organizationId && userId) { + if (this.cipher.organizationId) { this.organization$ = this.organizationService .organizations$(userId) .pipe(getOrganizationById(this.cipher.organizationId)) .pipe(takeUntil(this.destroyed$)); + + if (this.cipher.type === CipherType.Login) { + await this.checkPendingChangePasswordTasks(userId); + } } if (this.cipher.folderId) { @@ -182,17 +176,28 @@ export class CipherViewComponent implements OnChanges, OnDestroy { } async checkPendingChangePasswordTasks(userId: UserId): Promise { - if (!(await firstValueFrom(this.isSecurityTasksEnabled$))) { - return; + try { + // Show Tasks for Manage and Edit permissions + // Using cipherService to see if user has access to cipher in a non-AC context to address with Edit Except Password permissions + const allCiphers = await firstValueFrom(this.cipherService.ciphers$(userId)); + const cipherServiceCipher = allCiphers[this.cipher?.id as CipherId]; + + if (!cipherServiceCipher?.edit || !cipherServiceCipher?.viewPassword) { + this.hadPendingChangePasswordTask = false; + return; + } + + const tasks = await firstValueFrom(this.defaultTaskService.pendingTasks$(userId)); + + this.hadPendingChangePasswordTask = tasks?.some((task) => { + return ( + task.cipherId === this.cipher?.id && task.type === SecurityTaskType.UpdateAtRiskCredential + ); + }); + } catch (error) { + this.hadPendingChangePasswordTask = false; + this.logService.error("Failed to retrieve change password tasks for cipher", error); } - - const tasks = await firstValueFrom(this.defaultTaskService.pendingTasks$(userId)); - - this.hadPendingChangePasswordTask = tasks?.some((task) => { - return ( - task.cipherId === this.cipher?.id && task.type === SecurityTaskType.UpdateAtRiskCredential - ); - }); } launchChangePassword = async () => { From 8ead45f5344d13ae5667d9b97d529675b9037f07 Mon Sep 17 00:00:00 2001 From: Miles Blackwood Date: Thu, 1 May 2025 14:37:24 -0400 Subject: [PATCH 040/116] PM-17391 (#14323) * Establish patterns needing addressing. * Prevent non-password field misidentification (TOTP as username) * Allow 'tel' type for TOTP fill identification * Resolve todo and document. * Remove duplicated line. * Use account creation field type = totp to determine if focused field receives the inline menu for totp. Handles cases where totp can appear alongside login forms and also ensures general cipher and totp cipher inline menus render distinctly when focus changes between the field types necessitating. * Prevent type edge cases. --- .../autofill/background/overlay.background.ts | 26 ++-- .../autofill/enums/autofill-overlay.enum.ts | 1 + .../autofill-overlay-content.service.ts | 5 + .../src/autofill/services/autofill.service.ts | 119 +++++++++--------- 4 files changed, 84 insertions(+), 67 deletions(-) diff --git a/apps/browser/src/autofill/background/overlay.background.ts b/apps/browser/src/autofill/background/overlay.background.ts index 4e2e773a0c7..ab5dd4abb8f 100644 --- a/apps/browser/src/autofill/background/overlay.background.ts +++ b/apps/browser/src/autofill/background/overlay.background.ts @@ -661,20 +661,23 @@ export class OverlayBackground implements OverlayBackgroundInterface { return this.inlineMenuFido2Credentials.has(credentialId); } + /** + * When focused field data contains account creation field type of totp + * and there are totp fields in the current frame for page details return true + * + * @returns boolean + */ private isTotpFieldForCurrentField(): boolean { if (!this.focusedFieldData) { return false; } - const { tabId, frameId } = this.focusedFieldData; - const pageDetailsMap = this.pageDetailsForTab[tabId]; - if (!pageDetailsMap || !pageDetailsMap.has(frameId)) { + const totpFields = this.getTotpFields(); + if (!totpFields) { return false; } - const pageDetail = pageDetailsMap.get(frameId); return ( - pageDetail?.details?.fields?.every((field) => - this.inlineMenuFieldQualificationService.isTotpField(field), - ) || false + totpFields.length > 0 && + this.focusedFieldData?.accountCreationFieldType === InlineMenuAccountCreationFieldType.Totp ); } @@ -1399,7 +1402,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { const pageDetailsMap = this.pageDetailsForTab[currentTabId]; const pageDetails = pageDetailsMap?.get(currentFrameId); - const fields = pageDetails.details.fields; + const fields = pageDetails?.details?.fields || []; const totpFields = fields.filter((f) => this.inlineMenuFieldQualificationService.isTotpField(f), ); @@ -1679,7 +1682,12 @@ export class OverlayBackground implements OverlayBackgroundInterface { !this.focusedFieldMatchesFillType( focusedFieldData?.inlineMenuFillType, previousFocusedFieldData, - ) + ) || + // a TOTP field was just focused to - or unfocused from — a non-TOTP field + // may want to generalize this logic if cipher inline menu types exceed [general cipher, TOTP] + [focusedFieldData, previousFocusedFieldData].filter( + (fd) => fd?.accountCreationFieldType === InlineMenuAccountCreationFieldType.Totp, + ).length === 1 ) { const updateAllCipherTypes = !this.focusedFieldMatchesFillType( CipherType.Login, diff --git a/apps/browser/src/autofill/enums/autofill-overlay.enum.ts b/apps/browser/src/autofill/enums/autofill-overlay.enum.ts index 66ad0da546d..9cc457f3c1a 100644 --- a/apps/browser/src/autofill/enums/autofill-overlay.enum.ts +++ b/apps/browser/src/autofill/enums/autofill-overlay.enum.ts @@ -32,6 +32,7 @@ export const InlineMenuAccountCreationFieldType = { Text: "text", Email: "email", Password: "password", + Totp: "totp", } as const; export type InlineMenuAccountCreationFieldTypes = diff --git a/apps/browser/src/autofill/services/autofill-overlay-content.service.ts b/apps/browser/src/autofill/services/autofill-overlay-content.service.ts index 55260cc1149..dc8f45d104b 100644 --- a/apps/browser/src/autofill/services/autofill-overlay-content.service.ts +++ b/apps/browser/src/autofill/services/autofill-overlay-content.service.ts @@ -1128,6 +1128,11 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ * @param autofillFieldData - Autofill field data captured from the form field element. */ private qualifyAccountCreationFieldType(autofillFieldData: AutofillField) { + if (this.inlineMenuFieldQualificationService.isTotpField(autofillFieldData)) { + autofillFieldData.accountCreationFieldType = InlineMenuAccountCreationFieldType.Totp; + return; + } + if (!this.inlineMenuFieldQualificationService.isUsernameField(autofillFieldData)) { autofillFieldData.accountCreationFieldType = InlineMenuAccountCreationFieldType.Password; return; diff --git a/apps/browser/src/autofill/services/autofill.service.ts b/apps/browser/src/autofill/services/autofill.service.ts index 0423078fd1c..da46ceb0864 100644 --- a/apps/browser/src/autofill/services/autofill.service.ts +++ b/apps/browser/src/autofill/services/autofill.service.ts @@ -931,28 +931,37 @@ export default class AutofillService implements AutofillServiceInterface { } if (!passwordFields.length) { - // No password fields on this page. Let's try to just fuzzy fill the username. - pageDetails.fields.forEach((f) => { - if ( - !options.skipUsernameOnlyFill && - f.viewable && - (f.type === "text" || f.type === "email" || f.type === "tel") && - AutofillService.fieldIsFuzzyMatch(f, AutoFillConstants.UsernameFieldNames) - ) { - usernames.push(f); + // If there are no passwords, username or TOTP fields may be present. + // username and TOTP fields are mutually exclusive + pageDetails.fields.forEach((field) => { + if (!field.viewable) { + return; } - if ( + const isFillableTotpField = options.allowTotpAutofill && - f.viewable && - (f.type === "text" || f.type === "number") && - (AutofillService.fieldIsFuzzyMatch(f, [ + ["number", "tel", "text"].some((t) => t === field.type) && + (AutofillService.fieldIsFuzzyMatch(field, [ ...AutoFillConstants.TotpFieldNames, ...AutoFillConstants.AmbiguousTotpFieldNames, ]) || - f.autoCompleteType === "one-time-code") - ) { - totps.push(f); + field.autoCompleteType === "one-time-code"); + + const isFillableUsernameField = + !options.skipUsernameOnlyFill && + ["email", "tel", "text"].some((t) => t === field.type) && + AutofillService.fieldIsFuzzyMatch(field, AutoFillConstants.UsernameFieldNames); + + // Prefer more uniquely keyworded fields first. + switch (true) { + case isFillableTotpField: + totps.push(field); + return; + case isFillableUsernameField: + usernames.push(field); + return; + default: + return; } }); } @@ -2903,52 +2912,46 @@ export default class AutofillService implements AutofillServiceInterface { /** * Accepts a field and returns true if the field contains a * value that matches any of the names in the provided list. + * + * Returns boolean and attr of value that was matched as a tuple if showMatch is set to true. + * * @param {AutofillField} field * @param {string[]} names - * @returns {boolean} + * @param {boolean} showMatch + * @returns {boolean | [boolean, { attr: string; value: string }?]} */ - static fieldIsFuzzyMatch(field: AutofillField, names: string[]): boolean { - if (AutofillService.hasValue(field.htmlID) && this.fuzzyMatch(names, field.htmlID)) { - return true; - } - if (AutofillService.hasValue(field.htmlName) && this.fuzzyMatch(names, field.htmlName)) { - return true; - } - if ( - AutofillService.hasValue(field["label-tag"]) && - this.fuzzyMatch(names, field["label-tag"]) - ) { - return true; - } - if (AutofillService.hasValue(field.placeholder) && this.fuzzyMatch(names, field.placeholder)) { - return true; - } - if ( - AutofillService.hasValue(field["label-left"]) && - this.fuzzyMatch(names, field["label-left"]) - ) { - return true; - } - if ( - AutofillService.hasValue(field["label-top"]) && - this.fuzzyMatch(names, field["label-top"]) - ) { - return true; - } - if ( - AutofillService.hasValue(field["label-aria"]) && - this.fuzzyMatch(names, field["label-aria"]) - ) { - return true; - } - if ( - AutofillService.hasValue(field.dataSetValues) && - this.fuzzyMatch(names, field.dataSetValues) - ) { - return true; - } + static fieldIsFuzzyMatch( + field: AutofillField, + names: string[], + showMatch: true, + ): [boolean, { attr: string; value: string }?]; + static fieldIsFuzzyMatch(field: AutofillField, names: string[]): boolean; + static fieldIsFuzzyMatch( + field: AutofillField, + names: string[], + showMatch: boolean = false, + ): boolean | [boolean, { attr: string; value: string }?] { + const attrs = [ + "htmlID", + "htmlName", + "label-tag", + "placeholder", + "label-left", + "label-top", + "label-aria", + "dataSetValues", + ]; - return false; + for (const attr of attrs) { + const value = field[attr]; + if (!AutofillService.hasValue(value)) { + continue; + } + if (this.fuzzyMatch(names, value)) { + return showMatch ? [true, { attr, value }] : true; + } + } + return showMatch ? [false] : false; } /** From 4292744ca85248d18719a4bba2f1b1e95ae1daa5 Mon Sep 17 00:00:00 2001 From: Brandon Treston Date: Thu, 1 May 2025 15:20:22 -0400 Subject: [PATCH 041/116] remove deprecated component (#14582) --- .../manage/user-add-edit.component.html | 124 ----------------- .../manage/user-add-edit.component.ts | 125 ------------------ .../providers/providers.module.ts | 2 - 3 files changed, 251 deletions(-) delete mode 100644 bitwarden_license/bit-web/src/app/admin-console/providers/manage/user-add-edit.component.html delete mode 100644 bitwarden_license/bit-web/src/app/admin-console/providers/manage/user-add-edit.component.ts diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/user-add-edit.component.html b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/user-add-edit.component.html deleted file mode 100644 index 78d80d005c9..00000000000 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/user-add-edit.component.html +++ /dev/null @@ -1,124 +0,0 @@ -

      diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/user-add-edit.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/user-add-edit.component.ts deleted file mode 100644 index 82671a7e418..00000000000 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/user-add-edit.component.ts +++ /dev/null @@ -1,125 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core"; - -import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { ProviderUserType } from "@bitwarden/common/admin-console/enums"; -import { PermissionsApi } from "@bitwarden/common/admin-console/models/api/permissions.api"; -import { ProviderUserInviteRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-user-invite.request"; -import { ProviderUserUpdateRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-user-update.request"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { DialogService, ToastService } from "@bitwarden/components"; - -/** - * @deprecated Please use the {@link MembersDialogComponent} instead. - */ -@Component({ - selector: "provider-user-add-edit", - templateUrl: "user-add-edit.component.html", -}) -export class UserAddEditComponent implements OnInit { - @Input() name: string; - @Input() providerUserId: string; - @Input() providerId: string; - @Output() savedUser = new EventEmitter(); - @Output() deletedUser = new EventEmitter(); - - loading = true; - editMode = false; - title: string; - emails: string; - type: ProviderUserType = ProviderUserType.ServiceUser; - permissions = new PermissionsApi(); - showCustom = false; - access: "all" | "selected" = "selected"; - formPromise: Promise; - deletePromise: Promise; - userType = ProviderUserType; - - constructor( - private apiService: ApiService, - private i18nService: I18nService, - private platformUtilsService: PlatformUtilsService, - private logService: LogService, - private dialogService: DialogService, - private toastService: ToastService, - ) {} - - async ngOnInit() { - this.editMode = this.loading = this.providerUserId != null; - - if (this.editMode) { - this.editMode = true; - this.title = this.i18nService.t("editMember"); - try { - const user = await this.apiService.getProviderUser(this.providerId, this.providerUserId); - this.type = user.type; - } catch (e) { - this.logService.error(e); - } - } else { - this.title = this.i18nService.t("inviteMember"); - } - - this.loading = false; - } - - async submit() { - try { - if (this.editMode) { - const request = new ProviderUserUpdateRequest(); - request.type = this.type; - this.formPromise = this.apiService.putProviderUser( - this.providerId, - this.providerUserId, - request, - ); - } else { - const request = new ProviderUserInviteRequest(); - request.emails = this.emails.trim().split(/\s*,\s*/); - request.type = this.type; - this.formPromise = this.apiService.postProviderUserInvite(this.providerId, request); - } - await this.formPromise; - this.toastService.showToast({ - variant: "success", - title: null, - message: this.i18nService.t(this.editMode ? "editedUserId" : "invitedUsers", this.name), - }); - this.savedUser.emit(); - } catch (e) { - this.logService.error(e); - } - } - - async delete() { - if (!this.editMode) { - return; - } - - const confirmed = await this.dialogService.openSimpleDialog({ - title: this.name, - content: { key: "removeUserConfirmation" }, - type: "warning", - }); - - if (!confirmed) { - return false; - } - - try { - this.deletePromise = this.apiService.deleteProviderUser(this.providerId, this.providerUserId); - await this.deletePromise; - this.toastService.showToast({ - variant: "success", - title: null, - message: this.i18nService.t("removedUserId", this.name), - }); - this.deletedUser.emit(); - } catch (e) { - this.logService.error(e); - } - } -} diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/providers.module.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/providers.module.ts index 1c15812edc8..597acb0d4f0 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/providers.module.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/providers.module.ts @@ -30,7 +30,6 @@ import { BulkConfirmDialogComponent } from "./manage/dialogs/bulk-confirm-dialog import { BulkRemoveDialogComponent } from "./manage/dialogs/bulk-remove-dialog.component"; import { EventsComponent } from "./manage/events.component"; import { MembersComponent } from "./manage/members.component"; -import { UserAddEditComponent } from "./manage/user-add-edit.component"; import { ProvidersLayoutComponent } from "./providers-layout.component"; import { ProvidersRoutingModule } from "./providers-routing.module"; import { ProvidersComponent } from "./providers.component"; @@ -67,7 +66,6 @@ import { VerifyRecoverDeleteProviderComponent } from "./verify-recover-delete-pr MembersComponent, SetupComponent, SetupProviderComponent, - UserAddEditComponent, AddEditMemberDialogComponent, AddExistingOrganizationDialogComponent, CreateClientDialogComponent, From 23e7f120fd67ea8bf8ff554cd5e23b24a3ef4717 Mon Sep 17 00:00:00 2001 From: Jordan Aasen <166539328+jaasen-livefront@users.noreply.github.com> Date: Thu, 1 May 2025 13:03:11 -0700 Subject: [PATCH 042/116] [PM-19579] - add sshKey option to desktop (#14402) * add sshKey option to desktop * fix add new sshKey * revert removal of catch * revert removal of catch --- apps/desktop/src/main/menu/menu.file.ts | 6 ++++ .../app/vault/vault-items-v2.component.html | 4 +++ .../vault/app/vault/vault-v2.component.html | 1 - .../src/vault/app/vault/vault-v2.component.ts | 31 ++++++------------- .../src/vault/app/vault/vault.component.ts | 11 +++++++ 5 files changed, 31 insertions(+), 22 deletions(-) diff --git a/apps/desktop/src/main/menu/menu.file.ts b/apps/desktop/src/main/menu/menu.file.ts index 712e579515e..25db5b695e7 100644 --- a/apps/desktop/src/main/menu/menu.file.ts +++ b/apps/desktop/src/main/menu/menu.file.ts @@ -103,6 +103,12 @@ export class FileMenu extends FirstMenu implements IMenubarMenu { click: () => this.sendMessage("newSecureNote"), accelerator: "CmdOrCtrl+Shift+S", }, + { + id: "typeSshKey", + label: this.localize("typeSshKey"), + click: () => this.sendMessage("newSshKey"), + accelerator: "CmdOrCtrl+Shift+K", + }, ]; } diff --git a/apps/desktop/src/vault/app/vault/vault-items-v2.component.html b/apps/desktop/src/vault/app/vault/vault-items-v2.component.html index ff35e00fb0f..63e648e3cf3 100644 --- a/apps/desktop/src/vault/app/vault/vault-items-v2.component.html +++ b/apps/desktop/src/vault/app/vault/vault-items-v2.component.html @@ -88,5 +88,9 @@ {{ "typeSecureNote" | i18n }} + diff --git a/apps/desktop/src/vault/app/vault/vault-v2.component.html b/apps/desktop/src/vault/app/vault/vault-v2.component.html index 12f52502984..00e225f41d1 100644 --- a/apps/desktop/src/vault/app/vault/vault-v2.component.html +++ b/apps/desktop/src/vault/app/vault/vault-v2.component.html @@ -6,7 +6,6 @@ (onCipherClicked)="viewCipher($event)" (onCipherRightClicked)="viewCipherMenu($event)" (onAddCipher)="addCipher($event)" - (onAddCipherOptions)="addCipherOptions()" >
      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 7e799899418..05c6c5e261e 100644 --- a/apps/desktop/src/vault/app/vault/vault-v2.component.ts +++ b/apps/desktop/src/vault/app/vault/vault-v2.component.ts @@ -208,6 +208,9 @@ export class VaultV2Component implements OnInit, OnDestroy { case "newSecureNote": await this.addCipher(CipherType.SecureNote).catch(() => {}); break; + case "newSshKey": + await this.addCipher(CipherType.SshKey).catch(() => {}); + break; case "focusSearch": (document.querySelector("#search") as HTMLInputElement)?.select(); detectChanges = false; @@ -531,28 +534,14 @@ export class VaultV2Component implements OnInit, OnDestroy { this.action = "add"; this.prefillCipherFromFilter(); await this.go().catch(() => {}); - } - addCipherOptions() { - const menu: RendererMenuItem[] = [ - { - label: this.i18nService.t("typeLogin"), - click: () => this.addCipherWithChangeDetection(CipherType.Login), - }, - { - label: this.i18nService.t("typeCard"), - click: () => this.addCipherWithChangeDetection(CipherType.Card), - }, - { - label: this.i18nService.t("typeIdentity"), - click: () => this.addCipherWithChangeDetection(CipherType.Identity), - }, - { - label: this.i18nService.t("typeSecureNote"), - click: () => this.addCipherWithChangeDetection(CipherType.SecureNote), - }, - ]; - invokeMenu(menu); + if (type === CipherType.SshKey) { + this.toastService.showToast({ + variant: "success", + title: "", + message: this.i18nService.t("sshKeyGenerated"), + }); + } } async savedCipher(cipher: CipherView) { diff --git a/apps/desktop/src/vault/app/vault/vault.component.ts b/apps/desktop/src/vault/app/vault/vault.component.ts index a21a285a428..6c0d5ef81d0 100644 --- a/apps/desktop/src/vault/app/vault/vault.component.ts +++ b/apps/desktop/src/vault/app/vault/vault.component.ts @@ -151,6 +151,9 @@ export class VaultComponent implements OnInit, OnDestroy { case "newSecureNote": await this.addCipher(CipherType.SecureNote); break; + case "newSshKey": + await this.addCipher(CipherType.SshKey); + break; case "focusSearch": (document.querySelector("#search") as HTMLInputElement).select(); detectChanges = false; @@ -470,6 +473,14 @@ export class VaultComponent implements OnInit, OnDestroy { this.cipherId = null; this.prefillNewCipherFromFilter(); this.go(); + + if (type === CipherType.SshKey) { + this.toastService.showToast({ + variant: "success", + title: "", + message: this.i18nService.t("sshKeyGenerated"), + }); + } } addCipherOptions() { From c8d6e4899636e1084e68a3658ebb6db3f9ab28f8 Mon Sep 17 00:00:00 2001 From: Alex Morask <144709477+amorask-bitwarden@users.noreply.github.com> Date: Thu, 1 May 2025 17:13:18 -0400 Subject: [PATCH 043/116] [PM-18955] Use `OrganizationWarningsService` on AC Collections Page behind FF (#14437) * Add getWarnings to OrganizationBillingApiService * Add OrganizationWarningsService * Add feature flag * Add standalone warning components that consume new service * Add new components to AC collections vault when FF is enabled * Add OrganizationWarningsService spec * Run prettier on spec file * Thomas' feedback --- .../collections/vault.component.html | 15 +- .../collections/vault.component.ts | 47 ++- .../organization-warnings.service.spec.ts | 356 ++++++++++++++++++ .../services/organization-warnings.service.ts | 201 ++++++++++ .../warnings/free-trial-warning.component.ts | 56 +++ .../reseller-renewal-warning.component.ts | 45 +++ ...ization-billing-api.service.abstraction.ts | 4 + .../organization-warnings.response.ts | 97 +++++ .../organization-billing-api.service.ts | 14 + libs/common/src/enums/feature-flag.enum.ts | 2 + 10 files changed, 828 insertions(+), 9 deletions(-) create mode 100644 apps/web/src/app/billing/services/organization-warnings.service.spec.ts create mode 100644 apps/web/src/app/billing/services/organization-warnings.service.ts create mode 100644 apps/web/src/app/billing/warnings/free-trial-warning.component.ts create mode 100644 apps/web/src/app/billing/warnings/reseller-renewal-warning.component.ts create mode 100644 libs/common/src/billing/models/response/organization-warnings.response.ts diff --git a/apps/web/src/app/admin-console/organizations/collections/vault.component.html b/apps/web/src/app/admin-console/organizations/collections/vault.component.html index 604d326bf37..22da9a566f4 100644 --- a/apps/web/src/app/admin-console/organizations/collections/vault.component.html +++ b/apps/web/src/app/admin-console/organizations/collections/vault.component.html @@ -1,4 +1,15 @@ - + + + + + - + (false); protected currentSearchText$: Observable; - protected freeTrial$: Observable; - protected resellerWarning$: Observable; + protected useOrganizationWarningsService$: Observable; + protected freeTrialWhenWarningsServiceDisabled$: Observable; + protected resellerWarningWhenWarningsServiceDisabled$: Observable; protected prevCipherId: string | null = null; protected userId: UserId; /** @@ -255,6 +262,7 @@ export class VaultComponent implements OnInit, OnDestroy { private resellerWarningService: ResellerWarningService, private accountService: AccountService, private billingNotificationService: BillingNotificationService, + private organizationWarningsService: OrganizationWarningsService, ) {} async ngOnInit() { @@ -628,9 +636,23 @@ export class VaultComponent implements OnInit, OnDestroy { ) .subscribe(); - this.unpaidSubscriptionDialog$.pipe(takeUntil(this.destroy$)).subscribe(); + // Billing Warnings + this.useOrganizationWarningsService$ = this.configService.getFeatureFlag$( + FeatureFlag.UseOrganizationWarningsService, + ); - this.freeTrial$ = combineLatest([ + this.useOrganizationWarningsService$ + .pipe( + switchMap((enabled) => + enabled + ? this.organizationWarningsService.showInactiveSubscriptionDialog$(this.organization) + : this.unpaidSubscriptionDialog$, + ), + takeUntil(this.destroy$), + ) + .subscribe(); + + const freeTrial$ = combineLatest([ organization$, this.hasSubscription$.pipe(filter((hasSubscription) => hasSubscription !== null)), ]).pipe( @@ -655,7 +677,12 @@ export class VaultComponent implements OnInit, OnDestroy { filter((result) => result !== null), ); - this.resellerWarning$ = organization$.pipe( + this.freeTrialWhenWarningsServiceDisabled$ = this.useOrganizationWarningsService$.pipe( + filter((enabled) => !enabled), + switchMap(() => freeTrial$), + ); + + const resellerWarning$ = organization$.pipe( filter((org) => org.isOwner), switchMap((org) => from(this.billingApiService.getOrganizationBillingMetadata(org.id)).pipe( @@ -665,6 +692,12 @@ export class VaultComponent implements OnInit, OnDestroy { map(({ org, metadata }) => this.resellerWarningService.getWarning(org, metadata)), ); + this.resellerWarningWhenWarningsServiceDisabled$ = this.useOrganizationWarningsService$.pipe( + filter((enabled) => !enabled), + switchMap(() => resellerWarning$), + ); + // End Billing Warnings + firstSetup$ .pipe( switchMap(() => this.refresh$), diff --git a/apps/web/src/app/billing/services/organization-warnings.service.spec.ts b/apps/web/src/app/billing/services/organization-warnings.service.spec.ts new file mode 100644 index 00000000000..88c571e2d67 --- /dev/null +++ b/apps/web/src/app/billing/services/organization-warnings.service.spec.ts @@ -0,0 +1,356 @@ +import { Router } from "@angular/router"; +import { mock, MockProxy } from "jest-mock-extended"; +import { firstValueFrom, lastValueFrom } from "rxjs"; + +import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; +import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { OrganizationBillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/organizations/organization-billing-api.service.abstraction"; +import { OrganizationWarningsResponse } from "@bitwarden/common/billing/models/response/organization-warnings.response"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { DialogService, SimpleDialogOptions } from "@bitwarden/components"; + +import { OrganizationWarningsService } from "./organization-warnings.service"; + +describe("OrganizationWarningsService", () => { + let dialogService: MockProxy; + let i18nService: MockProxy; + let organizationApiService: MockProxy; + let organizationBillingApiService: MockProxy; + let router: MockProxy; + + let organizationWarningsService: OrganizationWarningsService; + + const respond = (responseBody: any) => + Promise.resolve(new OrganizationWarningsResponse(responseBody)); + + const empty = () => Promise.resolve(new OrganizationWarningsResponse({})); + + beforeEach(() => { + dialogService = mock(); + i18nService = mock(); + organizationApiService = mock(); + organizationBillingApiService = mock(); + router = mock(); + + organizationWarningsService = new OrganizationWarningsService( + dialogService, + i18nService, + organizationApiService, + organizationBillingApiService, + router, + ); + }); + + describe("cache$", () => { + it("should only request warnings once for a specific organization and replay the cached result for multiple subscriptions", async () => { + const response1 = respond({ + freeTrial: { + remainingTrialDays: 1, + }, + }); + + const organization1 = { + id: "1", + name: "Test", + } as Organization; + + const response2 = respond({ + freeTrial: { + remainingTrialDays: 2, + }, + }); + + const organization2 = { + id: "2", + name: "Test", + } as Organization; + + organizationBillingApiService.getWarnings.mockImplementation((id) => { + if (id === organization1.id) { + return response1; + } + + if (id === organization2.id) { + return response2; + } + + return empty(); + }); + + const oneDayRemainingTranslation = "oneDayRemaining"; + const twoDaysRemainingTranslation = "twoDaysRemaining"; + + i18nService.t.mockImplementation((id, p1) => { + if (id === "freeTrialEndPromptTomorrowNoOrgName") { + return oneDayRemainingTranslation; + } + + if (id === "freeTrialEndPromptCount" && p1 === 2) { + return twoDaysRemainingTranslation; + } + + return ""; + }); + + const organization1Subscription1 = await firstValueFrom( + organizationWarningsService.getFreeTrialWarning$(organization1), + ); + + const organization1Subscription2 = await firstValueFrom( + organizationWarningsService.getFreeTrialWarning$(organization1), + ); + + expect(organization1Subscription1).toEqual({ + organization: organization1, + message: oneDayRemainingTranslation, + }); + + expect(organization1Subscription2).toEqual(organization1Subscription1); + + const organization2Subscription1 = await firstValueFrom( + organizationWarningsService.getFreeTrialWarning$(organization2), + ); + + const organization2Subscription2 = await firstValueFrom( + organizationWarningsService.getFreeTrialWarning$(organization2), + ); + + expect(organization2Subscription1).toEqual({ + organization: organization2, + message: twoDaysRemainingTranslation, + }); + + expect(organization2Subscription2).toEqual(organization2Subscription1); + + expect(organizationBillingApiService.getWarnings).toHaveBeenCalledTimes(2); + }); + }); + + describe("getFreeTrialWarning$", () => { + it("should not emit a free trial warning when none is included in the warnings response", (done) => { + const organization = { + id: "1", + name: "Test", + } as Organization; + + organizationBillingApiService.getWarnings.mockReturnValue(empty()); + + const warning$ = organizationWarningsService.getFreeTrialWarning$(organization); + + warning$.subscribe({ + next: () => { + fail("Observable should not emit a value."); + }, + complete: () => { + done(); + }, + }); + }); + + it("should emit a free trial warning when one is included in the warnings response", async () => { + const response = respond({ + freeTrial: { + remainingTrialDays: 1, + }, + }); + + const organization = { + id: "1", + name: "Test", + } as Organization; + + organizationBillingApiService.getWarnings.mockImplementation((id) => { + if (id === organization.id) { + return response; + } else { + return empty(); + } + }); + + const translation = "translation"; + i18nService.t.mockImplementation((id) => { + if (id === "freeTrialEndPromptTomorrowNoOrgName") { + return translation; + } else { + return ""; + } + }); + + const warning = await firstValueFrom( + organizationWarningsService.getFreeTrialWarning$(organization), + ); + + expect(warning).toEqual({ + organization, + message: translation, + }); + }); + }); + + describe("getResellerRenewalWarning$", () => { + it("should not emit a reseller renewal warning when none is included in the warnings response", (done) => { + const organization = { + id: "1", + name: "Test", + } as Organization; + + organizationBillingApiService.getWarnings.mockReturnValue(empty()); + + const warning$ = organizationWarningsService.getResellerRenewalWarning$(organization); + + warning$.subscribe({ + next: () => { + fail("Observable should not emit a value."); + }, + complete: () => { + done(); + }, + }); + }); + + it("should emit a reseller renewal warning when one is included in the warnings response", async () => { + const response = respond({ + resellerRenewal: { + type: "upcoming", + upcoming: { + renewalDate: "2026-01-01T00:00:00.000Z", + }, + }, + }); + + const organization = { + id: "1", + name: "Test", + providerName: "Provider", + } as Organization; + + organizationBillingApiService.getWarnings.mockImplementation((id) => { + if (id === organization.id) { + return response; + } else { + return empty(); + } + }); + + const formattedDate = new Date("2026-01-01T00:00:00.000Z").toLocaleDateString("en-US", { + month: "short", + day: "2-digit", + year: "numeric", + }); + + const translation = "translation"; + i18nService.t.mockImplementation((id, p1, p2) => { + if ( + id === "resellerRenewalWarningMsg" && + p1 === organization.providerName && + p2 === formattedDate + ) { + return translation; + } else { + return ""; + } + }); + + const warning = await firstValueFrom( + organizationWarningsService.getResellerRenewalWarning$(organization), + ); + + expect(warning).toEqual({ + type: "info", + message: translation, + }); + }); + }); + + describe("showInactiveSubscriptionDialog$", () => { + it("should not emit the opening of a dialog for an inactive subscription warning when the warning is not included in the warnings response", (done) => { + const organization = { + id: "1", + name: "Test", + } as Organization; + + organizationBillingApiService.getWarnings.mockReturnValue(empty()); + + const warning$ = organizationWarningsService.showInactiveSubscriptionDialog$(organization); + + warning$.subscribe({ + next: () => { + fail("Observable should not emit a value."); + }, + complete: () => { + done(); + }, + }); + }); + + it("should emit the opening of a dialog for an inactive subscription warning when the warning is included in the warnings response", async () => { + const response = respond({ + inactiveSubscription: { + resolution: "add_payment_method", + }, + }); + + const organization = { + id: "1", + name: "Test", + providerName: "Provider", + } as Organization; + + organizationBillingApiService.getWarnings.mockImplementation((id) => { + if (id === organization.id) { + return response; + } else { + return empty(); + } + }); + + const titleTranslation = "title"; + const continueTranslation = "continue"; + const closeTranslation = "close"; + + i18nService.t.mockImplementation((id, param) => { + if (id === "suspendedOrganizationTitle" && param === organization.name) { + return titleTranslation; + } + if (id === "continue") { + return continueTranslation; + } + if (id === "close") { + return closeTranslation; + } + return ""; + }); + + const expectedOptions = { + title: titleTranslation, + content: { + key: "suspendedOwnerOrgMessage", + }, + type: "danger", + acceptButtonText: continueTranslation, + cancelButtonText: closeTranslation, + } as SimpleDialogOptions; + + dialogService.openSimpleDialog.mockImplementation((options) => { + if (JSON.stringify(options) == JSON.stringify(expectedOptions)) { + return Promise.resolve(true); + } else { + return Promise.resolve(false); + } + }); + + const observable$ = organizationWarningsService.showInactiveSubscriptionDialog$(organization); + + const routerNavigateSpy = jest.spyOn(router, "navigate").mockResolvedValue(true); + + await lastValueFrom(observable$); + + expect(routerNavigateSpy).toHaveBeenCalledWith( + ["organizations", `${organization.id}`, "billing", "payment-method"], + { + state: { launchPaymentModalAutomatically: true }, + }, + ); + }); + }); +}); diff --git a/apps/web/src/app/billing/services/organization-warnings.service.ts b/apps/web/src/app/billing/services/organization-warnings.service.ts new file mode 100644 index 00000000000..f75220a7744 --- /dev/null +++ b/apps/web/src/app/billing/services/organization-warnings.service.ts @@ -0,0 +1,201 @@ +import { Injectable } from "@angular/core"; +import { Router } from "@angular/router"; +import { + filter, + from, + lastValueFrom, + map, + Observable, + shareReplay, + switchMap, + takeWhile, +} from "rxjs"; +import { take } from "rxjs/operators"; + +import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; +import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { OrganizationBillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/organizations/organization-billing-api.service.abstraction"; +import { OrganizationWarningsResponse } from "@bitwarden/common/billing/models/response/organization-warnings.response"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { OrganizationId } from "@bitwarden/common/types/guid"; +import { DialogService } from "@bitwarden/components"; +import { openChangePlanDialog } from "@bitwarden/web-vault/app/billing/organizations/change-plan-dialog.component"; + +const format = (date: Date) => + date.toLocaleDateString("en-US", { + month: "short", + day: "2-digit", + year: "numeric", + }); + +export type FreeTrialWarning = { + organization: Pick; + message: string; +}; + +export type ResellerRenewalWarning = { + type: "info" | "warning"; + message: string; +}; + +@Injectable({ providedIn: "root" }) +export class OrganizationWarningsService { + private cache$ = new Map>(); + + constructor( + private dialogService: DialogService, + private i18nService: I18nService, + private organizationApiService: OrganizationApiServiceAbstraction, + private organizationBillingApiService: OrganizationBillingApiServiceAbstraction, + private router: Router, + ) {} + + getFreeTrialWarning$ = (organization: Organization): Observable => + this.getWarning$(organization, (response) => response.freeTrial).pipe( + map((warning) => { + const { remainingTrialDays } = warning; + + if (remainingTrialDays >= 2) { + return { + organization, + message: this.i18nService.t("freeTrialEndPromptCount", remainingTrialDays), + }; + } + + if (remainingTrialDays == 1) { + return { + organization, + message: this.i18nService.t("freeTrialEndPromptTomorrowNoOrgName"), + }; + } + + return { + organization, + message: this.i18nService.t("freeTrialEndingTodayWithoutOrgName"), + }; + }), + ); + + getResellerRenewalWarning$ = (organization: Organization): Observable => + this.getWarning$(organization, (response) => response.resellerRenewal).pipe( + map((warning): ResellerRenewalWarning | null => { + switch (warning.type) { + case "upcoming": { + return { + type: "info", + message: this.i18nService.t( + "resellerRenewalWarningMsg", + organization.providerName, + format(warning.upcoming!.renewalDate), + ), + }; + } + case "issued": { + return { + type: "info", + message: this.i18nService.t( + "resellerOpenInvoiceWarningMgs", + organization.providerName, + format(warning.issued!.issuedDate), + format(warning.issued!.dueDate), + ), + }; + } + case "past_due": { + return { + type: "warning", + message: this.i18nService.t( + "resellerPastDueWarningMsg", + organization.providerName, + format(warning.pastDue!.suspensionDate), + ), + }; + } + } + }), + filter((result): result is NonNullable => result !== null), + ); + + showInactiveSubscriptionDialog$ = (organization: Organization): Observable => + this.getWarning$(organization, (response) => response.inactiveSubscription).pipe( + switchMap(async (warning) => { + switch (warning.resolution) { + case "contact_provider": { + await this.dialogService.openSimpleDialog({ + title: this.i18nService.t("suspendedOrganizationTitle", organization.name), + content: { + key: "suspendedManagedOrgMessage", + placeholders: [organization.providerName], + }, + type: "danger", + acceptButtonText: this.i18nService.t("close"), + cancelButtonText: null, + }); + break; + } + case "add_payment_method": { + const confirmed = await this.dialogService.openSimpleDialog({ + title: this.i18nService.t("suspendedOrganizationTitle", organization.name), + content: { key: "suspendedOwnerOrgMessage" }, + type: "danger", + acceptButtonText: this.i18nService.t("continue"), + cancelButtonText: this.i18nService.t("close"), + }); + if (confirmed) { + await this.router.navigate( + ["organizations", `${organization.id}`, "billing", "payment-method"], + { + state: { launchPaymentModalAutomatically: true }, + }, + ); + } + break; + } + case "resubscribe": { + const subscription = await this.organizationApiService.getSubscription(organization.id); + const dialogReference = openChangePlanDialog(this.dialogService, { + data: { + organizationId: organization.id, + subscription: subscription, + productTierType: organization.productTierType, + }, + }); + await lastValueFrom(dialogReference.closed); + break; + } + case "contact_owner": { + await this.dialogService.openSimpleDialog({ + title: this.i18nService.t("suspendedOrganizationTitle", organization.name), + content: { key: "suspendedUserOrgMessage" }, + type: "danger", + acceptButtonText: this.i18nService.t("close"), + cancelButtonText: null, + }); + break; + } + } + }), + ); + + private getResponse$ = (organization: Organization): Observable => { + const existing = this.cache$.get(organization.id as OrganizationId); + if (existing) { + return existing; + } + const response$ = from(this.organizationBillingApiService.getWarnings(organization.id)).pipe( + shareReplay({ bufferSize: 1, refCount: false }), + ); + this.cache$.set(organization.id as OrganizationId, response$); + return response$; + }; + + private getWarning$ = ( + organization: Organization, + extract: (response: OrganizationWarningsResponse) => T | null | undefined, + ): Observable => + this.getResponse$(organization).pipe( + map(extract), + takeWhile((warning): warning is T => !!warning), + take(1), + ); +} diff --git a/apps/web/src/app/billing/warnings/free-trial-warning.component.ts b/apps/web/src/app/billing/warnings/free-trial-warning.component.ts new file mode 100644 index 00000000000..e83873e9d6b --- /dev/null +++ b/apps/web/src/app/billing/warnings/free-trial-warning.component.ts @@ -0,0 +1,56 @@ +import { AsyncPipe } from "@angular/common"; +import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core"; +import { Observable } from "rxjs"; + +import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { AnchorLinkDirective, BannerComponent } from "@bitwarden/components"; +import { I18nPipe } from "@bitwarden/ui-common"; + +import { + FreeTrialWarning, + OrganizationWarningsService, +} from "../services/organization-warnings.service"; + +@Component({ + selector: "app-free-trial-warning", + template: ` + @let warning = freeTrialWarning$ | async; + + @if (warning) { + + {{ warning.message }} + + {{ "clickHereToAddPaymentMethod" | i18n }} + + + } + `, + standalone: true, + imports: [AnchorLinkDirective, AsyncPipe, BannerComponent, I18nPipe], +}) +export class FreeTrialWarningComponent implements OnInit { + @Input({ required: true }) organization!: Organization; + @Output() clicked = new EventEmitter(); + + freeTrialWarning$!: Observable; + + constructor(private organizationWarningsService: OrganizationWarningsService) {} + + ngOnInit() { + this.freeTrialWarning$ = this.organizationWarningsService.getFreeTrialWarning$( + this.organization, + ); + } +} diff --git a/apps/web/src/app/billing/warnings/reseller-renewal-warning.component.ts b/apps/web/src/app/billing/warnings/reseller-renewal-warning.component.ts new file mode 100644 index 00000000000..fc94e85e28d --- /dev/null +++ b/apps/web/src/app/billing/warnings/reseller-renewal-warning.component.ts @@ -0,0 +1,45 @@ +import { AsyncPipe } from "@angular/common"; +import { Component, Input, OnInit } from "@angular/core"; +import { Observable } from "rxjs"; + +import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { BannerComponent } from "@bitwarden/components"; + +import { + OrganizationWarningsService, + ResellerRenewalWarning, +} from "../services/organization-warnings.service"; + +@Component({ + selector: "app-reseller-renewal-warning", + template: ` + @let warning = resellerRenewalWarning$ | async; + + @if (warning) { + + {{ warning.message }} + + } + `, + standalone: true, + imports: [AsyncPipe, BannerComponent], +}) +export class ResellerRenewalWarningComponent implements OnInit { + @Input({ required: true }) organization!: Organization; + + resellerRenewalWarning$!: Observable; + + constructor(private organizationWarningsService: OrganizationWarningsService) {} + + ngOnInit() { + this.resellerRenewalWarning$ = this.organizationWarningsService.getResellerRenewalWarning$( + this.organization, + ); + } +} diff --git a/libs/common/src/billing/abstractions/organizations/organization-billing-api.service.abstraction.ts b/libs/common/src/billing/abstractions/organizations/organization-billing-api.service.abstraction.ts index e1f7ad49012..4975da0d7d2 100644 --- a/libs/common/src/billing/abstractions/organizations/organization-billing-api.service.abstraction.ts +++ b/libs/common/src/billing/abstractions/organizations/organization-billing-api.service.abstraction.ts @@ -1,3 +1,5 @@ +import { OrganizationWarningsResponse } from "@bitwarden/common/billing/models/response/organization-warnings.response"; + import { BillingInvoiceResponse, BillingTransactionResponse, @@ -15,6 +17,8 @@ export abstract class OrganizationBillingApiServiceAbstraction { startAfter?: string, ) => Promise; + abstract getWarnings: (id: string) => Promise; + abstract setupBusinessUnit: ( id: string, request: { diff --git a/libs/common/src/billing/models/response/organization-warnings.response.ts b/libs/common/src/billing/models/response/organization-warnings.response.ts new file mode 100644 index 00000000000..ff70298101e --- /dev/null +++ b/libs/common/src/billing/models/response/organization-warnings.response.ts @@ -0,0 +1,97 @@ +import { BaseResponse } from "../../../models/response/base.response"; + +export class OrganizationWarningsResponse extends BaseResponse { + freeTrial?: FreeTrialWarningResponse; + inactiveSubscription?: InactiveSubscriptionWarningResponse; + resellerRenewal?: ResellerRenewalWarningResponse; + + constructor(response: any) { + super(response); + const freeTrialWarning = this.getResponseProperty("FreeTrial"); + if (freeTrialWarning) { + this.freeTrial = new FreeTrialWarningResponse(freeTrialWarning); + } + const inactiveSubscriptionWarning = this.getResponseProperty("InactiveSubscription"); + if (inactiveSubscriptionWarning) { + this.inactiveSubscription = new InactiveSubscriptionWarningResponse( + inactiveSubscriptionWarning, + ); + } + const resellerWarning = this.getResponseProperty("ResellerRenewal"); + if (resellerWarning) { + this.resellerRenewal = new ResellerRenewalWarningResponse(resellerWarning); + } + } +} + +class FreeTrialWarningResponse extends BaseResponse { + remainingTrialDays: number; + + constructor(response: any) { + super(response); + this.remainingTrialDays = this.getResponseProperty("RemainingTrialDays"); + } +} + +class InactiveSubscriptionWarningResponse extends BaseResponse { + resolution: string; + + constructor(response: any) { + super(response); + this.resolution = this.getResponseProperty("Resolution"); + } +} + +class ResellerRenewalWarningResponse extends BaseResponse { + type: "upcoming" | "issued" | "past_due"; + upcoming?: UpcomingRenewal; + issued?: IssuedRenewal; + pastDue?: PastDueRenewal; + + constructor(response: any) { + super(response); + this.type = this.getResponseProperty("Type"); + switch (this.type) { + case "upcoming": { + this.upcoming = new UpcomingRenewal(this.getResponseProperty("Upcoming")); + break; + } + case "issued": { + this.issued = new IssuedRenewal(this.getResponseProperty("Issued")); + break; + } + case "past_due": { + this.pastDue = new PastDueRenewal(this.getResponseProperty("PastDue")); + } + } + } +} + +class UpcomingRenewal extends BaseResponse { + renewalDate: Date; + + constructor(response: any) { + super(response); + this.renewalDate = new Date(this.getResponseProperty("RenewalDate")); + } +} + +class IssuedRenewal extends BaseResponse { + issuedDate: Date; + dueDate: Date; + + constructor(response: any) { + super(response); + this.issuedDate = new Date(this.getResponseProperty("IssuedDate")); + this.dueDate = new Date(this.getResponseProperty("DueDate")); + } +} + +class PastDueRenewal extends BaseResponse { + suspensionDate: Date; + + constructor(response: any) { + super(response); + this.suspensionDate = new Date(this.getResponseProperty("SuspensionDate")); + } +} diff --git a/libs/common/src/billing/services/organization/organization-billing-api.service.ts b/libs/common/src/billing/services/organization/organization-billing-api.service.ts index 405bd41957f..1189316a487 100644 --- a/libs/common/src/billing/services/organization/organization-billing-api.service.ts +++ b/libs/common/src/billing/services/organization/organization-billing-api.service.ts @@ -1,3 +1,5 @@ +import { OrganizationWarningsResponse } from "@bitwarden/common/billing/models/response/organization-warnings.response"; + import { ApiService } from "../../../abstractions/api.service"; import { OrganizationBillingApiServiceAbstraction } from "../../abstractions/organizations/organization-billing-api.service.abstraction"; import { @@ -50,6 +52,18 @@ export class OrganizationBillingApiService implements OrganizationBillingApiServ return r?.map((i: any) => new BillingTransactionResponse(i)) || []; } + async getWarnings(id: string): Promise { + const response = await this.apiService.send( + "GET", + `/organizations/${id}/billing/warnings`, + null, + true, + true, + ); + + return new OrganizationWarningsResponse(response); + } + async setupBusinessUnit( id: string, request: { diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index 3644ceefa9a..3e905a6253c 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -35,6 +35,7 @@ export enum FeatureFlag { PM18794_ProviderPaymentMethod = "pm-18794-provider-payment-method", PM17772_AdminInitiatedSponsorships = "pm-17772-admin-initiated-sponsorships", PM19956_RequireProviderPaymentMethodDuringSetup = "pm-19956-require-provider-payment-method-during-setup", + UseOrganizationWarningsService = "use-organization-warnings-service", /* Data Insights and Reporting */ CriticalApps = "pm-14466-risk-insights-critical-application", @@ -123,6 +124,7 @@ export const DefaultFeatureFlagValue = { [FeatureFlag.PM18794_ProviderPaymentMethod]: FALSE, [FeatureFlag.PM17772_AdminInitiatedSponsorships]: FALSE, [FeatureFlag.PM19956_RequireProviderPaymentMethodDuringSetup]: FALSE, + [FeatureFlag.UseOrganizationWarningsService]: FALSE, /* Key Management */ [FeatureFlag.PrivateKeyRegeneration]: FALSE, From 835516a925304803416a703f81dede6b954f6ee3 Mon Sep 17 00:00:00 2001 From: Maciej Zieniuk <167752252+mzieniukbw@users.noreply.github.com> Date: Fri, 2 May 2025 08:26:48 +0200 Subject: [PATCH 044/116] [PM-20424] [BEEEP] browser client webpack optimisations (#14343) * browser client webpack optimisations * fixing browser extension loading --- apps/browser/webpack.config.js | 35 ++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/apps/browser/webpack.config.js b/apps/browser/webpack.config.js index 4b66ed7d70a..6d9113be7ed 100644 --- a/apps/browser/webpack.config.js +++ b/apps/browser/webpack.config.js @@ -81,6 +81,8 @@ const moduleRules = [ loader: "babel-loader", options: { configFile: "../../babel.config.json", + cacheDirectory: ENV === "development", + compact: ENV !== "development", }, }, ], @@ -205,6 +207,20 @@ const mainConfig = { "content/send-on-installed-message": "./src/vault/content/send-on-installed-message.ts", "content/send-popup-open-message": "./src/vault/content/send-popup-open-message.ts", }, + cache: + ENV !== "development" + ? false + : { + type: "filesystem", + name: "main-cache", + cacheDirectory: path.resolve(__dirname, "../../node_modules/.cache/webpack-browser-main"), + buildDependencies: { + config: [__filename], + }, + }, + snapshot: { + unmanagedPaths: [path.resolve(__dirname, "../../node_modules/@bitwarden/")], + }, optimization: { minimize: ENV !== "development", minimizer: [ @@ -263,6 +279,7 @@ const mainConfig = { fs: false, path: require.resolve("path-browserify"), }, + cache: true, }, output: { filename: "[name].js", @@ -357,6 +374,23 @@ if (manifestVersion == 2) { ], noParse: /argon2(-simd)?\.wasm$/, }, + cache: + ENV !== "development" + ? false + : { + type: "filesystem", + name: "background-cache", + cacheDirectory: path.resolve( + __dirname, + "../../node_modules/.cache/webpack-browser-background", + ), + buildDependencies: { + config: [__filename], + }, + }, + snapshot: { + unmanagedPaths: [path.resolve(__dirname, "../../node_modules/@bitwarden/")], + }, experiments: { asyncWebAssembly: true, }, @@ -369,6 +403,7 @@ if (manifestVersion == 2) { fs: false, path: require.resolve("path-browserify"), }, + cache: true, }, dependencies: ["main"], plugins: [...requiredPlugins], From 2ba022532c7f7a48fb3161a10dd0fae1ff5e675e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 2 May 2025 13:20:46 +0200 Subject: [PATCH 045/116] [deps] Architecture: Update eslint-import-resolver-typescript to v4 (#14509) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package-lock.json | 23 ++++++----------------- package.json | 2 +- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/package-lock.json b/package-lock.json index 25322b844b7..95e44955767 100644 --- a/package-lock.json +++ b/package-lock.json @@ -139,7 +139,7 @@ "electron-updater": "6.3.9", "eslint": "8.57.1", "eslint-config-prettier": "10.1.2", - "eslint-import-resolver-typescript": "3.10.1", + "eslint-import-resolver-typescript": "4.3.4", "eslint-plugin-import": "2.31.0", "eslint-plugin-rxjs": "5.0.3", "eslint-plugin-rxjs-angular": "2.0.1", @@ -8336,16 +8336,6 @@ "node": ">= 8" } }, - "node_modules/@nolyfill/is-core-module": { - "version": "1.0.39", - "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", - "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.4.0" - } - }, "node_modules/@npmcli/agent": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-2.2.2.tgz", @@ -19053,22 +19043,21 @@ } }, "node_modules/eslint-import-resolver-typescript": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", - "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-4.3.4.tgz", + "integrity": "sha512-buzw5z5VtiQMysYLH9iW9BV04YyZebsw+gPi+c4FCjfS9i6COYOrEWw9t3m3wA9PFBfqcBCqWf32qrXLbwafDw==", "dev": true, "license": "ISC", "dependencies": { - "@nolyfill/is-core-module": "1.0.39", "debug": "^4.4.0", "get-tsconfig": "^4.10.0", "is-bun-module": "^2.0.0", "stable-hash": "^0.0.5", "tinyglobby": "^0.2.13", - "unrs-resolver": "^1.6.2" + "unrs-resolver": "^1.6.3" }, "engines": { - "node": "^14.18.0 || >=16.0.0" + "node": "^16.17.0 || >=18.6.0" }, "funding": { "url": "https://opencollective.com/eslint-import-resolver-typescript" diff --git a/package.json b/package.json index bc00ac57a59..0060cf56d4b 100644 --- a/package.json +++ b/package.json @@ -101,7 +101,7 @@ "electron-updater": "6.3.9", "eslint": "8.57.1", "eslint-config-prettier": "10.1.2", - "eslint-import-resolver-typescript": "3.10.1", + "eslint-import-resolver-typescript": "4.3.4", "eslint-plugin-import": "2.31.0", "eslint-plugin-rxjs": "5.0.3", "eslint-plugin-rxjs-angular": "2.0.1", From 534c2be55b0a24d1a3b6e7f1401107edd428f6fa Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 2 May 2025 13:52:55 +0200 Subject: [PATCH 046/116] [deps]: Update Linting minor-patch to v8.31.0 (#14583) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Oscar Hinton --- package-lock.json | 507 +++++++--------------------------------------- package.json | 6 +- 2 files changed, 76 insertions(+), 437 deletions(-) diff --git a/package-lock.json b/package-lock.json index 95e44955767..d7895d5a272 100644 --- a/package-lock.json +++ b/package-lock.json @@ -117,8 +117,8 @@ "@types/proper-lockfile": "4.1.4", "@types/retry": "0.12.5", "@types/zxcvbn": "4.4.5", - "@typescript-eslint/rule-tester": "8.22.0", - "@typescript-eslint/utils": "8.22.0", + "@typescript-eslint/rule-tester": "8.31.0", + "@typescript-eslint/utils": "8.31.0", "@webcomponents/custom-elements": "1.6.0", "@yao-pkg/pkg": "5.16.1", "angular-eslint": "18.4.3", @@ -174,7 +174,7 @@ "tsconfig-paths-webpack-plugin": "4.2.0", "type-fest": "2.19.0", "typescript": "5.4.2", - "typescript-eslint": "8.30.1", + "typescript-eslint": "8.31.0", "typescript-strict-plugin": "2.4.4", "url": "0.11.4", "util": "0.12.5", @@ -11951,17 +11951,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.30.1.tgz", - "integrity": "sha512-v+VWphxMjn+1t48/jO4t950D6KR8JaJuNXzi33Ve6P8sEmPr5k6CEXjdGwT6+LodVnEa91EQCtwjWNUCPweo+Q==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.31.0.tgz", + "integrity": "sha512-evaQJZ/J/S4wisevDvC1KFZkPzRetH8kYZbkgcTRyql3mcKsf+ZFDV1BVWUGTCAW5pQHoqn5gK5b8kn7ou9aFQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.30.1", - "@typescript-eslint/type-utils": "8.30.1", - "@typescript-eslint/utils": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1", + "@typescript-eslint/scope-manager": "8.31.0", + "@typescript-eslint/type-utils": "8.31.0", + "@typescript-eslint/utils": "8.31.0", + "@typescript-eslint/visitor-keys": "8.31.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -11980,106 +11980,6 @@ "typescript": ">=4.8.4 <5.9.0" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.30.1.tgz", - "integrity": "sha512-+C0B6ChFXZkuaNDl73FJxRYT0G7ufVPOSQkqkpM/U198wUwUFOtgo1k/QzFh1KjpBitaK7R1tgjVz6o9HmsRPg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.30.1.tgz", - "integrity": "sha512-kQQnxymiUy9tTb1F2uep9W6aBiYODgq5EMSk6Nxh4Z+BDUoYUSa029ISs5zTzKBFnexQEh71KqwjKnRz58lusQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.0.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.30.1.tgz", - "integrity": "sha512-T/8q4R9En2tcEsWPQgB5BQ0XJVOtfARcUvOa8yJP3fh9M/mXraLxZrkCfGb6ChrO/V3W+Xbd04RacUEqk1CFEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.30.1", - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/typescript-estree": "8.30.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.30.1.tgz", - "integrity": "sha512-aEhgas7aJ6vZnNFC7K4/vMGDGyOiqWcYZPpIWrTKuTAlsvDNKy2GFDqh9smL+iq069ZvR0YzEeq0B8NJlLzjFA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.30.1", - "eslint-visitor-keys": "^4.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -12281,16 +12181,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.30.1.tgz", - "integrity": "sha512-H+vqmWwT5xoNrXqWs/fesmssOW70gxFlgcMlYcBaWNPIEWDgLa4W9nkSPmhuOgLnXq9QYgkZ31fhDyLhleCsAg==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.31.0.tgz", + "integrity": "sha512-67kYYShjBR0jNI5vsf/c3WG4u+zDnCTHTPqVMQguffaWWFs7artgwKmfwdifl+r6XyM5LYLas/dInj2T0SgJyw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.30.1", - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/typescript-estree": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1", + "@typescript-eslint/scope-manager": "8.31.0", + "@typescript-eslint/types": "8.31.0", + "@typescript-eslint/typescript-estree": "8.31.0", + "@typescript-eslint/visitor-keys": "8.31.0", "debug": "^4.3.4" }, "engines": { @@ -12305,16 +12205,12 @@ "typescript": ">=4.8.4 <5.9.0" } }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.30.1.tgz", - "integrity": "sha512-+C0B6ChFXZkuaNDl73FJxRYT0G7ufVPOSQkqkpM/U198wUwUFOtgo1k/QzFh1KjpBitaK7R1tgjVz6o9HmsRPg==", + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.31.0.tgz", + "integrity": "sha512-Ch8oSjVyYyJxPQk8pMiP2FFGYatqXQfQIaMp+TpuuLlDachRWpUAeEu1u9B/v/8LToehUIWyiKcA/w5hUFRKuQ==", "dev": true, "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1" - }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -12323,73 +12219,16 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.30.1.tgz", - "integrity": "sha512-kQQnxymiUy9tTb1F2uep9W6aBiYODgq5EMSk6Nxh4Z+BDUoYUSa029ISs5zTzKBFnexQEh71KqwjKnRz58lusQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.0.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.30.1.tgz", - "integrity": "sha512-aEhgas7aJ6vZnNFC7K4/vMGDGyOiqWcYZPpIWrTKuTAlsvDNKy2GFDqh9smL+iq069ZvR0YzEeq0B8NJlLzjFA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.30.1", - "eslint-visitor-keys": "^4.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/@typescript-eslint/rule-tester": { - "version": "8.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/rule-tester/-/rule-tester-8.22.0.tgz", - "integrity": "sha512-krTIaDM08bSQ9sIpqDTP0aX4P0Ck/WQpj+7uMIeNqzzWEWmoJFyle12B0Na15KwBLPV2MJPmaZUn+v2qenXjaw==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/rule-tester/-/rule-tester-8.31.0.tgz", + "integrity": "sha512-17Dn3vpNtHqxyjOieODNKBAyxH8WNvkfZAzkHR2VA0cW/49YcmHF3YyenNI3wfF05iZJ9MWnjU2kBGz0gDavfw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.22.0", - "@typescript-eslint/utils": "8.22.0", + "@typescript-eslint/parser": "8.31.0", + "@typescript-eslint/typescript-estree": "8.31.0", + "@typescript-eslint/utils": "8.31.0", "ajv": "^6.12.6", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "4.6.2", @@ -12431,14 +12270,14 @@ "license": "MIT" }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.22.0.tgz", - "integrity": "sha512-/lwVV0UYgkj7wPSw0o8URy6YI64QmcOdwHuGuxWIYznO6d45ER0wXUbksr9pYdViAofpUCNJx/tAzNukgvaaiQ==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.31.0.tgz", + "integrity": "sha512-knO8UyF78Nt8O/B64i7TlGXod69ko7z6vJD9uhSlm0qkAbGeRUSudcm0+K/4CrRjrpiHfBCjMWlc08Vav1xwcw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.22.0", - "@typescript-eslint/visitor-keys": "8.22.0" + "@typescript-eslint/types": "8.31.0", + "@typescript-eslint/visitor-keys": "8.31.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -12449,9 +12288,9 @@ } }, "node_modules/@typescript-eslint/scope-manager/node_modules/@typescript-eslint/types": { - "version": "8.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.22.0.tgz", - "integrity": "sha512-0S4M4baNzp612zwpD4YOieP3VowOARgK2EkN/GBn95hpyF8E2fbMT55sRHWBq+Huaqk3b3XK+rxxlM8sPgGM6A==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.31.0.tgz", + "integrity": "sha512-Ch8oSjVyYyJxPQk8pMiP2FFGYatqXQfQIaMp+TpuuLlDachRWpUAeEu1u9B/v/8LToehUIWyiKcA/w5hUFRKuQ==", "dev": true, "license": "MIT", "engines": { @@ -12463,14 +12302,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.30.1.tgz", - "integrity": "sha512-64uBF76bfQiJyHgZISC7vcNz3adqQKIccVoKubyQcOnNcdJBvYOILV1v22Qhsw3tw3VQu5ll8ND6hycgAR5fEA==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.31.0.tgz", + "integrity": "sha512-DJ1N1GdjI7IS7uRlzJuEDCgDQix3ZVYVtgeWEyhyn4iaoitpMBX6Ndd488mXSx0xah/cONAkEaYyylDyAeHMHg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.30.1", - "@typescript-eslint/utils": "8.30.1", + "@typescript-eslint/typescript-estree": "8.31.0", + "@typescript-eslint/utils": "8.31.0", "debug": "^4.3.4", "ts-api-utils": "^2.0.1" }, @@ -12486,106 +12325,6 @@ "typescript": ">=4.8.4 <5.9.0" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/scope-manager": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.30.1.tgz", - "integrity": "sha512-+C0B6ChFXZkuaNDl73FJxRYT0G7ufVPOSQkqkpM/U198wUwUFOtgo1k/QzFh1KjpBitaK7R1tgjVz6o9HmsRPg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.30.1.tgz", - "integrity": "sha512-kQQnxymiUy9tTb1F2uep9W6aBiYODgq5EMSk6Nxh4Z+BDUoYUSa029ISs5zTzKBFnexQEh71KqwjKnRz58lusQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.0.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/utils": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.30.1.tgz", - "integrity": "sha512-T/8q4R9En2tcEsWPQgB5BQ0XJVOtfARcUvOa8yJP3fh9M/mXraLxZrkCfGb6ChrO/V3W+Xbd04RacUEqk1CFEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.30.1", - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/typescript-estree": "8.30.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.30.1.tgz", - "integrity": "sha512-aEhgas7aJ6vZnNFC7K4/vMGDGyOiqWcYZPpIWrTKuTAlsvDNKy2GFDqh9smL+iq069ZvR0YzEeq0B8NJlLzjFA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.30.1", - "eslint-visitor-keys": "^4.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/@typescript-eslint/types": { "version": "8.30.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.30.1.tgz", @@ -12601,20 +12340,20 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.22.0.tgz", - "integrity": "sha512-SJX99NAS2ugGOzpyhMza/tX+zDwjvwAtQFLsBo3GQxiGcvaKlqGBkmZ+Y1IdiSi9h4Q0Lr5ey+Cp9CGWNY/F/w==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.31.0.tgz", + "integrity": "sha512-xLmgn4Yl46xi6aDSZ9KkyfhhtnYI15/CvHbpOy/eR5NWhK/BK8wc709KKwhAR0m4ZKRP7h07bm4BWUYOCuRpQQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.22.0", - "@typescript-eslint/visitor-keys": "8.22.0", + "@typescript-eslint/types": "8.31.0", + "@typescript-eslint/visitor-keys": "8.31.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", - "ts-api-utils": "^2.0.0" + "ts-api-utils": "^2.0.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -12624,13 +12363,13 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.8.0" + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/types": { - "version": "8.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.22.0.tgz", - "integrity": "sha512-0S4M4baNzp612zwpD4YOieP3VowOARgK2EkN/GBn95hpyF8E2fbMT55sRHWBq+Huaqk3b3XK+rxxlM8sPgGM6A==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.31.0.tgz", + "integrity": "sha512-Ch8oSjVyYyJxPQk8pMiP2FFGYatqXQfQIaMp+TpuuLlDachRWpUAeEu1u9B/v/8LToehUIWyiKcA/w5hUFRKuQ==", "dev": true, "license": "MIT", "engines": { @@ -12642,16 +12381,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.22.0.tgz", - "integrity": "sha512-T8oc1MbF8L+Bk2msAvCUzjxVB2Z2f+vXYfcucE2wOmYs7ZUwco5Ep0fYZw8quNwOiw9K8GYVL+Kgc2pETNTLOg==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.31.0.tgz", + "integrity": "sha512-qi6uPLt9cjTFxAb1zGNgTob4x9ur7xC6mHQJ8GwEzGMGE9tYniublmJaowOJ9V2jUzxrltTPfdG2nKlWsq0+Ww==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.22.0", - "@typescript-eslint/types": "8.22.0", - "@typescript-eslint/typescript-estree": "8.22.0" + "@typescript-eslint/scope-manager": "8.31.0", + "@typescript-eslint/types": "8.31.0", + "@typescript-eslint/typescript-estree": "8.31.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -12662,13 +12401,13 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { - "version": "8.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.22.0.tgz", - "integrity": "sha512-0S4M4baNzp612zwpD4YOieP3VowOARgK2EkN/GBn95hpyF8E2fbMT55sRHWBq+Huaqk3b3XK+rxxlM8sPgGM6A==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.31.0.tgz", + "integrity": "sha512-Ch8oSjVyYyJxPQk8pMiP2FFGYatqXQfQIaMp+TpuuLlDachRWpUAeEu1u9B/v/8LToehUIWyiKcA/w5hUFRKuQ==", "dev": true, "license": "MIT", "engines": { @@ -12680,13 +12419,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.22.0.tgz", - "integrity": "sha512-AWpYAXnUgvLNabGTy3uBylkgZoosva/miNd1I8Bz3SjotmQPbVqhO4Cczo8AsZ44XVErEBPr/CRSgaj8sG7g0w==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.31.0.tgz", + "integrity": "sha512-QcGHmlRHWOl93o64ZUMNewCdwKGU6WItOU52H0djgNmn1EOrhVudrDzXz4OycCRSCPwFCDrE2iIt5vmuUdHxuQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.22.0", + "@typescript-eslint/types": "8.31.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -12698,9 +12437,9 @@ } }, "node_modules/@typescript-eslint/visitor-keys/node_modules/@typescript-eslint/types": { - "version": "8.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.22.0.tgz", - "integrity": "sha512-0S4M4baNzp612zwpD4YOieP3VowOARgK2EkN/GBn95hpyF8E2fbMT55sRHWBq+Huaqk3b3XK+rxxlM8sPgGM6A==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.31.0.tgz", + "integrity": "sha512-Ch8oSjVyYyJxPQk8pMiP2FFGYatqXQfQIaMp+TpuuLlDachRWpUAeEu1u9B/v/8LToehUIWyiKcA/w5hUFRKuQ==", "dev": true, "license": "MIT", "engines": { @@ -35407,15 +35146,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.30.1.tgz", - "integrity": "sha512-D7lC0kcehVH7Mb26MRQi64LMyRJsj3dToJxM1+JVTl53DQSV5/7oUGWQLcKl1C1KnoVHxMMU2FNQMffr7F3Row==", + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.31.0.tgz", + "integrity": "sha512-u+93F0sB0An8WEAPtwxVhFby573E8ckdjwUUQUj9QA4v8JAvgtoDdIyYR3XFwFHq2W1KJ1AurwJCO+w+Y1ixyQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.30.1", - "@typescript-eslint/parser": "8.30.1", - "@typescript-eslint/utils": "8.30.1" + "@typescript-eslint/eslint-plugin": "8.31.0", + "@typescript-eslint/parser": "8.31.0", + "@typescript-eslint/utils": "8.31.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -35429,106 +35168,6 @@ "typescript": ">=4.8.4 <5.9.0" } }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/scope-manager": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.30.1.tgz", - "integrity": "sha512-+C0B6ChFXZkuaNDl73FJxRYT0G7ufVPOSQkqkpM/U198wUwUFOtgo1k/QzFh1KjpBitaK7R1tgjVz6o9HmsRPg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.30.1.tgz", - "integrity": "sha512-kQQnxymiUy9tTb1F2uep9W6aBiYODgq5EMSk6Nxh4Z+BDUoYUSa029ISs5zTzKBFnexQEh71KqwjKnRz58lusQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/visitor-keys": "8.30.1", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.0.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.30.1.tgz", - "integrity": "sha512-T/8q4R9En2tcEsWPQgB5BQ0XJVOtfARcUvOa8yJP3fh9M/mXraLxZrkCfGb6ChrO/V3W+Xbd04RacUEqk1CFEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.30.1", - "@typescript-eslint/types": "8.30.1", - "@typescript-eslint/typescript-estree": "8.30.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.30.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.30.1.tgz", - "integrity": "sha512-aEhgas7aJ6vZnNFC7K4/vMGDGyOiqWcYZPpIWrTKuTAlsvDNKy2GFDqh9smL+iq069ZvR0YzEeq0B8NJlLzjFA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.30.1", - "eslint-visitor-keys": "^4.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/typescript-eslint/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/typescript-strict-plugin": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/typescript-strict-plugin/-/typescript-strict-plugin-2.4.4.tgz", diff --git a/package.json b/package.json index 0060cf56d4b..f7cb09c451c 100644 --- a/package.json +++ b/package.json @@ -79,8 +79,8 @@ "@types/proper-lockfile": "4.1.4", "@types/retry": "0.12.5", "@types/zxcvbn": "4.4.5", - "@typescript-eslint/rule-tester": "8.22.0", - "@typescript-eslint/utils": "8.22.0", + "@typescript-eslint/rule-tester": "8.31.0", + "@typescript-eslint/utils": "8.31.0", "@webcomponents/custom-elements": "1.6.0", "@yao-pkg/pkg": "5.16.1", "angular-eslint": "18.4.3", @@ -136,7 +136,7 @@ "tsconfig-paths-webpack-plugin": "4.2.0", "type-fest": "2.19.0", "typescript": "5.4.2", - "typescript-eslint": "8.30.1", + "typescript-eslint": "8.31.0", "typescript-strict-plugin": "2.4.4", "url": "0.11.4", "util": "0.12.5", From 1e663b0941b9f4ad5e7b6fa63a5ca3a4a433be89 Mon Sep 17 00:00:00 2001 From: "bw-ghapp[bot]" <178206702+bw-ghapp[bot]@users.noreply.github.com> Date: Fri, 2 May 2025 14:17:30 +0200 Subject: [PATCH 047/116] Autosync the updated translations (#14593) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/desktop/src/locales/af/messages.json | 30 +++++ apps/desktop/src/locales/ar/messages.json | 30 +++++ apps/desktop/src/locales/az/messages.json | 30 +++++ apps/desktop/src/locales/be/messages.json | 30 +++++ apps/desktop/src/locales/bg/messages.json | 30 +++++ apps/desktop/src/locales/bn/messages.json | 30 +++++ apps/desktop/src/locales/bs/messages.json | 30 +++++ apps/desktop/src/locales/ca/messages.json | 30 +++++ apps/desktop/src/locales/cs/messages.json | 30 +++++ apps/desktop/src/locales/cy/messages.json | 30 +++++ apps/desktop/src/locales/da/messages.json | 30 +++++ apps/desktop/src/locales/de/messages.json | 30 +++++ apps/desktop/src/locales/el/messages.json | 30 +++++ apps/desktop/src/locales/en_GB/messages.json | 30 +++++ apps/desktop/src/locales/en_IN/messages.json | 30 +++++ apps/desktop/src/locales/eo/messages.json | 30 +++++ apps/desktop/src/locales/es/messages.json | 30 +++++ apps/desktop/src/locales/et/messages.json | 30 +++++ apps/desktop/src/locales/eu/messages.json | 30 +++++ apps/desktop/src/locales/fa/messages.json | 30 +++++ apps/desktop/src/locales/fi/messages.json | 112 +++++++++++------- apps/desktop/src/locales/fil/messages.json | 30 +++++ apps/desktop/src/locales/fr/messages.json | 30 +++++ apps/desktop/src/locales/gl/messages.json | 30 +++++ apps/desktop/src/locales/he/messages.json | 30 +++++ apps/desktop/src/locales/hi/messages.json | 30 +++++ apps/desktop/src/locales/hr/messages.json | 30 +++++ apps/desktop/src/locales/hu/messages.json | 30 +++++ apps/desktop/src/locales/id/messages.json | 30 +++++ apps/desktop/src/locales/it/messages.json | 30 +++++ apps/desktop/src/locales/ja/messages.json | 30 +++++ apps/desktop/src/locales/ka/messages.json | 30 +++++ apps/desktop/src/locales/km/messages.json | 30 +++++ apps/desktop/src/locales/kn/messages.json | 30 +++++ apps/desktop/src/locales/ko/messages.json | 30 +++++ apps/desktop/src/locales/lt/messages.json | 30 +++++ apps/desktop/src/locales/lv/messages.json | 40 ++++++- apps/desktop/src/locales/me/messages.json | 30 +++++ apps/desktop/src/locales/ml/messages.json | 30 +++++ apps/desktop/src/locales/mr/messages.json | 30 +++++ apps/desktop/src/locales/my/messages.json | 30 +++++ apps/desktop/src/locales/nb/messages.json | 30 +++++ apps/desktop/src/locales/ne/messages.json | 30 +++++ apps/desktop/src/locales/nl/messages.json | 30 +++++ apps/desktop/src/locales/nn/messages.json | 30 +++++ apps/desktop/src/locales/or/messages.json | 30 +++++ apps/desktop/src/locales/pl/messages.json | 30 +++++ apps/desktop/src/locales/pt_BR/messages.json | 30 +++++ apps/desktop/src/locales/pt_PT/messages.json | 30 +++++ apps/desktop/src/locales/ro/messages.json | 30 +++++ apps/desktop/src/locales/ru/messages.json | 30 +++++ apps/desktop/src/locales/si/messages.json | 30 +++++ apps/desktop/src/locales/sk/messages.json | 30 +++++ apps/desktop/src/locales/sl/messages.json | 30 +++++ apps/desktop/src/locales/sr/messages.json | 116 ++++++++++++------- apps/desktop/src/locales/sv/messages.json | 30 +++++ apps/desktop/src/locales/te/messages.json | 30 +++++ apps/desktop/src/locales/th/messages.json | 30 +++++ apps/desktop/src/locales/tr/messages.json | 30 +++++ apps/desktop/src/locales/uk/messages.json | 30 +++++ apps/desktop/src/locales/vi/messages.json | 30 +++++ apps/desktop/src/locales/zh_CN/messages.json | 34 +++++- apps/desktop/src/locales/zh_TW/messages.json | 30 +++++ 63 files changed, 1981 insertions(+), 91 deletions(-) diff --git a/apps/desktop/src/locales/af/messages.json b/apps/desktop/src/locales/af/messages.json index c75b1640ceb..f1c510893ef 100644 --- a/apps/desktop/src/locales/af/messages.json +++ b/apps/desktop/src/locales/af/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/ar/messages.json b/apps/desktop/src/locales/ar/messages.json index cca70b3c956..696bb21087f 100644 --- a/apps/desktop/src/locales/ar/messages.json +++ b/apps/desktop/src/locales/ar/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/az/messages.json b/apps/desktop/src/locales/az/messages.json index 9b10b7bdeeb..b3c61e0af43 100644 --- a/apps/desktop/src/locales/az/messages.json +++ b/apps/desktop/src/locales/az/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Daşı" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/be/messages.json b/apps/desktop/src/locales/be/messages.json index b6714f1d440..c01caa5c0ba 100644 --- a/apps/desktop/src/locales/be/messages.json +++ b/apps/desktop/src/locales/be/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/bg/messages.json b/apps/desktop/src/locales/bg/messages.json index 5cef611de63..1db8f182818 100644 --- a/apps/desktop/src/locales/bg/messages.json +++ b/apps/desktop/src/locales/bg/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Преместване" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/bn/messages.json b/apps/desktop/src/locales/bn/messages.json index d6e16c352ff..f41dcb12057 100644 --- a/apps/desktop/src/locales/bn/messages.json +++ b/apps/desktop/src/locales/bn/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/bs/messages.json b/apps/desktop/src/locales/bs/messages.json index fa62597873c..3188a1fa889 100644 --- a/apps/desktop/src/locales/bs/messages.json +++ b/apps/desktop/src/locales/bs/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/ca/messages.json b/apps/desktop/src/locales/ca/messages.json index 9c842b710d7..f989d530b2d 100644 --- a/apps/desktop/src/locales/ca/messages.json +++ b/apps/desktop/src/locales/ca/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/cs/messages.json b/apps/desktop/src/locales/cs/messages.json index cfa18447bc9..fba5cf3e1ce 100644 --- a/apps/desktop/src/locales/cs/messages.json +++ b/apps/desktop/src/locales/cs/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Přesunout" + }, + "newLoginNudgeTitle": { + "message": "Ušetřete čas s automatickým vyplňováním" + }, + "newLoginNudgeBody": { + "message": "Zahrne webovou stránku, takže se toto přihlášení objeví jako návrh automatického vyplňování." + }, + "newCardNudgeTitle": { + "message": "Bezproblémová online pokladna" + }, + "newCardNudgeBody": { + "message": "Karty - snadné, bezpečné a přesné vyplňování platebních formulářů." + }, + "newIdentityNudgeTitle": { + "message": "Jednodušší vytváření účtů" + }, + "newIdentityNudgeBody": { + "message": "Identity - rychlé automatické vyplňování dlouhých registračních nebo kontaktních formulářů." + }, + "newNoteNudgeTitle": { + "message": "Udržujte svá citlivá data v bezpečí" + }, + "newNoteNudgeBody": { + "message": "Poznámky - bezpečné ukládání citlivých údajů, jako jsou bankovní nebo pojišťovací údaje." + }, + "newSshNudgeTitle": { + "message": "Přístup SSH pro vývojáře" + }, + "newSshNudgeBody": { + "message": "Uložte své klíče a připojte se k SSH agentovi pro rychlé a šifrované ověření." } } diff --git a/apps/desktop/src/locales/cy/messages.json b/apps/desktop/src/locales/cy/messages.json index 6c6a88be50d..efcf558bbed 100644 --- a/apps/desktop/src/locales/cy/messages.json +++ b/apps/desktop/src/locales/cy/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/da/messages.json b/apps/desktop/src/locales/da/messages.json index 6378f8106bb..f7b0c33707a 100644 --- a/apps/desktop/src/locales/da/messages.json +++ b/apps/desktop/src/locales/da/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/de/messages.json b/apps/desktop/src/locales/de/messages.json index c984397dfe0..b1c129fd488 100644 --- a/apps/desktop/src/locales/de/messages.json +++ b/apps/desktop/src/locales/de/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Verschieben" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/el/messages.json b/apps/desktop/src/locales/el/messages.json index a0045e2a47b..a91b678a04c 100644 --- a/apps/desktop/src/locales/el/messages.json +++ b/apps/desktop/src/locales/el/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/en_GB/messages.json b/apps/desktop/src/locales/en_GB/messages.json index 5262047789a..bcc9dd74934 100644 --- a/apps/desktop/src/locales/en_GB/messages.json +++ b/apps/desktop/src/locales/en_GB/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/en_IN/messages.json b/apps/desktop/src/locales/en_IN/messages.json index a3614289e16..8e40b110992 100644 --- a/apps/desktop/src/locales/en_IN/messages.json +++ b/apps/desktop/src/locales/en_IN/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/eo/messages.json b/apps/desktop/src/locales/eo/messages.json index c79e7e29af4..54322042979 100644 --- a/apps/desktop/src/locales/eo/messages.json +++ b/apps/desktop/src/locales/eo/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/es/messages.json b/apps/desktop/src/locales/es/messages.json index 9ba2d313b30..823d0cc6a2c 100644 --- a/apps/desktop/src/locales/es/messages.json +++ b/apps/desktop/src/locales/es/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Mover" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/et/messages.json b/apps/desktop/src/locales/et/messages.json index 615e52f98de..e2fb62c4df2 100644 --- a/apps/desktop/src/locales/et/messages.json +++ b/apps/desktop/src/locales/et/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/eu/messages.json b/apps/desktop/src/locales/eu/messages.json index f14fd0064d6..cfab30889eb 100644 --- a/apps/desktop/src/locales/eu/messages.json +++ b/apps/desktop/src/locales/eu/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/fa/messages.json b/apps/desktop/src/locales/fa/messages.json index f27a9f294cb..2fcb63b847b 100644 --- a/apps/desktop/src/locales/fa/messages.json +++ b/apps/desktop/src/locales/fa/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/fi/messages.json b/apps/desktop/src/locales/fi/messages.json index 77f22346ed5..627b65bb4ed 100644 --- a/apps/desktop/src/locales/fi/messages.json +++ b/apps/desktop/src/locales/fi/messages.json @@ -394,16 +394,16 @@ "message": "Todennusavain (TOTP)" }, "authenticatorKey": { - "message": "Authenticator key" + "message": "Todennusavain" }, "autofillOptions": { - "message": "Autofill options" + "message": "Automaattitäytön asetukset" }, "websiteUri": { - "message": "Website (URI)" + "message": "Verkkosivusto (URI)" }, "websiteUriCount": { - "message": "Website (URI) $COUNT$", + "message": "Verkkosivusto (URI) $COUNT$", "description": "Label for an input field that contains a website URI. The input field is part of a list of fields, and the count indicates the position of the field in the list.", "placeholders": { "count": { @@ -413,43 +413,43 @@ } }, "websiteAdded": { - "message": "Website added" + "message": "Verkkosivusto lisättiin" }, "addWebsite": { - "message": "Add website" + "message": "Lisää verkkosivusto" }, "deleteWebsite": { - "message": "Delete website" + "message": "Poista verkkosivusto" }, "owner": { - "message": "Owner" + "message": "Omistaja" }, "addField": { - "message": "Add field" + "message": "Lisää kenttä" }, "fieldType": { - "message": "Field type" + "message": "Kentän tyyppi" }, "fieldLabel": { - "message": "Field label" + "message": "Kentän nimi" }, "add": { - "message": "Add" + "message": "Lisää" }, "textHelpText": { - "message": "Use text fields for data like security questions" + "message": "Käytä tekstikenttiä esimerkiksi turvakysymysten kaltaisille tiedoille" }, "hiddenHelpText": { - "message": "Use hidden fields for sensitive data like a password" + "message": "Käytä piilotettuja kenttiä esimerkiksi salasanojen kaltaisille arkaluonteisille tiedoille" }, "checkBoxHelpText": { - "message": "Use checkboxes if you'd like to autofill a form's checkbox, like a remember email" + "message": "Käytä valintaruutuja esimerkiksi sähköpostiosoitteen muistamisen kaltaisten valintaruutujen automaattiseen merkintään" }, "linkedHelpText": { - "message": "Use a linked field when you are experiencing autofill issues for a specific website." + "message": "Käytä linkitettyjä kenttiä kohdatessasi sivustokohtaisia automaattitäytön ongelmia." }, "linkedLabelHelpText": { - "message": "Enter the the field's html id, name, aria-label, or placeholder." + "message": "Syötä kentän HTML-koodista löytyvä id-, name-, aria-label- tai placeholder-arvo." }, "folder": { "message": "Kansio" @@ -477,7 +477,7 @@ "description": "This describes a field that is 'linked' (related) to another field." }, "cfTypeCheckbox": { - "message": "Checkbox" + "message": "Valintaruutu" }, "linkedValue": { "message": "Linkitetty arvo", @@ -1977,10 +1977,10 @@ } }, "cardDetails": { - "message": "Card details" + "message": "Kortin tiedot" }, "cardBrandDetails": { - "message": "$BRAND$ details", + "message": "$BRAND$-tiedot", "placeholders": { "brand": { "content": "$1", @@ -1989,29 +1989,29 @@ } }, "learnMoreAboutAuthenticators": { - "message": "Learn more about authenticators" + "message": "Lue lisää todentajista" }, "copyTOTP": { - "message": "Copy Authenticator key (TOTP)" + "message": "Kopioi todennusavain (TOTP)" }, "totpHelperTitle": { - "message": "Make 2-step verification seamless" + "message": "Tee kaksivaiheisesta kirjautumisesta saumatonta" }, "totpHelper": { - "message": "Bitwarden can store and fill 2-step verification codes. Copy and paste the key into this field." + "message": "Bitwarden voi säilyttää ja täyttää kaksivaiheisen kirjautumisen koodit. Kopioi ja liitä todennusavain tähän kenttään." }, "totpHelperWithCapture": { - "message": "Bitwarden can store and fill 2-step verification codes. Select the camera icon to take a screenshot of this website's authenticator QR code, or copy and paste the key into this field." + "message": "Bitwarden voi säilyttää ja täyttää kaksivaiheisen kirjautumisen koodit. Kamerakuvakkeella voit kaapata todennusavaimen avoimen sivun QR-koodista automaattisesti, tai voit kopioida ja liittää sen tähän kenttään manuaalisesti." }, "premium": { "message": "Premium", "description": "Premium membership" }, "cardExpiredTitle": { - "message": "Expired card" + "message": "Vanhentunut kortti" }, "cardExpiredMessage": { - "message": "If you've renewed it, update the card's information" + "message": "Jos olet uudistanut kortin, päivitä sen tiedot" }, "verificationRequired": { "message": "Vahvistus vaaditaan", @@ -2183,13 +2183,13 @@ "message": "Organisaatiokäytäntö estää kohteiden tuonnin yksityiseen holviisi." }, "personalDetails": { - "message": "Personal details" + "message": "Henkilökohtaiset tiedot" }, "identification": { "message": "Identification" }, "contactInfo": { - "message": "Contact information" + "message": "Yhteystiedot" }, "allSends": { "message": "Kaikki Sendit", @@ -2626,7 +2626,7 @@ "message": "Luo sähköpostiosoite" }, "usernameGenerator": { - "message": "Username generator" + "message": "Käyttäjätunnusgeneraattori" }, "spinboxBoundariesHint": { "message": "Arvon tulee olla väliltä $MIN$—$MAX$.", @@ -3550,10 +3550,10 @@ "message": "Kertakirjautumiselle ei löytynyt vapaita portteja." }, "securePasswordGenerated": { - "message": "Secure password generated! Don't forget to also update your password on the website." + "message": "Turvallinen salasana luotiin! Muista vaihtaa se myös verkkosivuston tiliasetuksiin." }, "useGeneratorHelpTextPartOne": { - "message": "Use the generator", + "message": "Käytä generaattoria", "description": "This will be used as part of a larger sentence, broken up to include the generator icon. The full sentence will read 'Use the generator [GENERATOR_ICON] to create a strong unique password'" }, "useGeneratorHelpTextPartTwo": { @@ -3567,10 +3567,10 @@ "message": "Biometrinen lukituksen avaus ei ole tällä hetkellä käytettävissä." }, "biometricsStatusHelptextAutoSetupNeeded": { - "message": "Biometric unlock is unavailable due to misconfigured system files." + "message": "Biometrinen avaus ei ole käytettävissä virheellisesti määritettyjen järjestelmätiedostojen takia." }, "biometricsStatusHelptextManualSetupNeeded": { - "message": "Biometric unlock is unavailable due to misconfigured system files." + "message": "Biometrinen avaus ei ole käytettävissä virheellisesti määritettyjen järjestelmätiedostojen takia." }, "biometricsStatusHelptextNotEnabledLocally": { "message": "Biometrinen avaus ei ole käytettävissä, koska sitä ei ole otettu käyttöön osoitteelle $EMAIL$ Bitwardenin työpöytäsovelluksessa.", @@ -3597,7 +3597,7 @@ "message": "Warning: Agent Forwarding" }, "agentForwardingWarningText": { - "message": "This request comes from a remote device that you are logged into" + "message": "Tämä pyyntö tulee etälaitteelta, johon olet kirjautunut" }, "sshkeyApprovalMessageInfix": { "message": "pyytää pääsyä" @@ -3639,25 +3639,25 @@ "message": "Määritä kaksivaiheinen kirjautuminen" }, "itemDetails": { - "message": "Item details" + "message": "Kohteen tiedot" }, "itemName": { - "message": "Item name" + "message": "Kohteen nimi" }, "loginCredentials": { - "message": "Login credentials" + "message": "Kirjautumistiedot" }, "additionalOptions": { - "message": "Additional options" + "message": "Lisävalinnat" }, "itemHistory": { - "message": "Item history" + "message": "Kohteen historia" }, "lastEdited": { "message": "Last edited" }, "upload": { - "message": "Upload" + "message": "Lähetä" }, "newDeviceVerificationNoticeContentPage1": { "message": "Bitwarden lähettää tilisi sähköpostiosoitteeseen koodin, jolla voit vahvistaa kirjautumiset uusista laitteista helmikuusta 2025 alkaen." @@ -3712,5 +3712,35 @@ }, "move": { "message": "Siirrä" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/fil/messages.json b/apps/desktop/src/locales/fil/messages.json index cdad0fe5487..2735aef242e 100644 --- a/apps/desktop/src/locales/fil/messages.json +++ b/apps/desktop/src/locales/fil/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/fr/messages.json b/apps/desktop/src/locales/fr/messages.json index bb019d64f36..0170cf2d0ce 100644 --- a/apps/desktop/src/locales/fr/messages.json +++ b/apps/desktop/src/locales/fr/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/gl/messages.json b/apps/desktop/src/locales/gl/messages.json index 81e3a94ff4d..2350e0df4c7 100644 --- a/apps/desktop/src/locales/gl/messages.json +++ b/apps/desktop/src/locales/gl/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/he/messages.json b/apps/desktop/src/locales/he/messages.json index 7c3d30d7bbe..a8f52de239f 100644 --- a/apps/desktop/src/locales/he/messages.json +++ b/apps/desktop/src/locales/he/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/hi/messages.json b/apps/desktop/src/locales/hi/messages.json index add8b81f429..a5d273d0e25 100644 --- a/apps/desktop/src/locales/hi/messages.json +++ b/apps/desktop/src/locales/hi/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/hr/messages.json b/apps/desktop/src/locales/hr/messages.json index 0a9b7be1fd9..af78a035927 100644 --- a/apps/desktop/src/locales/hr/messages.json +++ b/apps/desktop/src/locales/hr/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/hu/messages.json b/apps/desktop/src/locales/hu/messages.json index 6b145ce80db..ae97ccd49e6 100644 --- a/apps/desktop/src/locales/hu/messages.json +++ b/apps/desktop/src/locales/hu/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Áthelyezés" + }, + "newLoginNudgeTitle": { + "message": "Idő megtakarítás automatikus kitöltéssel" + }, + "newLoginNudgeBody": { + "message": "Adjunk meg egy webhelyet, hogy ez a bejelentkezési név automatikusan kitöltendő javaslatként jelenjen meg." + }, + "newCardNudgeTitle": { + "message": "Zökkenőmentes online fizetés" + }, + "newCardNudgeBody": { + "message": "Kártyákkal könnyedén, biztonságosan és pontosan tölthetjük ki automatikusan a fizetési űrlapokat." + }, + "newIdentityNudgeTitle": { + "message": "Egyszerűsítsük a fiókok létrehozását" + }, + "newIdentityNudgeBody": { + "message": "Azonosítókkal gyorsan automatikusan kitölthetjük a hosszú regisztrációs vagy kapcsolatfelvételi űrlapokat." + }, + "newNoteNudgeTitle": { + "message": "Tartsuk biztonságban az érzékeny adatokat" + }, + "newNoteNudgeBody": { + "message": "Jegyzetekkel biztonságosan tárolhatjuk az érzékeny adatokat, például a banki vagy biztosítási adatokat." + }, + "newSshNudgeTitle": { + "message": "Fejlesztőbarát SSH hozzáférés" + }, + "newSshNudgeBody": { + "message": "Tároljuk a kulcsokat és csatlakozzunk az SSH-ügynökhöz a gyors, titkosított hitelesítéshez." } } diff --git a/apps/desktop/src/locales/id/messages.json b/apps/desktop/src/locales/id/messages.json index 7afef92ee3e..e54bcece504 100644 --- a/apps/desktop/src/locales/id/messages.json +++ b/apps/desktop/src/locales/id/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/it/messages.json b/apps/desktop/src/locales/it/messages.json index e1aca77337e..f02823d09de 100644 --- a/apps/desktop/src/locales/it/messages.json +++ b/apps/desktop/src/locales/it/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/ja/messages.json b/apps/desktop/src/locales/ja/messages.json index 0bddf31cdb3..dc87276cfe3 100644 --- a/apps/desktop/src/locales/ja/messages.json +++ b/apps/desktop/src/locales/ja/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "移動" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/ka/messages.json b/apps/desktop/src/locales/ka/messages.json index ff3c146ca52..dd01fa84df2 100644 --- a/apps/desktop/src/locales/ka/messages.json +++ b/apps/desktop/src/locales/ka/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/km/messages.json b/apps/desktop/src/locales/km/messages.json index 81e3a94ff4d..2350e0df4c7 100644 --- a/apps/desktop/src/locales/km/messages.json +++ b/apps/desktop/src/locales/km/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/kn/messages.json b/apps/desktop/src/locales/kn/messages.json index a599cef168a..b0f9ac4849f 100644 --- a/apps/desktop/src/locales/kn/messages.json +++ b/apps/desktop/src/locales/kn/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/ko/messages.json b/apps/desktop/src/locales/ko/messages.json index ff6f9b4b8f1..ed94aae47e8 100644 --- a/apps/desktop/src/locales/ko/messages.json +++ b/apps/desktop/src/locales/ko/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/lt/messages.json b/apps/desktop/src/locales/lt/messages.json index ab8a991ca72..00a5602ff3c 100644 --- a/apps/desktop/src/locales/lt/messages.json +++ b/apps/desktop/src/locales/lt/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/lv/messages.json b/apps/desktop/src/locales/lv/messages.json index f6d8cbed4d6..4a3ed41f980 100644 --- a/apps/desktop/src/locales/lv/messages.json +++ b/apps/desktop/src/locales/lv/messages.json @@ -980,7 +980,7 @@ "message": "Atlasīt divpakāpju pieteikšanās veidu" }, "selfHostedEnvironment": { - "message": "Pašuzturēta vide" + "message": "Pašmitināta vide" }, "selfHostedBaseUrlHint": { "message": "Jānorāda sava pašizvietotā Bitward servera pamata URL. Piemērs: https://bitwarden.uznemums.lv" @@ -1145,10 +1145,10 @@ "message": "Iegūt pārlūka paplašinājumu" }, "syncingComplete": { - "message": "Sinhronizācija pabeigta" + "message": "Sinhronizēšana pabeigta" }, "syncingFailed": { - "message": "Sinhronizācija neizdevās" + "message": "Sinhronizēšana neizdevās" }, "yourVaultIsLocked": { "message": "Glabātava ir aizslēgta. Jāapliecina sava identitāte, lai turpinātu." @@ -2508,7 +2508,7 @@ "message": "Galvenā parole tika noņemta." }, "convertOrganizationEncryptionDesc": { - "message": "$ORGANIZATION$ izmanto vienoto pieteikšanos ar pašizvietotu atslēgu serveri. Tās dalībniekiem vairs nav nepieciešama galvenā parole, lai pieteiktos.", + "message": "$ORGANIZATION$ izmanto vienoto pieteikšanos ar pašmitinātu atslēgu serveri. Tās dalībniekiem vairs nav nepieciešama galvenā parole, lai pieteiktos.", "placeholders": { "organization": { "content": "$1", @@ -3123,7 +3123,7 @@ "description": "European Union" }, "selfHostedServer": { - "message": "pašizvietots" + "message": "pašmitināts" }, "accessDenied": { "message": "Piekļuve liegta. Nav nepieciešamo atļauju, lai skatītu šo lapu." @@ -3712,5 +3712,35 @@ }, "move": { "message": "Pārvietot" + }, + "newLoginNudgeTitle": { + "message": "Laika ietaupīšana ar automātisko aizpildi" + }, + "newLoginNudgeBody": { + "message": "Iekļauj tīmekļvietni, lai šis pieteikšanās vienums parādītos kā automātiskās aizpildes ieteikums!" + }, + "newCardNudgeTitle": { + "message": "Plūdena apmaksa tiešsaistē" + }, + "newCardNudgeBody": { + "message": "Ar kartēm ir viegli automātiski aizpildīt maksājumu veidlapu droši un rūpīgi." + }, + "newIdentityNudgeTitle": { + "message": "Kontu izveidošanas vienkāršošana" + }, + "newIdentityNudgeBody": { + "message": "Ar identitātēm var ātri automātiski aizpildīt garas reģistrēšanās vai saziņas veidlapas." + }, + "newNoteNudgeTitle": { + "message": "Turi savus jūtīgos datus drošībā" + }, + "newNoteNudgeBody": { + "message": "Piezīmēs var droši glabāt jūtīgus datus, piemēram, bankas vai apdrošināšanas informāciju." + }, + "newSshNudgeTitle": { + "message": "Izstrādātājiem draudzīga SSH piekļuve" + }, + "newSshNudgeBody": { + "message": "Glabā savas atslēgas un savienojies ar SSH aģentu ātrai, šifrētai autentificēšanai!" } } diff --git a/apps/desktop/src/locales/me/messages.json b/apps/desktop/src/locales/me/messages.json index abb4f972898..2ee83d6e91b 100644 --- a/apps/desktop/src/locales/me/messages.json +++ b/apps/desktop/src/locales/me/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/ml/messages.json b/apps/desktop/src/locales/ml/messages.json index 92ee37940fc..9ce30119832 100644 --- a/apps/desktop/src/locales/ml/messages.json +++ b/apps/desktop/src/locales/ml/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/mr/messages.json b/apps/desktop/src/locales/mr/messages.json index 81e3a94ff4d..2350e0df4c7 100644 --- a/apps/desktop/src/locales/mr/messages.json +++ b/apps/desktop/src/locales/mr/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/my/messages.json b/apps/desktop/src/locales/my/messages.json index 06cbb78f922..b53b3fb241a 100644 --- a/apps/desktop/src/locales/my/messages.json +++ b/apps/desktop/src/locales/my/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/nb/messages.json b/apps/desktop/src/locales/nb/messages.json index e1e9114db49..f0e6b338bc8 100644 --- a/apps/desktop/src/locales/nb/messages.json +++ b/apps/desktop/src/locales/nb/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/ne/messages.json b/apps/desktop/src/locales/ne/messages.json index 2f68046ec3a..93485f6d773 100644 --- a/apps/desktop/src/locales/ne/messages.json +++ b/apps/desktop/src/locales/ne/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/nl/messages.json b/apps/desktop/src/locales/nl/messages.json index f149dbf158f..dee51830601 100644 --- a/apps/desktop/src/locales/nl/messages.json +++ b/apps/desktop/src/locales/nl/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Verplaatsen" + }, + "newLoginNudgeTitle": { + "message": "Tijd besparen met automatisch aanvullen" + }, + "newLoginNudgeBody": { + "message": "Voeg een website toe zodat deze login wordt weergegeven als een automatische invulsuggestie." + }, + "newCardNudgeTitle": { + "message": "Naadloos online afrekenen" + }, + "newCardNudgeBody": { + "message": "Met kaarten gemakkelijk, veilig en accuraat automatisch invullen van betaalformulieren." + }, + "newIdentityNudgeTitle": { + "message": "Vereenvoudig het aanmaken van accounts" + }, + "newIdentityNudgeBody": { + "message": "Met identiteiten vul je lange registratie- of contactformulieren snel automatisch in." + }, + "newNoteNudgeTitle": { + "message": "Houd je gevoelige gegevens veilig" + }, + "newNoteNudgeBody": { + "message": "Met notities veilig opslaan van gevoelige gegevens zoals bank- of verzekeringsgegevens." + }, + "newSshNudgeTitle": { + "message": "Ontwikkelaars-vriendelijke SSH-toegang" + }, + "newSshNudgeBody": { + "message": "Sla je sleutels op en verbind met de SSH-agent voor snelle, versleutelde authenticatie." } } diff --git a/apps/desktop/src/locales/nn/messages.json b/apps/desktop/src/locales/nn/messages.json index ffa70f3e737..894b505221b 100644 --- a/apps/desktop/src/locales/nn/messages.json +++ b/apps/desktop/src/locales/nn/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/or/messages.json b/apps/desktop/src/locales/or/messages.json index c4c923b662b..0e99fbcda3e 100644 --- a/apps/desktop/src/locales/or/messages.json +++ b/apps/desktop/src/locales/or/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/pl/messages.json b/apps/desktop/src/locales/pl/messages.json index e5a8cdf77d4..c6000b84e7a 100644 --- a/apps/desktop/src/locales/pl/messages.json +++ b/apps/desktop/src/locales/pl/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/pt_BR/messages.json b/apps/desktop/src/locales/pt_BR/messages.json index be20f43a0e3..35d63e07b1a 100644 --- a/apps/desktop/src/locales/pt_BR/messages.json +++ b/apps/desktop/src/locales/pt_BR/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Mover" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/pt_PT/messages.json b/apps/desktop/src/locales/pt_PT/messages.json index e721133c325..50163e811ba 100644 --- a/apps/desktop/src/locales/pt_PT/messages.json +++ b/apps/desktop/src/locales/pt_PT/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Mover" + }, + "newLoginNudgeTitle": { + "message": "Poupe tempo com o preenchimento automático" + }, + "newLoginNudgeBody": { + "message": "Inclua um site para que esta credencial apareça como uma sugestão de preenchimento automático." + }, + "newCardNudgeTitle": { + "message": "Pagamentos online sem problemas" + }, + "newCardNudgeBody": { + "message": "Com os cartões, preencha facilmente formulários de pagamento de forma segura e exata." + }, + "newIdentityNudgeTitle": { + "message": "Simplifique a criação de contas" + }, + "newIdentityNudgeBody": { + "message": "Com as identidades, preencha rapidamente formulários de registo ou de contacto longos." + }, + "newNoteNudgeTitle": { + "message": "Mantenha os seus dados sensíveis seguros" + }, + "newNoteNudgeBody": { + "message": "Com as notas, armazene de forma segura dados sensíveis, como dados bancários ou de seguros." + }, + "newSshNudgeTitle": { + "message": "Acesso SSH de fácil utilização pelos programadores" + }, + "newSshNudgeBody": { + "message": "Guarde as suas chaves e ligue-se ao agente SSH para uma autenticação rápida e encriptada." } } diff --git a/apps/desktop/src/locales/ro/messages.json b/apps/desktop/src/locales/ro/messages.json index 1e37615ff21..80b87b73f50 100644 --- a/apps/desktop/src/locales/ro/messages.json +++ b/apps/desktop/src/locales/ro/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/ru/messages.json b/apps/desktop/src/locales/ru/messages.json index 76a11b7868e..b1f3c35e9be 100644 --- a/apps/desktop/src/locales/ru/messages.json +++ b/apps/desktop/src/locales/ru/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Переместить" + }, + "newLoginNudgeTitle": { + "message": "Экономьте время с помощью автозаполнения" + }, + "newLoginNudgeBody": { + "message": "Включите сайт, чтобы этот логин отображался в качестве предложения для автозаполнения." + }, + "newCardNudgeTitle": { + "message": "Оформление заказа через интернет" + }, + "newCardNudgeBody": { + "message": "С помощью карт можно легко и безопасно автоматически заполнять формы оплаты." + }, + "newIdentityNudgeTitle": { + "message": "Упрощение создания аккаунтов" + }, + "newIdentityNudgeBody": { + "message": "С помощью личностей можно быстро заполнять длинные регистрационные или контактные формы." + }, + "newNoteNudgeTitle": { + "message": "Храните ваши конфиденциальные данные в безопасности" + }, + "newNoteNudgeBody": { + "message": "С помощью заметок можно надежно хранить конфиденциальные данные, например, банковские или страховые реквизиты." + }, + "newSshNudgeTitle": { + "message": "Удобный для разработчиков SSH-доступ" + }, + "newSshNudgeBody": { + "message": "Храните свои ключи и подключайтесь с помощью агента SSH для быстрой и зашифрованной аутентификации." } } diff --git a/apps/desktop/src/locales/si/messages.json b/apps/desktop/src/locales/si/messages.json index a697124f72c..d345f7849a0 100644 --- a/apps/desktop/src/locales/si/messages.json +++ b/apps/desktop/src/locales/si/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/sk/messages.json b/apps/desktop/src/locales/sk/messages.json index d8bc74f1b0b..a65d6c24f5a 100644 --- a/apps/desktop/src/locales/sk/messages.json +++ b/apps/desktop/src/locales/sk/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Presunúť" + }, + "newLoginNudgeTitle": { + "message": "Ušetrite čas s automatickým vypĺňaním" + }, + "newLoginNudgeBody": { + "message": "Zadajte webovú stránku, aby sa tieto prihlasovacie údaje zobrazili ako návrh na automatické vyplnenie." + }, + "newCardNudgeTitle": { + "message": "Bezproblémová online registrácia" + }, + "newCardNudgeBody": { + "message": "S kartami môžete jednoducho, bezpečne a presne automaticky vypĺňať platobné formuláre." + }, + "newIdentityNudgeTitle": { + "message": "Zjednodušenie vytvárania účtov" + }, + "newIdentityNudgeBody": { + "message": "Pomocou identít môžete rýchlo automaticky vypĺňať dlhé registračné alebo kontaktné formuláre." + }, + "newNoteNudgeTitle": { + "message": "Udržujte svoje citlivé údaje v bezpečí" + }, + "newNoteNudgeBody": { + "message": "Pomocou poznámok môžete bezpečne ukladať citlivé údaje, napríklad bankové údaje alebo údaje o poistení." + }, + "newSshNudgeTitle": { + "message": "Prístup SSH vhodný pre vývojárov" + }, + "newSshNudgeBody": { + "message": "Uložte si kľúče a pripojte sa pomocou agenta SSH na rýchle šifrované overovanie." } } diff --git a/apps/desktop/src/locales/sl/messages.json b/apps/desktop/src/locales/sl/messages.json index ecfc8b9c00b..fd4c17f949e 100644 --- a/apps/desktop/src/locales/sl/messages.json +++ b/apps/desktop/src/locales/sl/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/sr/messages.json b/apps/desktop/src/locales/sr/messages.json index 195b816a2ba..aa0ac47cd9d 100644 --- a/apps/desktop/src/locales/sr/messages.json +++ b/apps/desktop/src/locales/sr/messages.json @@ -394,16 +394,16 @@ "message": "Једнократни код" }, "authenticatorKey": { - "message": "Authenticator key" + "message": "Кључ аутентификатора" }, "autofillOptions": { - "message": "Autofill options" + "message": "Опције Ауто-пуњења" }, "websiteUri": { - "message": "Website (URI)" + "message": "Вебсајт (URI)" }, "websiteUriCount": { - "message": "Website (URI) $COUNT$", + "message": "Сајт (URI) $COUNT$", "description": "Label for an input field that contains a website URI. The input field is part of a list of fields, and the count indicates the position of the field in the list.", "placeholders": { "count": { @@ -413,43 +413,43 @@ } }, "websiteAdded": { - "message": "Website added" + "message": "Вебсајт додат" }, "addWebsite": { - "message": "Add website" + "message": "Додај вебсајт" }, "deleteWebsite": { - "message": "Delete website" + "message": "Обриши вебсајт" }, "owner": { - "message": "Owner" + "message": "Власник" }, "addField": { - "message": "Add field" + "message": "Додај поље" }, "fieldType": { - "message": "Field type" + "message": "Врста поља" }, "fieldLabel": { - "message": "Field label" + "message": "Ознака поља" }, "add": { - "message": "Add" + "message": "Додај" }, "textHelpText": { - "message": "Use text fields for data like security questions" + "message": "Користите текстуална поља за податке као што су безбедносна питања" }, "hiddenHelpText": { - "message": "Use hidden fields for sensitive data like a password" + "message": "Користите скривена поља за осетљиве податке као што је лозинка" }, "checkBoxHelpText": { - "message": "Use checkboxes if you'd like to autofill a form's checkbox, like a remember email" + "message": "Користите поља за потврду ако желите да аутоматски попуните поље за потврду обрасца, на пример имејл за памћење" }, "linkedHelpText": { - "message": "Use a linked field when you are experiencing autofill issues for a specific website." + "message": "Користите повезано поље када имате проблема са аутоматским попуњавањем за одређену веб локацију." }, "linkedLabelHelpText": { - "message": "Enter the the field's html id, name, aria-label, or placeholder." + "message": "Унесите html Ид поља, име, aria-label, или placeholder." }, "folder": { "message": "Фасцикла" @@ -477,7 +477,7 @@ "description": "This describes a field that is 'linked' (related) to another field." }, "cfTypeCheckbox": { - "message": "Checkbox" + "message": "Поље за потврду" }, "linkedValue": { "message": "Вредност повезана", @@ -1977,10 +1977,10 @@ } }, "cardDetails": { - "message": "Card details" + "message": "Детаљи картице" }, "cardBrandDetails": { - "message": "$BRAND$ details", + "message": "$BRAND$ детаљи", "placeholders": { "brand": { "content": "$1", @@ -1989,29 +1989,29 @@ } }, "learnMoreAboutAuthenticators": { - "message": "Learn more about authenticators" + "message": "Сазнајте више о аутентификаторима" }, "copyTOTP": { - "message": "Copy Authenticator key (TOTP)" + "message": "Копирати једнократни кôд (TOTP)" }, "totpHelperTitle": { - "message": "Make 2-step verification seamless" + "message": "Учините верификацију у 2 корака беспрекорном" }, "totpHelper": { - "message": "Bitwarden can store and fill 2-step verification codes. Copy and paste the key into this field." + "message": "Bitwarden може да чува и попуњава верификационе кодове у 2 корака. Копирајте и налепите кључ у ово поље." }, "totpHelperWithCapture": { - "message": "Bitwarden can store and fill 2-step verification codes. Select the camera icon to take a screenshot of this website's authenticator QR code, or copy and paste the key into this field." + "message": "Bitwarden може да чува и попуњава верификационе кодове у 2 корака. Изаберите икону камере да бисте направили снимак екрана QR кода за аутентификацију ове веб локације или копирајте и налепите кључ у ово поље." }, "premium": { - "message": "Premium", + "message": "Премијум", "description": "Premium membership" }, "cardExpiredTitle": { - "message": "Expired card" + "message": "Картица је истекла" }, "cardExpiredMessage": { - "message": "If you've renewed it, update the card's information" + "message": "Ако сте је обновили, ажурирајте податке о картици" }, "verificationRequired": { "message": "Потребдна верификација", @@ -2183,13 +2183,13 @@ "message": "Политика организације је блокирала увоз ставки у ваш појединачни сеф." }, "personalDetails": { - "message": "Personal details" + "message": "Личне информације" }, "identification": { - "message": "Identification" + "message": "Идентификација" }, "contactInfo": { - "message": "Contact information" + "message": "Контакт подаци" }, "allSends": { "message": "Сва слања", @@ -2626,7 +2626,7 @@ "message": "Генеришите имејл" }, "usernameGenerator": { - "message": "Username generator" + "message": "Генератор корисничког имена" }, "spinboxBoundariesHint": { "message": "Вредност мора бити између $MIN$ и $MAX$.", @@ -3323,7 +3323,7 @@ "message": "Следите наведене кораке да бисте завршили пријављивање." }, "followTheStepsBelowToFinishLoggingInWithSecurityKey": { - "message": "Follow the steps below to finish logging in with your security key." + "message": "Следите наведене кораке да бисте завршили пријаву са својим безбедносним кључем." }, "launchDuo": { "message": "Покренути Duo у претраживачу" @@ -3550,14 +3550,14 @@ "message": "Нису пронађени портови за SSO пријаву." }, "securePasswordGenerated": { - "message": "Secure password generated! Don't forget to also update your password on the website." + "message": "Сигурна лозинка је генерисана! Не заборавите да ажурирате и своју лозинку на веб локацији." }, "useGeneratorHelpTextPartOne": { - "message": "Use the generator", + "message": "Употребити генератор", "description": "This will be used as part of a larger sentence, broken up to include the generator icon. The full sentence will read 'Use the generator [GENERATOR_ICON] to create a strong unique password'" }, "useGeneratorHelpTextPartTwo": { - "message": "to create a strong unique password", + "message": "да креирате јаку јединствену лозинку", "description": "This will be used as part of a larger sentence, broken up to include the generator icon. The full sentence will read 'Use the generator [GENERATOR_ICON] to create a strong unique password'" }, "biometricsStatusHelptextUnlockNeeded": { @@ -3639,25 +3639,25 @@ "message": "Поставити дво-степенску пријаву" }, "itemDetails": { - "message": "Item details" + "message": "Детаљи ставке" }, "itemName": { - "message": "Item name" + "message": "Име ставке" }, "loginCredentials": { - "message": "Login credentials" + "message": "Акредитиве за пријављивање" }, "additionalOptions": { - "message": "Additional options" + "message": "Додатне опције" }, "itemHistory": { - "message": "Item history" + "message": "Историја предмета" }, "lastEdited": { - "message": "Last edited" + "message": "Последња измена" }, "upload": { - "message": "Upload" + "message": "Отпреми" }, "newDeviceVerificationNoticeContentPage1": { "message": "Bitwarden ће послати кôд на имејл вашег налога за верификовање пријављивања са нових уређаја почевши од фебруара 2025." @@ -3712,5 +3712,35 @@ }, "move": { "message": "Премести" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/sv/messages.json b/apps/desktop/src/locales/sv/messages.json index 84828a05130..5ca6c5b9372 100644 --- a/apps/desktop/src/locales/sv/messages.json +++ b/apps/desktop/src/locales/sv/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/te/messages.json b/apps/desktop/src/locales/te/messages.json index 81e3a94ff4d..2350e0df4c7 100644 --- a/apps/desktop/src/locales/te/messages.json +++ b/apps/desktop/src/locales/te/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/th/messages.json b/apps/desktop/src/locales/th/messages.json index c027755373f..6bd75b86398 100644 --- a/apps/desktop/src/locales/th/messages.json +++ b/apps/desktop/src/locales/th/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/tr/messages.json b/apps/desktop/src/locales/tr/messages.json index 9f62e072700..3100cbabcbb 100644 --- a/apps/desktop/src/locales/tr/messages.json +++ b/apps/desktop/src/locales/tr/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Taşı" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/uk/messages.json b/apps/desktop/src/locales/uk/messages.json index 099039fd5bd..9b751b4830a 100644 --- a/apps/desktop/src/locales/uk/messages.json +++ b/apps/desktop/src/locales/uk/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Перемістити" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/vi/messages.json b/apps/desktop/src/locales/vi/messages.json index b555eb49bae..d75175778a3 100644 --- a/apps/desktop/src/locales/vi/messages.json +++ b/apps/desktop/src/locales/vi/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/zh_CN/messages.json b/apps/desktop/src/locales/zh_CN/messages.json index cba4968d45d..7085d4aa033 100644 --- a/apps/desktop/src/locales/zh_CN/messages.json +++ b/apps/desktop/src/locales/zh_CN/messages.json @@ -2189,7 +2189,7 @@ "message": "Identification" }, "contactInfo": { - "message": "Contact information" + "message": "联系信息" }, "allSends": { "message": "所有的 Send", @@ -3550,7 +3550,7 @@ "message": "未找到用于 SSO 登录的空闲端口。" }, "securePasswordGenerated": { - "message": "安全的密码生成好了!别忘了在网站上也更新一下您的密码。" + "message": "安全的密码已生成!不要忘记在网站上更新您的密码。" }, "useGeneratorHelpTextPartOne": { "message": "Use the generator", @@ -3712,5 +3712,35 @@ }, "move": { "message": "移动" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } diff --git a/apps/desktop/src/locales/zh_TW/messages.json b/apps/desktop/src/locales/zh_TW/messages.json index 40830f1d2e0..a871a07adda 100644 --- a/apps/desktop/src/locales/zh_TW/messages.json +++ b/apps/desktop/src/locales/zh_TW/messages.json @@ -3712,5 +3712,35 @@ }, "move": { "message": "Move" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } } From da15a4ce8de5a6115ca2a250868b88349523cd82 Mon Sep 17 00:00:00 2001 From: Conner Turnbull <133619638+cturnbull-bitwarden@users.noreply.github.com> Date: Fri, 2 May 2025 08:47:55 -0400 Subject: [PATCH 048/116] [PM-20403] Updated F4E copy (#14404) * Updated F4E copy * Updated incorrect i18n key --- .../app/billing/members/free-bitwarden-families.component.html | 2 +- .../src/app/billing/settings/sponsored-families.component.html | 2 +- apps/web/src/locales/en/messages.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/web/src/app/billing/members/free-bitwarden-families.component.html b/apps/web/src/app/billing/members/free-bitwarden-families.component.html index 9e32fb925a8..8eeef6f0f36 100644 --- a/apps/web/src/app/billing/members/free-bitwarden-families.component.html +++ b/apps/web/src/app/billing/members/free-bitwarden-families.component.html @@ -14,7 +14,7 @@ {{ "sponsoredFamiliesIncludeMessage" | i18n }}:
      • {{ "sponsoredFamiliesPremiumAccess" | i18n }}
      • -
      • {{ "sponsoredFamiliesSharedCollectionsMessage" | i18n }}
      • +
      • {{ "sponsoredFamiliesSharedCollectionsForFamilyMembers" | i18n }}
      diff --git a/apps/web/src/app/billing/settings/sponsored-families.component.html b/apps/web/src/app/billing/settings/sponsored-families.component.html index 5a6957718a3..7708f63365e 100644 --- a/apps/web/src/app/billing/settings/sponsored-families.component.html +++ b/apps/web/src/app/billing/settings/sponsored-families.component.html @@ -13,7 +13,7 @@ {{ "sponsoredFamiliesIncludeMessage" | i18n }}:
      • {{ "sponsoredFamiliesPremiumAccess" | i18n }}
      • -
      • {{ "sponsoredFamiliesSharedCollectionsMessage" | i18n }}
      • +
      • {{ "sponsoredFamiliesSharedCollectionsForFamilyMembers" | i18n }}
      diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 59ba7961d82..8a37db4afda 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -6327,7 +6327,7 @@ "sponsoredFamiliesPremiumAccess": { "message": "Premium access for up to 6 users" }, - "sponsoredFamiliesSharedCollectionsMessage": { + "sponsoredFamiliesSharedCollectionsForFamilyMembers": { "message": "Shared collections for family members" }, "memberFamilies": { From 8fbb46a360b91387526a16665988aa9d472d4424 Mon Sep 17 00:00:00 2001 From: "bw-ghapp[bot]" <178206702+bw-ghapp[bot]@users.noreply.github.com> Date: Fri, 2 May 2025 15:24:49 +0200 Subject: [PATCH 049/116] Autosync the updated translations (#14592) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/browser/src/_locales/ar/messages.json | 53 +++++++++++++- apps/browser/src/_locales/az/messages.json | 53 +++++++++++++- apps/browser/src/_locales/be/messages.json | 53 +++++++++++++- apps/browser/src/_locales/bg/messages.json | 53 +++++++++++++- apps/browser/src/_locales/bn/messages.json | 53 +++++++++++++- apps/browser/src/_locales/bs/messages.json | 53 +++++++++++++- apps/browser/src/_locales/ca/messages.json | 53 +++++++++++++- apps/browser/src/_locales/cs/messages.json | 53 +++++++++++++- apps/browser/src/_locales/cy/messages.json | 53 +++++++++++++- apps/browser/src/_locales/da/messages.json | 53 +++++++++++++- apps/browser/src/_locales/de/messages.json | 53 +++++++++++++- apps/browser/src/_locales/el/messages.json | 53 +++++++++++++- apps/browser/src/_locales/en_GB/messages.json | 53 +++++++++++++- apps/browser/src/_locales/en_IN/messages.json | 53 +++++++++++++- apps/browser/src/_locales/es/messages.json | 53 +++++++++++++- apps/browser/src/_locales/et/messages.json | 53 +++++++++++++- apps/browser/src/_locales/eu/messages.json | 53 +++++++++++++- apps/browser/src/_locales/fa/messages.json | 53 +++++++++++++- apps/browser/src/_locales/fi/messages.json | 67 ++++++++++++++--- apps/browser/src/_locales/fil/messages.json | 53 +++++++++++++- apps/browser/src/_locales/fr/messages.json | 53 +++++++++++++- apps/browser/src/_locales/gl/messages.json | 53 +++++++++++++- apps/browser/src/_locales/he/messages.json | 53 +++++++++++++- apps/browser/src/_locales/hi/messages.json | 53 +++++++++++++- apps/browser/src/_locales/hr/messages.json | 53 +++++++++++++- apps/browser/src/_locales/hu/messages.json | 53 +++++++++++++- apps/browser/src/_locales/id/messages.json | 53 +++++++++++++- apps/browser/src/_locales/it/messages.json | 53 +++++++++++++- apps/browser/src/_locales/ja/messages.json | 53 +++++++++++++- apps/browser/src/_locales/ka/messages.json | 53 +++++++++++++- apps/browser/src/_locales/km/messages.json | 53 +++++++++++++- apps/browser/src/_locales/kn/messages.json | 53 +++++++++++++- apps/browser/src/_locales/ko/messages.json | 53 +++++++++++++- apps/browser/src/_locales/lt/messages.json | 53 +++++++++++++- apps/browser/src/_locales/lv/messages.json | 67 ++++++++++++++--- apps/browser/src/_locales/ml/messages.json | 53 +++++++++++++- apps/browser/src/_locales/mr/messages.json | 53 +++++++++++++- apps/browser/src/_locales/my/messages.json | 53 +++++++++++++- apps/browser/src/_locales/nb/messages.json | 55 +++++++++++++- apps/browser/src/_locales/ne/messages.json | 53 +++++++++++++- apps/browser/src/_locales/nl/messages.json | 53 +++++++++++++- apps/browser/src/_locales/nn/messages.json | 53 +++++++++++++- apps/browser/src/_locales/or/messages.json | 53 +++++++++++++- apps/browser/src/_locales/pl/messages.json | 53 +++++++++++++- apps/browser/src/_locales/pt_BR/messages.json | 53 +++++++++++++- apps/browser/src/_locales/pt_PT/messages.json | 53 +++++++++++++- apps/browser/src/_locales/ro/messages.json | 53 +++++++++++++- apps/browser/src/_locales/ru/messages.json | 53 +++++++++++++- apps/browser/src/_locales/si/messages.json | 53 +++++++++++++- apps/browser/src/_locales/sk/messages.json | 53 +++++++++++++- apps/browser/src/_locales/sl/messages.json | 53 +++++++++++++- apps/browser/src/_locales/sr/messages.json | 71 ++++++++++++++++--- apps/browser/src/_locales/sv/messages.json | 53 +++++++++++++- apps/browser/src/_locales/te/messages.json | 53 +++++++++++++- apps/browser/src/_locales/th/messages.json | 53 +++++++++++++- apps/browser/src/_locales/tr/messages.json | 53 +++++++++++++- apps/browser/src/_locales/uk/messages.json | 53 +++++++++++++- apps/browser/src/_locales/vi/messages.json | 53 +++++++++++++- apps/browser/src/_locales/zh_CN/messages.json | 55 +++++++++++++- apps/browser/src/_locales/zh_TW/messages.json | 53 +++++++++++++- 60 files changed, 3145 insertions(+), 85 deletions(-) diff --git a/apps/browser/src/_locales/ar/messages.json b/apps/browser/src/_locales/ar/messages.json index deef20f6a1f..c2f3c72f281 100644 --- a/apps/browser/src/_locales/ar/messages.json +++ b/apps/browser/src/_locales/ar/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Change at-risk password" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/az/messages.json b/apps/browser/src/_locales/az/messages.json index 1b7bec7367a..3550ec52462 100644 --- a/apps/browser/src/_locales/az/messages.json +++ b/apps/browser/src/_locales/az/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Riskli parolları dəyişdir" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Bitwarden-ə xoş gəlmisiniz" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Bitwarden mobil, brauzer və masaüstü tətbiqləri ilə limitsiz cihaz arasında limitsiz parol saxlayın." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/be/messages.json b/apps/browser/src/_locales/be/messages.json index 132e1a75fe1..20c5bb8ff35 100644 --- a/apps/browser/src/_locales/be/messages.json +++ b/apps/browser/src/_locales/be/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Change at-risk password" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/bg/messages.json b/apps/browser/src/_locales/bg/messages.json index d54b9d710eb..2bc42c0e6c9 100644 --- a/apps/browser/src/_locales/bg/messages.json +++ b/apps/browser/src/_locales/bg/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Промяна на парола в риск" }, + "settingsVaultOptions": { + "message": "Настройки на трезора" + }, + "emptyVaultDescription": { + "message": "Трезорът може да пази не само паролите Ви. Съхранявайте защитени данни за вход, идентификационни данни, карти и бележки." + }, "introCarouselLabel": { "message": "Добре дошли в Битуорден" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Съхранявайте неограничен брой пароли на множество устройства – с приложенията на Битуорден за мобилни телефони, браузър и компютър." + }, + "emptyVaultNudgeTitle": { + "message": "Внасяне на съществуващи пароли" + }, + "emptyVaultNudgeBody": { + "message": "Използвайте функцията за внасяне, за да прехвърлите лесно данните си за вписване в Битуорден, без да ги добавяте ръчно." + }, + "emptyVaultNudgeButton": { + "message": "Внасяне сега" + }, + "hasItemsVaultNudgeTitle": { + "message": "Добре дошли в трезора си!" + }, + "hasItemsVaultNudgeBody": { + "message": "Елементи за авт. попълване в текущата страница\nЛюбими елементи за лесен достъп\nПотърсете в трезора си за нещо друго" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/bn/messages.json b/apps/browser/src/_locales/bn/messages.json index 90965cff8d6..c14d5308a6b 100644 --- a/apps/browser/src/_locales/bn/messages.json +++ b/apps/browser/src/_locales/bn/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Change at-risk password" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/bs/messages.json b/apps/browser/src/_locales/bs/messages.json index cb845575ae8..eaa9ba863e0 100644 --- a/apps/browser/src/_locales/bs/messages.json +++ b/apps/browser/src/_locales/bs/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Change at-risk password" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/ca/messages.json b/apps/browser/src/_locales/ca/messages.json index 4394488f189..bbf6ccb9c41 100644 --- a/apps/browser/src/_locales/ca/messages.json +++ b/apps/browser/src/_locales/ca/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Change at-risk password" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/cs/messages.json b/apps/browser/src/_locales/cs/messages.json index 2e0d015387a..dfbc0acea76 100644 --- a/apps/browser/src/_locales/cs/messages.json +++ b/apps/browser/src/_locales/cs/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Změnit ohrožené heslo" }, + "settingsVaultOptions": { + "message": "Volby trezoru" + }, + "emptyVaultDescription": { + "message": "Trezor chrání více než jen Vaše hesla. Bezpečně zde uložte zabezpečená přihlášení, ID, karty a poznámky." + }, "introCarouselLabel": { "message": "Vítejte v Bitwardenu" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Uložte neomezená hesla na neomezených zařízeních s Bitwardenem na mobilu, prohlížeči a desktopové aplikaci." + }, + "emptyVaultNudgeTitle": { + "message": "Importovat existující hesla" + }, + "emptyVaultNudgeBody": { + "message": "Pomocí importu rychle přenesete přihlašovací údaje do Bitwardenu a to bez jejich ručního přidání." + }, + "emptyVaultNudgeButton": { + "message": "Importovat nyní" + }, + "hasItemsVaultNudgeTitle": { + "message": "Vítejte ve Vašem trezoru!" + }, + "hasItemsVaultNudgeBody": { + "message": "Položky automatického vyplňování pro aktuální stránku\nOblíbené položky pro snadný přístup\nNajděte v trezoru něco jiného" + }, + "newLoginNudgeTitle": { + "message": "Ušetřete čas s automatickým vyplňováním" + }, + "newLoginNudgeBody": { + "message": "Zahrne webovou stránku, takže se toto přihlášení objeví jako návrh automatického vyplňování." + }, + "newCardNudgeTitle": { + "message": "Bezproblémová online pokladna" + }, + "newCardNudgeBody": { + "message": "Karty - snadné, bezpečné a přesné vyplňování platebních formulářů." + }, + "newIdentityNudgeTitle": { + "message": "Jednodušší vytváření účtů" + }, + "newIdentityNudgeBody": { + "message": "Identity - rychlé automatické vyplňování dlouhých registračních nebo kontaktních formulářů." + }, + "newNoteNudgeTitle": { + "message": "Udržujte svá citlivá data v bezpečí" + }, + "newNoteNudgeBody": { + "message": "Poznámky - bezpečné ukládání citlivých údajů, jako jsou bankovní nebo pojišťovací údaje." + }, + "newSshNudgeTitle": { + "message": "Přístup SSH pro vývojáře" + }, + "newSshNudgeBody": { + "message": "Uložte své klíče a připojte se k SSH agentovi pro rychlé a šifrované ověření." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/cy/messages.json b/apps/browser/src/_locales/cy/messages.json index fdde829ed61..5fa135a929d 100644 --- a/apps/browser/src/_locales/cy/messages.json +++ b/apps/browser/src/_locales/cy/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Change at-risk password" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/da/messages.json b/apps/browser/src/_locales/da/messages.json index afce44f6bc2..f1c55f3557d 100644 --- a/apps/browser/src/_locales/da/messages.json +++ b/apps/browser/src/_locales/da/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Change at-risk password" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/de/messages.json b/apps/browser/src/_locales/de/messages.json index 242f3c57392..251a0323c60 100644 --- a/apps/browser/src/_locales/de/messages.json +++ b/apps/browser/src/_locales/de/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Gefährdetes Passwort ändern" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Willkommen bei Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Speicher eine unbegrenzte Anzahl von Passwörtern auf unbegrenzt vielen Geräten mit Bitwarden-Apps für Smartphones, Browser und Desktop." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/el/messages.json b/apps/browser/src/_locales/el/messages.json index d9a1e088b2d..691f546b327 100644 --- a/apps/browser/src/_locales/el/messages.json +++ b/apps/browser/src/_locales/el/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Change at-risk password" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/en_GB/messages.json b/apps/browser/src/_locales/en_GB/messages.json index f96f1016975..1e0def93584 100644 --- a/apps/browser/src/_locales/en_GB/messages.json +++ b/apps/browser/src/_locales/en_GB/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Change at-risk password" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavourite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/en_IN/messages.json b/apps/browser/src/_locales/en_IN/messages.json index 48c63c615f3..380eab31eda 100644 --- a/apps/browser/src/_locales/en_IN/messages.json +++ b/apps/browser/src/_locales/en_IN/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Change at-risk password" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavourite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/es/messages.json b/apps/browser/src/_locales/es/messages.json index 900c03d8963..76401c685ad 100644 --- a/apps/browser/src/_locales/es/messages.json +++ b/apps/browser/src/_locales/es/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Cambiar contraseña de riesgo" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Guarda contraseñas ilimitadas a través de dispositivos ilimitados con aplicaciones móviles, de navegador y de escritorio de Bitwarden." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/et/messages.json b/apps/browser/src/_locales/et/messages.json index ff7b222202b..f76adcc27e2 100644 --- a/apps/browser/src/_locales/et/messages.json +++ b/apps/browser/src/_locales/et/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Change at-risk password" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/eu/messages.json b/apps/browser/src/_locales/eu/messages.json index c87f87dbc22..cd3d58896d4 100644 --- a/apps/browser/src/_locales/eu/messages.json +++ b/apps/browser/src/_locales/eu/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Change at-risk password" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/fa/messages.json b/apps/browser/src/_locales/fa/messages.json index 656a3e6b609..b8f8d2ccdbf 100644 --- a/apps/browser/src/_locales/fa/messages.json +++ b/apps/browser/src/_locales/fa/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Change at-risk password" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/fi/messages.json b/apps/browser/src/_locales/fi/messages.json index e0da64cd0b8..c3f48b1df3b 100644 --- a/apps/browser/src/_locales/fi/messages.json +++ b/apps/browser/src/_locales/fi/messages.json @@ -1072,14 +1072,14 @@ "description": "Aria label for the view button in notification bar confirmation message" }, "notificationEditTooltip": { - "message": "Edit before saving", + "message": "Muokkaa ennen tallentamista", "description": "Tooltip and Aria label for edit button on cipher item" }, "newNotification": { - "message": "New notification" + "message": "Uusi ilmoitus" }, "labelWithNotification": { - "message": "$LABEL$: New notification", + "message": "$LABEL$: Uusi ilmoitus", "description": "Label for the notification with a new login suggestion.", "placeholders": { "label": { @@ -1089,7 +1089,7 @@ } }, "loginSaveConfirmation": { - "message": "$ITEMNAME$ saved to Bitwarden.", + "message": "$ITEMNAME$ tallennettiin Bitwardeniin.", "placeholders": { "itemName": { "content": "$1" @@ -1098,7 +1098,7 @@ "description": "Shown to user after item is saved." }, "loginUpdatedConfirmation": { - "message": "$ITEMNAME$ updated in Bitwarden.", + "message": "$ITEMNAME$ päivitettiin Bitwardeniin.", "placeholders": { "itemName": { "content": "$1" @@ -1115,11 +1115,11 @@ "description": "Button text for updating an existing login entry." }, "saveLogin": { - "message": "Save login", + "message": "Tallenna kirjautumistieto", "description": "Prompt asking the user if they want to save their login details." }, "updateLogin": { - "message": "Update existing login", + "message": "Päivitä olemassaoleva kirjautumistieto", "description": "Prompt asking the user if they want to update an existing login entry." }, "loginSaveSuccess": { @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Vaihda vaarantunut salasana" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Tervetuloa Bitwardeniin" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Tallenna rajattomasti salasanoja, rajattomalla määrällä laitteita, Bitwardenin mobiili-, selain- ja työpöytäsovelluksilla." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/fil/messages.json b/apps/browser/src/_locales/fil/messages.json index 2f1a6b3a9c9..d01670a2353 100644 --- a/apps/browser/src/_locales/fil/messages.json +++ b/apps/browser/src/_locales/fil/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Change at-risk password" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/fr/messages.json b/apps/browser/src/_locales/fr/messages.json index 696b9a75d05..b8c4fa9d011 100644 --- a/apps/browser/src/_locales/fr/messages.json +++ b/apps/browser/src/_locales/fr/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Changer le mot de passe à risque" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/gl/messages.json b/apps/browser/src/_locales/gl/messages.json index 329de4ae2be..40823d54799 100644 --- a/apps/browser/src/_locales/gl/messages.json +++ b/apps/browser/src/_locales/gl/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Change at-risk password" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/he/messages.json b/apps/browser/src/_locales/he/messages.json index 607875ae38e..a41386d9494 100644 --- a/apps/browser/src/_locales/he/messages.json +++ b/apps/browser/src/_locales/he/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "שנה סיסמה בסיכון" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/hi/messages.json b/apps/browser/src/_locales/hi/messages.json index 39a3956654a..d4fff520b87 100644 --- a/apps/browser/src/_locales/hi/messages.json +++ b/apps/browser/src/_locales/hi/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Change at-risk password" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/hr/messages.json b/apps/browser/src/_locales/hr/messages.json index 30d314d2cbd..f3e4bd840c7 100644 --- a/apps/browser/src/_locales/hr/messages.json +++ b/apps/browser/src/_locales/hr/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Promijeni rizičnu lozinku" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/hu/messages.json b/apps/browser/src/_locales/hu/messages.json index 1fcedb0f9db..07161af5727 100644 --- a/apps/browser/src/_locales/hu/messages.json +++ b/apps/browser/src/_locales/hu/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Kockázatos jelszó megváltoztatása" }, + "settingsVaultOptions": { + "message": "Széf opciók" + }, + "emptyVaultDescription": { + "message": "A széf többre alkalmas, mint a jelszavak mentése. Menthetünk belépéseket, azonosítókat, kártyákat és feljegyzéseket teljes biztonságban." + }, "introCarouselLabel": { "message": "Üdvözlet a Bitwardenben" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Mentsünk el a korlátlan jelszót korlátlan számú eszközön a Bitwarden mobil, böngésző és asztali alkalmazásokkal." + }, + "emptyVaultNudgeTitle": { + "message": "Létező jelszavak importálása" + }, + "emptyVaultNudgeBody": { + "message": "Az importálóval gyorsan átvihetünk bejelentkezéseket a Bitwardenbe anélkül, hogy manuálisan hozzáadnánk azokat." + }, + "emptyVaultNudgeButton": { + "message": "Importálás most" + }, + "hasItemsVaultNudgeTitle": { + "message": "Üdvözlet a széfben!" + }, + "hasItemsVaultNudgeBody": { + "message": "Az aktuális oldal elemeinek automatikus kitöltése\nKedvenc elemek a könnyű hozzáférés érdekében\nValami más keresése a széfben" + }, + "newLoginNudgeTitle": { + "message": "Idő megtakarítás automatikus kitöltéssel" + }, + "newLoginNudgeBody": { + "message": "Adjunk meg egy webhelyet, hogy ez a bejelentkezési név automatikusan kitöltendő javaslatként jelenjen meg." + }, + "newCardNudgeTitle": { + "message": "Zökkenőmentes online fizetés" + }, + "newCardNudgeBody": { + "message": "Kártyákkal könnyedén, biztonságosan és pontosan tölthetjük ki automatikusan a fizetési űrlapokat." + }, + "newIdentityNudgeTitle": { + "message": "Egyszerűsítsük a fiókok létrehozását" + }, + "newIdentityNudgeBody": { + "message": "Azonosítókkal gyorsan automatikusan kitölthetjük a hosszú regisztrációs vagy kapcsolatfelvételi űrlapokat." + }, + "newNoteNudgeTitle": { + "message": "Tartsuk biztonságban az érzékeny adatokat" + }, + "newNoteNudgeBody": { + "message": "Jegyzetekkel biztonságosan tárolhatjuk az érzékeny adatokat, például a banki vagy biztosítási adatokat." + }, + "newSshNudgeTitle": { + "message": "Fejlesztőbarát SSH hozzáférés" + }, + "newSshNudgeBody": { + "message": "Tároljuk a kulcsokat és csatlakozzunk az SSH-ügynökhöz a gyors, titkosított hitelesítéshez." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/id/messages.json b/apps/browser/src/_locales/id/messages.json index 2e8a19ce28e..5c77c54ecf4 100644 --- a/apps/browser/src/_locales/id/messages.json +++ b/apps/browser/src/_locales/id/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Ubah kata sandi yang berrisiko" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/it/messages.json b/apps/browser/src/_locales/it/messages.json index 498ba22cd26..724416cdcde 100644 --- a/apps/browser/src/_locales/it/messages.json +++ b/apps/browser/src/_locales/it/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Cambia parola d'accesso a rischio" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/ja/messages.json b/apps/browser/src/_locales/ja/messages.json index 00cd29e8830..be70cd7d939 100644 --- a/apps/browser/src/_locales/ja/messages.json +++ b/apps/browser/src/_locales/ja/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "危険なパスワードの変更" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Bitwarden へようこそ" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Bitwarden のモバイル、ブラウザ、デスクトップアプリでは、保存できるパスワード数やデバイス数に制限はありません。" + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/ka/messages.json b/apps/browser/src/_locales/ka/messages.json index 4c798e2b119..bb4a9be2a6c 100644 --- a/apps/browser/src/_locales/ka/messages.json +++ b/apps/browser/src/_locales/ka/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Change at-risk password" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/km/messages.json b/apps/browser/src/_locales/km/messages.json index 4f83b07506b..99a09c75d41 100644 --- a/apps/browser/src/_locales/km/messages.json +++ b/apps/browser/src/_locales/km/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Change at-risk password" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/kn/messages.json b/apps/browser/src/_locales/kn/messages.json index d12738ed231..408727f7ec8 100644 --- a/apps/browser/src/_locales/kn/messages.json +++ b/apps/browser/src/_locales/kn/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Change at-risk password" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/ko/messages.json b/apps/browser/src/_locales/ko/messages.json index 2d30255100e..01cb0fbf7c3 100644 --- a/apps/browser/src/_locales/ko/messages.json +++ b/apps/browser/src/_locales/ko/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Change at-risk password" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/lt/messages.json b/apps/browser/src/_locales/lt/messages.json index 465f0fdad38..51879334614 100644 --- a/apps/browser/src/_locales/lt/messages.json +++ b/apps/browser/src/_locales/lt/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Change at-risk password" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/lv/messages.json b/apps/browser/src/_locales/lv/messages.json index 2d73bacc5c9..6c39d9ff3e6 100644 --- a/apps/browser/src/_locales/lv/messages.json +++ b/apps/browser/src/_locales/lv/messages.json @@ -437,7 +437,7 @@ "message": "Sinhronizēt glabātavu" }, "lastSync": { - "message": "Pēdējā sinhronizācija:" + "message": "Pēdējā sinhronizēšana:" }, "passGen": { "message": "Paroļu veidotājs" @@ -947,10 +947,10 @@ "message": "Noskaties mūsu uzsākšanas pamācību, lai uzzinātu, kā iegūt vislielāko labumu no pārlūka paplašinājuma!" }, "syncingComplete": { - "message": "Sinhronizācija pabeigta" + "message": "Sinhronizēšana pabeigta" }, "syncingFailed": { - "message": "Sinhronizācija neizdevās" + "message": "Sinhronizēšana neizdevās" }, "passwordCopied": { "message": "Parole ievietota starpliktuvē" @@ -1543,7 +1543,7 @@ "message": "Jāievada e-pastā nosūtītais kods." }, "selfHostedEnvironment": { - "message": "Pašuzturēta vide" + "message": "Pašmitināta vide" }, "selfHostedBaseUrlHint": { "message": "Jānorāda sava pašizvietotā Bitward servera pamata URL. Piemērs: https://bitwarden.uznemums.lv" @@ -2370,7 +2370,7 @@ "message": "Netika atrastas atsvaidzināšanas pilnvaras vai API atslēgas. Lūgums mēģināt izrakstīties un atkal pieteikties." }, "desktopSyncVerificationTitle": { - "message": "Darbvirsmas sinhronizācijas apstiprinājums" + "message": "Darbvirsmas sinhronizēšanas apliecinājums" }, "desktopIntegrationVerificationText": { "message": "Lūgumus pārliecināties, ka darbvirsmas lietotne rāda šo atpazīšanas vārdkopu:" @@ -2996,7 +2996,7 @@ "message": "Nav atrasts neviens neatkārtojams identifikators" }, "convertOrganizationEncryptionDesc": { - "message": "$ORGANIZATION$ izmanto vienoto pieteikšanos ar pašizvietotu atslēgu serveri. Tās dalībniekiem vairs nav nepieciešama galvenā parole, lai pieteiktos.", + "message": "$ORGANIZATION$ izmanto vienoto pieteikšanos ar pašmitinātu atslēgu serveri. Tās dalībniekiem vairs nav nepieciešama galvenā parole, lai pieteiktos.", "placeholders": { "organization": { "content": "$1", @@ -3315,7 +3315,7 @@ "message": "Servera versija" }, "selfHostedServer": { - "message": "pašizvietots" + "message": "pašmitināts" }, "thirdParty": { "message": "Trešās puses" @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Mainīt riskam pakļautu paroli" }, + "settingsVaultOptions": { + "message": "Glabātavas iespējas" + }, + "emptyVaultDescription": { + "message": "Glabātava aizsargā vairāk kā tikai paroles. Drošā veidā glabā šeit pieteikšanās vienumus, identitātes, kartes un piezīmes!" + }, "introCarouselLabel": { "message": "Laipni lūdzam Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Neierobežotu paroļu skaitu var saglabāt neierobežotā ierīdžu daudzumā ar Bitwarden viedtālruņa, pārlūka un darbvirsmas lietotni." + }, + "emptyVaultNudgeTitle": { + "message": "Ievietot esošas paroles" + }, + "emptyVaultNudgeBody": { + "message": "Ievietotājs ir izmantojams, lai pieteikšanās vienumus ātri pārnest uz Bitwarden bez pašrocīgas to pievienošanas." + }, + "emptyVaultNudgeButton": { + "message": "Ievietot tagad" + }, + "hasItemsVaultNudgeTitle": { + "message": "Laipni lūdzam Tavā glabātavā!" + }, + "hasItemsVaultNudgeBody": { + "message": "Automātiskās aizpldes vienumu pašreizējai lapai\nIzlases vienumi vieglai piekļuvei\nPārējo var meklēt glabātavā" + }, + "newLoginNudgeTitle": { + "message": "Laika ietaupīšana ar automātisko aizpildi" + }, + "newLoginNudgeBody": { + "message": "Iekļauj tīmekļvietni, lai šis pieteikšanās vienums parādītos kā automātiskās aizpildes ieteikums!" + }, + "newCardNudgeTitle": { + "message": "Plūdena apmaksa tiešsaistē" + }, + "newCardNudgeBody": { + "message": "Ar kartēm ir viegli automātiski aizpildīt maksājumu veidlapu droši un rūpīgi." + }, + "newIdentityNudgeTitle": { + "message": "Kontu izveidošanas vienkāršošana" + }, + "newIdentityNudgeBody": { + "message": "Ar identitātēm var ātri automātiski aizpildīt garas reģistrēšanās vai saziņas veidlapas." + }, + "newNoteNudgeTitle": { + "message": "Turi savus jūtīgos datus drošībā" + }, + "newNoteNudgeBody": { + "message": "Piezīmēs var droši glabāt jūtīgus datus, piemēram, bankas vai apdrošināšanas informāciju." + }, + "newSshNudgeTitle": { + "message": "Izstrādātājiem draudzīga SSH piekļuve" + }, + "newSshNudgeBody": { + "message": "Glabā savas atslēgas un savienojies ar SSH aģentu ātrai, šifrētai autentificēšanai!" } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/ml/messages.json b/apps/browser/src/_locales/ml/messages.json index e5b2e89cee6..15d35d29c76 100644 --- a/apps/browser/src/_locales/ml/messages.json +++ b/apps/browser/src/_locales/ml/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Change at-risk password" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/mr/messages.json b/apps/browser/src/_locales/mr/messages.json index 221015a7b42..8dee37bfcb8 100644 --- a/apps/browser/src/_locales/mr/messages.json +++ b/apps/browser/src/_locales/mr/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Change at-risk password" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/my/messages.json b/apps/browser/src/_locales/my/messages.json index 4f83b07506b..99a09c75d41 100644 --- a/apps/browser/src/_locales/my/messages.json +++ b/apps/browser/src/_locales/my/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Change at-risk password" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/nb/messages.json b/apps/browser/src/_locales/nb/messages.json index 81c7682680c..f3d87aaa067 100644 --- a/apps/browser/src/_locales/nb/messages.json +++ b/apps/browser/src/_locales/nb/messages.json @@ -1185,7 +1185,7 @@ "message": "Ja, oppdater nå" }, "notificationUnlockDesc": { - "message": "Unlock your Bitwarden vault to complete the autofill request." + "message": "Lås opp Bitwarden-hvelvet ditt for å utføre auto-utfyllingen." }, "notificationUnlock": { "message": "Lås opp" @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Change at-risk password" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/ne/messages.json b/apps/browser/src/_locales/ne/messages.json index 4f83b07506b..99a09c75d41 100644 --- a/apps/browser/src/_locales/ne/messages.json +++ b/apps/browser/src/_locales/ne/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Change at-risk password" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/nl/messages.json b/apps/browser/src/_locales/nl/messages.json index afe7821e096..2934c5da3fd 100644 --- a/apps/browser/src/_locales/nl/messages.json +++ b/apps/browser/src/_locales/nl/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Risicovol wachtwoord wijzigen" }, + "settingsVaultOptions": { + "message": "Kluis-instellingen" + }, + "emptyVaultDescription": { + "message": "De kluis beschermt meer dan alleen je wachtwoorden. Sla hier beveiligde inloggegevens, ID's, kaarten en notities op." + }, "introCarouselLabel": { "message": "Welkom bij Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Onbeperkt wachtwoorden opslaan op alle apparaten met Bitwarden-apps voor mobiel, browser en desktop." + }, + "emptyVaultNudgeTitle": { + "message": "Bestaande wachtwoorden importeren" + }, + "emptyVaultNudgeBody": { + "message": "Gebruik de importer om snel logins naar Bitwarden over te dragen zonder ze handmatig toe te voegen." + }, + "emptyVaultNudgeButton": { + "message": "Nu importeren" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welkom in je kluis!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items voor de huidige pagina\nFavoriete items voor eenvoudige toegang\nDoorzoek je kluis naar iets anders" + }, + "newLoginNudgeTitle": { + "message": "Tijd besparen met automatisch aanvullen" + }, + "newLoginNudgeBody": { + "message": "Voeg een website toe zodat deze login wordt weergegeven als een automatische invulsuggestie." + }, + "newCardNudgeTitle": { + "message": "Naadloos online afrekenen" + }, + "newCardNudgeBody": { + "message": "Met kaarten gemakkelijk, veilig en accuraat automatisch invullen van betaalformulieren." + }, + "newIdentityNudgeTitle": { + "message": "Vereenvoudig het aanmaken van accounts" + }, + "newIdentityNudgeBody": { + "message": "Met identiteiten vul je lange registratie- of contactformulieren snel automatisch in." + }, + "newNoteNudgeTitle": { + "message": "Houd je gevoelige gegevens veilig" + }, + "newNoteNudgeBody": { + "message": "Met notities veilig opslaan van gevoelige gegevens zoals bank- of verzekeringsgegevens." + }, + "newSshNudgeTitle": { + "message": "Ontwikkelaars-vriendelijke SSH-toegang" + }, + "newSshNudgeBody": { + "message": "Sla je sleutels op en verbind met de SSH-agent voor snelle, versleutelde authenticatie." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/nn/messages.json b/apps/browser/src/_locales/nn/messages.json index 4f83b07506b..99a09c75d41 100644 --- a/apps/browser/src/_locales/nn/messages.json +++ b/apps/browser/src/_locales/nn/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Change at-risk password" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/or/messages.json b/apps/browser/src/_locales/or/messages.json index 4f83b07506b..99a09c75d41 100644 --- a/apps/browser/src/_locales/or/messages.json +++ b/apps/browser/src/_locales/or/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Change at-risk password" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/pl/messages.json b/apps/browser/src/_locales/pl/messages.json index 1cc26eb5ef1..042cafb0300 100644 --- a/apps/browser/src/_locales/pl/messages.json +++ b/apps/browser/src/_locales/pl/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Zmień hasło zagrożone" }, + "settingsVaultOptions": { + "message": "Ustawienia Sejfu" + }, + "emptyVaultDescription": { + "message": "Sejf chroni nie tylko Twoje hasła. Przechowuj tutaj bezpiecznie loginy, identyfikatory, karty i notatki." + }, "introCarouselLabel": { "message": "Witaj w Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Zapisuj nieograniczoną liczbę haseł na nieograniczonej liczbie urządzeń dzięki aplikacjom Bitwarden na urządzenia mobilne, przeglądarki i komputery stacjonarne." + }, + "emptyVaultNudgeTitle": { + "message": "Importuj istniejące hasła" + }, + "emptyVaultNudgeBody": { + "message": "Użyj importera, aby szybko przenieść loginy do Bitwarden bez ręcznego dodawania ich." + }, + "emptyVaultNudgeButton": { + "message": "Importuj teraz" + }, + "hasItemsVaultNudgeTitle": { + "message": "Witaj w Twoim Sejfie!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autouzupełnianie elementów dla bieżącej strony\nUlubione elementy dla łatwego dostępu\nPrzeszukaj swój sejf w poszukiwaniu czegoś innego" + }, + "newLoginNudgeTitle": { + "message": "Oszczędzaj czas dzięki autouzupełnianiu" + }, + "newLoginNudgeBody": { + "message": "Dołącz stronę internetową, aby ten login pojawił się jako sugestia autouzupełniania." + }, + "newCardNudgeTitle": { + "message": "Bezproblemowe zamówienia online" + }, + "newCardNudgeBody": { + "message": "Z kartami łatwe autouzupełnianie formularzy płatności w sposób bezpieczny i dokładny." + }, + "newIdentityNudgeTitle": { + "message": "Uprość tworzenie kont" + }, + "newIdentityNudgeBody": { + "message": "Z tożsamościami, szybko autouzupełnij długie formularze rejestracyjne lub kontaktowe." + }, + "newNoteNudgeTitle": { + "message": "Zachowaj bezpieczeństwo wrażliwych danych" + }, + "newNoteNudgeBody": { + "message": "Z notatkami bezpiecznie przechowuj dane szczególnie chronione, takie jak dane bankowe lub ubezpieczeniowe." + }, + "newSshNudgeTitle": { + "message": "Przyjazny dla deweloperów dostęp SSH" + }, + "newSshNudgeBody": { + "message": "Przechowuj swoje klucze i połącz się z agentem SSH dla szybkiego, szyfrowanego uwierzytelniania." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/pt_BR/messages.json b/apps/browser/src/_locales/pt_BR/messages.json index cd516ec83a6..c4fb561b5be 100644 --- a/apps/browser/src/_locales/pt_BR/messages.json +++ b/apps/browser/src/_locales/pt_BR/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Alterar senhas vulneráveis" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Bem-vindo(a) ao Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Guarde quantas senhas quiser e acesse de qualquer lugar com o Bitwarden. No seu celular, navegador e computador." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/pt_PT/messages.json b/apps/browser/src/_locales/pt_PT/messages.json index 16746dac172..0af5cb2d13f 100644 --- a/apps/browser/src/_locales/pt_PT/messages.json +++ b/apps/browser/src/_locales/pt_PT/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Alterar palavra-passe em risco" }, + "settingsVaultOptions": { + "message": "Opções do cofre" + }, + "emptyVaultDescription": { + "message": "O cofre protege mais do que apenas as suas palavras-passe. Guarde aqui credenciais, IDs, cartões e notas de forma segura." + }, "introCarouselLabel": { "message": "Bem-vindo ao Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Guarde palavras-passe ilimitadas em dispositivos ilimitados com as apps Bitwarden para telemóvel, navegador e computador." + }, + "emptyVaultNudgeTitle": { + "message": "Importar palavras-passe existentes" + }, + "emptyVaultNudgeBody": { + "message": "Utilize o importador para transferir rapidamente as credenciais para o Bitwarden sem as adicionar manualmente." + }, + "emptyVaultNudgeButton": { + "message": "Importar agora" + }, + "hasItemsVaultNudgeTitle": { + "message": "Bem-vindo ao seu cofre!" + }, + "hasItemsVaultNudgeBody": { + "message": "Preenchimento automático de itens para a página atual\nItens favoritos para um acesso fácil\nProcurar outra coisa no seu cofre" + }, + "newLoginNudgeTitle": { + "message": "Poupe tempo com o preenchimento automático" + }, + "newLoginNudgeBody": { + "message": "Inclua um site para que esta credencial apareça como uma sugestão de preenchimento automático." + }, + "newCardNudgeTitle": { + "message": "Pagamentos online sem problemas" + }, + "newCardNudgeBody": { + "message": "Com os cartões, preencha facilmente formulários de pagamento de forma segura e exata." + }, + "newIdentityNudgeTitle": { + "message": "Simplifique a criação de contas" + }, + "newIdentityNudgeBody": { + "message": "Com as identidades, preencha rapidamente formulários de registo ou de contacto longos." + }, + "newNoteNudgeTitle": { + "message": "Mantenha os seus dados sensíveis seguros" + }, + "newNoteNudgeBody": { + "message": "Com as notas, armazene de forma segura dados sensíveis, como dados bancários ou de seguros." + }, + "newSshNudgeTitle": { + "message": "Acesso SSH de fácil utilização pelos programadores" + }, + "newSshNudgeBody": { + "message": "Guarde as suas chaves e ligue-se ao agente SSH para uma autenticação rápida e encriptada." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/ro/messages.json b/apps/browser/src/_locales/ro/messages.json index ba3cbcf9469..bf08e0969b2 100644 --- a/apps/browser/src/_locales/ro/messages.json +++ b/apps/browser/src/_locales/ro/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Change at-risk password" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/ru/messages.json b/apps/browser/src/_locales/ru/messages.json index cd146190373..f6343f6d2bb 100644 --- a/apps/browser/src/_locales/ru/messages.json +++ b/apps/browser/src/_locales/ru/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Изменить пароль, подверженный риску" }, + "settingsVaultOptions": { + "message": "Настройки хранилища" + }, + "emptyVaultDescription": { + "message": "Хранилище защищает не только ваши пароли. Логины, идентификаторы, карты и заметки в нем надежно защищены." + }, "introCarouselLabel": { "message": "Добро пожаловать в Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Сохраняйте неограниченное количество паролей на неограниченном количестве устройств с помощью мобильных, браузерных и десктопных приложений Bitwarden." + }, + "emptyVaultNudgeTitle": { + "message": "Импорт существующих паролей" + }, + "emptyVaultNudgeBody": { + "message": "Используйте импортер, чтобы быстро перенести логины в Bitwarden без их ручного добавления." + }, + "emptyVaultNudgeButton": { + "message": "Импортировать сейчас" + }, + "hasItemsVaultNudgeTitle": { + "message": "Добро пожаловать в ваше хранилище!" + }, + "hasItemsVaultNudgeBody": { + "message": "Автозаполнение элементов для текущей страницы\nИзбранные элементы для легкого доступа\nПоиск в хранилище для чего-либо еще" + }, + "newLoginNudgeTitle": { + "message": "Экономьте время с помощью автозаполнения" + }, + "newLoginNudgeBody": { + "message": "Включите сайт, чтобы этот логин отображался в качестве предложения для автозаполнения." + }, + "newCardNudgeTitle": { + "message": "Оформление заказа через интернет" + }, + "newCardNudgeBody": { + "message": "С помощью карт можно легко и безопасно автоматически заполнять формы оплаты." + }, + "newIdentityNudgeTitle": { + "message": "Упрощение создания аккаунтов" + }, + "newIdentityNudgeBody": { + "message": "С помощью личностей можно быстро заполнять длинные регистрационные или контактные формы." + }, + "newNoteNudgeTitle": { + "message": "Храните ваши конфиденциальные данные в безопасности" + }, + "newNoteNudgeBody": { + "message": "С помощью заметок можно надежно хранить конфиденциальные данные, например, банковские или страховые реквизиты." + }, + "newSshNudgeTitle": { + "message": "Удобный для разработчиков SSH-доступ" + }, + "newSshNudgeBody": { + "message": "Храните свои ключи и подключайтесь с помощью агента SSH для быстрой и зашифрованной аутентификации." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/si/messages.json b/apps/browser/src/_locales/si/messages.json index 166b5490003..a5f7e772505 100644 --- a/apps/browser/src/_locales/si/messages.json +++ b/apps/browser/src/_locales/si/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Change at-risk password" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/sk/messages.json b/apps/browser/src/_locales/sk/messages.json index 70ff5bbfc27..5f65633e44c 100644 --- a/apps/browser/src/_locales/sk/messages.json +++ b/apps/browser/src/_locales/sk/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Zmeniť rizikové heslá" }, + "settingsVaultOptions": { + "message": "Možnosti trezoru" + }, + "emptyVaultDescription": { + "message": "Trezor chráni viac ako len heslá. Môžete tu bezpečne ukladať prihlasovacie údaje, identifikačné údaje, karty a poznámky." + }, "introCarouselLabel": { "message": "Vitajte v Bitwardene" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Ukladajte neobmedzený počet hesiel na neobmedzenom počte zariadení pomocou mobilných aplikácií, prehliadačov a desktopových aplikácií Bitwardenu." + }, + "emptyVaultNudgeTitle": { + "message": "Import existujúcich hesiel" + }, + "emptyVaultNudgeBody": { + "message": "Pomocou nástroja na import môžete rýchlo preniesť prihlasovacie údaje do Bitwardenu bez ručného pridávania." + }, + "emptyVaultNudgeButton": { + "message": "Importovať teraz" + }, + "hasItemsVaultNudgeTitle": { + "message": "Vitajte vo svojom trezore!" + }, + "hasItemsVaultNudgeBody": { + "message": "Automatické vypĺňanie položiek pre aktuálnu stránku\nObľúbené položky pre ľahký prístup\nVyhľadajte v trezore niečo iné" + }, + "newLoginNudgeTitle": { + "message": "Ušetrite čas s automatickým vypĺňaním" + }, + "newLoginNudgeBody": { + "message": "Zadajte webovú stránku, aby sa tieto prihlasovacie údaje zobrazili ako návrh na automatické vyplnenie." + }, + "newCardNudgeTitle": { + "message": "Bezproblémová online registrácia" + }, + "newCardNudgeBody": { + "message": "S kartami môžete jednoducho, bezpečne a presne automaticky vypĺňať platobné formuláre." + }, + "newIdentityNudgeTitle": { + "message": "Zjednodušenie vytvárania účtov" + }, + "newIdentityNudgeBody": { + "message": "Pomocou identít môžete rýchlo automaticky vypĺňať dlhé registračné alebo kontaktné formuláre." + }, + "newNoteNudgeTitle": { + "message": "Udržujte svoje citlivé údaje v bezpečí" + }, + "newNoteNudgeBody": { + "message": "Pomocou poznámok môžete bezpečne ukladať citlivé údaje, napríklad bankové údaje alebo údaje o poistení." + }, + "newSshNudgeTitle": { + "message": "Prístup SSH vhodný pre vývojárov" + }, + "newSshNudgeBody": { + "message": "Uložte si kľúče a pripojte sa pomocou agenta SSH na rýchle šifrované overovanie." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/sl/messages.json b/apps/browser/src/_locales/sl/messages.json index b0c815f2089..c7585878d8a 100644 --- a/apps/browser/src/_locales/sl/messages.json +++ b/apps/browser/src/_locales/sl/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Change at-risk password" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/sr/messages.json b/apps/browser/src/_locales/sr/messages.json index bc1304270f8..a5b18b0da21 100644 --- a/apps/browser/src/_locales/sr/messages.json +++ b/apps/browser/src/_locales/sr/messages.json @@ -887,7 +887,7 @@ "message": "Следите наведене кораке да бисте завршили пријављивање." }, "followTheStepsBelowToFinishLoggingInWithSecurityKey": { - "message": "Follow the steps below to finish logging in with your security key." + "message": "Следите наведене кораке да бисте завршили пријаву са својим безбедносним кључем." }, "restartRegistration": { "message": "Поново покрените регистрацију" @@ -1063,7 +1063,7 @@ "message": "Сачувај" }, "notificationViewAria": { - "message": "View $ITEMNAME$, opens in new window", + "message": "Преглед $ITEMNAME$, отвара се у новом прозору", "placeholders": { "itemName": { "content": "$1" @@ -1072,14 +1072,14 @@ "description": "Aria label for the view button in notification bar confirmation message" }, "notificationEditTooltip": { - "message": "Edit before saving", + "message": "Уреди пре сачувавања", "description": "Tooltip and Aria label for edit button on cipher item" }, "newNotification": { - "message": "New notification" + "message": "Ново обавештење" }, "labelWithNotification": { - "message": "$LABEL$: New notification", + "message": "$LABEL$: Ново обавештење", "description": "Label for the notification with a new login suggestion.", "placeholders": { "label": { @@ -1089,7 +1089,7 @@ } }, "loginSaveConfirmation": { - "message": "$ITEMNAME$ saved to Bitwarden.", + "message": "$ITEMNAME$ сачувано и Bitwarden.", "placeholders": { "itemName": { "content": "$1" @@ -1098,7 +1098,7 @@ "description": "Shown to user after item is saved." }, "loginUpdatedConfirmation": { - "message": "$ITEMNAME$ updated in Bitwarden.", + "message": "$ITEMNAME$ ажурирано у Bitwarden.", "placeholders": { "itemName": { "content": "$1" @@ -1115,11 +1115,11 @@ "description": "Button text for updating an existing login entry." }, "saveLogin": { - "message": "Save login", + "message": "Сачувати пријаву", "description": "Prompt asking the user if they want to save their login details." }, "updateLogin": { - "message": "Update existing login", + "message": "Ажурирати постојећу пријаву", "description": "Prompt asking the user if they want to update an existing login entry." }, "loginSaveSuccess": { @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Променити ризичну лозинку" }, + "settingsVaultOptions": { + "message": "Опције сефа" + }, + "emptyVaultDescription": { + "message": "Сеф штити више од само ваших лозинки. Овде безбедно чувајте безбедне пријаве, личне карте, картице и белешке." + }, "introCarouselLabel": { "message": "Добродошли у Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Сачувајте неограничене лозинке на неограниченим уређајима помоћу Bitwarden мобилних апликација, претраживача и десктоп апликација." + }, + "emptyVaultNudgeTitle": { + "message": "Увоз постојеће лозинке" + }, + "emptyVaultNudgeBody": { + "message": "Користите увозник да брзо преносите пријаве у Bitwarden без да их ручно додате." + }, + "emptyVaultNudgeButton": { + "message": "Увези сада" + }, + "hasItemsVaultNudgeTitle": { + "message": "Добродошли у ваш сеф!" + }, + "hasItemsVaultNudgeBody": { + "message": "Ауто-пуњење предмета за тренутну страницу\nОмиљени предмети за лак приступ\nПретражите сеф за нешто друго" + }, + "newLoginNudgeTitle": { + "message": "Уштедите време са ауто-пуњењем" + }, + "newLoginNudgeBody": { + "message": "Укључите веб страницу тако да се ова пријава појављује као предлог за ауто-пуњење." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "Са картицама, лако и сигурносно попуните формуларе за плаћање." + }, + "newIdentityNudgeTitle": { + "message": "Поједноставите креирање налога" + }, + "newIdentityNudgeBody": { + "message": "Са идентитетима, брзо попуните регистрације или контактних образаци." + }, + "newNoteNudgeTitle": { + "message": "Држите на сигурном своје осетљиве податке" + }, + "newNoteNudgeBody": { + "message": "Са белешкама, сигурно чувајте осетљиве податке попут банкарског или подаци о осигурању." + }, + "newSshNudgeTitle": { + "message": "Лак SSH приступ" + }, + "newSshNudgeBody": { + "message": "Чувајте кључеве и повежите се са SSH агент за брзу, шифровану аутентификацију." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/sv/messages.json b/apps/browser/src/_locales/sv/messages.json index edd000c97cf..27443c64140 100644 --- a/apps/browser/src/_locales/sv/messages.json +++ b/apps/browser/src/_locales/sv/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Change at-risk password" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/te/messages.json b/apps/browser/src/_locales/te/messages.json index 4f83b07506b..99a09c75d41 100644 --- a/apps/browser/src/_locales/te/messages.json +++ b/apps/browser/src/_locales/te/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Change at-risk password" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/th/messages.json b/apps/browser/src/_locales/th/messages.json index f9245cc7186..f6c8a9f0584 100644 --- a/apps/browser/src/_locales/th/messages.json +++ b/apps/browser/src/_locales/th/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Change at-risk password" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/tr/messages.json b/apps/browser/src/_locales/tr/messages.json index 57707940772..052941f2281 100644 --- a/apps/browser/src/_locales/tr/messages.json +++ b/apps/browser/src/_locales/tr/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Change at-risk password" }, + "settingsVaultOptions": { + "message": "Kasa seçenekleri" + }, + "emptyVaultDescription": { + "message": "Kasanız sadece parolalarınız için değil. Hesaplarınızı, kimliklerinizi, kredi kartlarınızı ve notlarınızı da güvenle burada depolayabilirsiniz." + }, "introCarouselLabel": { "message": "Bitwarden’a hoş geldiniz" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Bitwarden mobil, tarayıcı ve masaüstü uygulamalarıyla istediğiniz kadar cihaza istediğiniz kadar parola kaydedebilirsiniz." + }, + "emptyVaultNudgeTitle": { + "message": "Mevcut parolaları içe aktar" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Şimdi içe aktar" + }, + "hasItemsVaultNudgeTitle": { + "message": "Kasanıza hoş geldiniz!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Otomatik doldurmayla zaman kazanın" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Kesintisiz çevrimiçi alışveriş" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Hesap oluşturmak artık daha basit" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Hassas verilerinizi güvende tutun" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/uk/messages.json b/apps/browser/src/_locales/uk/messages.json index aebdce5b8f0..7b62690d03c 100644 --- a/apps/browser/src/_locales/uk/messages.json +++ b/apps/browser/src/_locales/uk/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Змінити ризикований пароль" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Вітаємо в Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Зберігайте скільки завгодно паролів на необмеженій кількості пристроїв, використовуючи Bitwarden для мобільних пристроїв, браузерів та комп'ютерів." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/vi/messages.json b/apps/browser/src/_locales/vi/messages.json index c2f46945323..280a590033a 100644 --- a/apps/browser/src/_locales/vi/messages.json +++ b/apps/browser/src/_locales/vi/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Change at-risk password" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/zh_CN/messages.json b/apps/browser/src/_locales/zh_CN/messages.json index d11f8d7bbd7..521c72a2fb5 100644 --- a/apps/browser/src/_locales/zh_CN/messages.json +++ b/apps/browser/src/_locales/zh_CN/messages.json @@ -2180,7 +2180,7 @@ "message": "使用此用户名" }, "securePasswordGenerated": { - "message": "安全的密码生成好了!别忘了在网站上也更新一下您的密码。" + "message": "安全的密码已生成!不要忘记在网站上更新您的密码。" }, "useGeneratorHelpTextPartOne": { "message": "使用生成器", @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "更改有风险的密码" }, + "settingsVaultOptions": { + "message": "密码库选项" + }, + "emptyVaultDescription": { + "message": "密码库不仅保护您的密码。在这里还可以安全地存储登录、ID、支付卡和笔记。" + }, "introCarouselLabel": { "message": "欢迎使用 Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "使用 Bitwarden 移动端、浏览器和桌面 App 在无限制的设备上保存无限数量的密码。" + }, + "emptyVaultNudgeTitle": { + "message": "导入现有密码" + }, + "emptyVaultNudgeBody": { + "message": "使用导入器快速将登录传输到 Bitwarden 而无需手动添加。" + }, + "emptyVaultNudgeButton": { + "message": "立即导入" + }, + "hasItemsVaultNudgeTitle": { + "message": "欢迎来到您的密码库!" + }, + "hasItemsVaultNudgeBody": { + "message": "自动填充项目用于当前页面\n收藏夹项目用于轻松访问\n搜索密码库用于其他目的" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} diff --git a/apps/browser/src/_locales/zh_TW/messages.json b/apps/browser/src/_locales/zh_TW/messages.json index dde0607ee18..d122f7abab2 100644 --- a/apps/browser/src/_locales/zh_TW/messages.json +++ b/apps/browser/src/_locales/zh_TW/messages.json @@ -5201,6 +5201,12 @@ "changeAtRiskPassword": { "message": "Change at-risk password" }, + "settingsVaultOptions": { + "message": "Vault options" + }, + "emptyVaultDescription": { + "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + }, "introCarouselLabel": { "message": "Welcome to Bitwarden" }, @@ -5227,5 +5233,50 @@ }, "secureDevicesBody": { "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + }, + "emptyVaultNudgeTitle": { + "message": "Import existing passwords" + }, + "emptyVaultNudgeBody": { + "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + }, + "emptyVaultNudgeButton": { + "message": "Import now" + }, + "hasItemsVaultNudgeTitle": { + "message": "Welcome to your vault!" + }, + "hasItemsVaultNudgeBody": { + "message": "Autofill items for the current page\nFavorite items for easy access\nSearch your vault for something else" + }, + "newLoginNudgeTitle": { + "message": "Save time with autofill" + }, + "newLoginNudgeBody": { + "message": "Include a Website so this login appears as an autofill suggestion." + }, + "newCardNudgeTitle": { + "message": "Seamless online checkout" + }, + "newCardNudgeBody": { + "message": "With cards, easily autofill payment forms securely and accurately." + }, + "newIdentityNudgeTitle": { + "message": "Simplify creating accounts" + }, + "newIdentityNudgeBody": { + "message": "With identities, quickly autofill long registration or contact forms." + }, + "newNoteNudgeTitle": { + "message": "Keep your sensitive data safe" + }, + "newNoteNudgeBody": { + "message": "With notes, securely store sensitive data like banking or insurance details." + }, + "newSshNudgeTitle": { + "message": "Developer-friendly SSH access" + }, + "newSshNudgeBody": { + "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication." } -} \ No newline at end of file +} From d9fee1d1ad899ee73849c7cfa79f9bddbeb28139 Mon Sep 17 00:00:00 2001 From: ronymc Date: Fri, 2 May 2025 16:03:14 +0200 Subject: [PATCH 050/116] Fix-Reordering URIs in an item loses match detection for all URIs #14188. (#14315) Co-authored-by: SmithThe4th --- .../components/autofill-options/autofill-options.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 5b1e4eca103..ac670a39335 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 @@ -239,7 +239,7 @@ export class AutofillOptionsComponent implements OnInit { (control) => Object.assign(new LoginUriView(), { uri: control.value.uri, - matchDetection: control.value.matchDetection ?? null, + match: control.value.matchDetection ?? null, }) as LoginUriView, ); return cipher; From ebe3e98a1fbf4ace7a9f14795120840d64c9427e Mon Sep 17 00:00:00 2001 From: Nick Krantz <125900171+nick-livefront@users.noreply.github.com> Date: Fri, 2 May 2025 10:11:29 -0500 Subject: [PATCH 051/116] [PM-21022] Remove fido2Credentials when cloning a cipher (#14573) * remove fido2Credentials from cipherView when cloning a cipher * add check for login on cloning cipher --- .../components/cipher-form.component.spec.ts | 39 ++++++++++++++++++- .../components/cipher-form.component.ts | 4 ++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/libs/vault/src/cipher-form/components/cipher-form.component.spec.ts b/libs/vault/src/cipher-form/components/cipher-form.component.spec.ts index f53dbfbbda1..4c61ad5d2d5 100644 --- a/libs/vault/src/cipher-form/components/cipher-form.component.spec.ts +++ b/libs/vault/src/cipher-form/components/cipher-form.component.spec.ts @@ -1,13 +1,17 @@ import { ChangeDetectorRef } from "@angular/core"; import { ComponentFixture, TestBed } from "@angular/core/testing"; import { ReactiveFormsModule } from "@angular/forms"; +import { mock } from "jest-mock-extended"; import { ViewCacheService } from "@bitwarden/angular/platform/abstractions/view-cache.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { Cipher } from "@bitwarden/common/vault/models/domain/cipher"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { Fido2CredentialView } from "@bitwarden/common/vault/models/view/fido2-credential.view"; import { ToastService } from "@bitwarden/components"; +import { CipherFormConfig } from "../abstractions/cipher-form-config.service"; import { CipherFormService } from "../abstractions/cipher-form.service"; import { CipherFormCacheService } from "../services/default-cipher-form-cache.service"; @@ -17,20 +21,24 @@ describe("CipherFormComponent", () => { let component: CipherFormComponent; let fixture: ComponentFixture; + const decryptCipher = jest.fn().mockResolvedValue(new CipherView()); + beforeEach(async () => { + decryptCipher.mockClear(); + await TestBed.configureTestingModule({ imports: [CipherFormComponent, ReactiveFormsModule], providers: [ { provide: ChangeDetectorRef, useValue: {} }, { provide: I18nService, useValue: { t: (key: string) => key } }, { provide: ToastService, useValue: { showToast: jest.fn() } }, - { provide: CipherFormService, useValue: { saveCipher: jest.fn() } }, + { provide: CipherFormService, useValue: { saveCipher: jest.fn(), decryptCipher } }, { provide: CipherFormCacheService, useValue: { init: jest.fn(), getCachedCipherView: jest.fn() }, }, { provide: ViewCacheService, useValue: { signal: jest.fn(() => () => null) } }, - { provide: ConfigService, useValue: {} }, + { provide: ConfigService, useValue: mock() }, ], }).compileComponents(); }); @@ -87,4 +95,31 @@ describe("CipherFormComponent", () => { expect(component.website).toEqual("https://example.com"); }); }); + + describe("clone", () => { + const cipherView = new CipherView(); + cipherView.id = "test-id"; + cipherView.login.fido2Credentials = [new Fido2CredentialView()]; + + beforeEach(() => { + component.config = { + mode: "clone", + originalCipher: new Cipher(), + } as CipherFormConfig; + + decryptCipher.mockResolvedValue(cipherView); + }); + + it("clears id on updatedCipherView", async () => { + await component.ngOnInit(); + + expect(component["updatedCipherView"]?.id).toBeNull(); + }); + + it("clears fido2Credentials on updatedCipherView", async () => { + await component.ngOnInit(); + + expect(component["updatedCipherView"]?.login.fido2Credentials).toBeNull(); + }); + }); }); 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 96e1328338b..8b99b60bc16 100644 --- a/libs/vault/src/cipher-form/components/cipher-form.component.ts +++ b/libs/vault/src/cipher-form/components/cipher-form.component.ts @@ -243,6 +243,10 @@ export class CipherFormComponent implements AfterViewInit, OnInit, OnChanges, Ci if (this.config.mode === "clone") { this.updatedCipherView.id = null; + + if (this.updatedCipherView.login) { + this.updatedCipherView.login.fido2Credentials = null; + } } } else { this.updatedCipherView.type = this.config.cipherType; From e23bc6b77be8944618b4dd666946c49d38719dd5 Mon Sep 17 00:00:00 2001 From: cyprain-okeke <108260115+cyprain-okeke@users.noreply.github.com> Date: Fri, 2 May 2025 16:50:50 +0100 Subject: [PATCH 052/116] [PM-21073] No error when submitting empty form (#14597) * Resolve the space in note and colon issue * Resolve the max length for Note * Revert the changes --- .../billing/members/add-sponsorship-dialog.component.html | 5 ++--- .../app/billing/members/add-sponsorship-dialog.component.ts | 6 +++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/apps/web/src/app/billing/members/add-sponsorship-dialog.component.html b/apps/web/src/app/billing/members/add-sponsorship-dialog.component.html index 405211d6ecb..5bcfe9a15e1 100644 --- a/apps/web/src/app/billing/members/add-sponsorship-dialog.component.html +++ b/apps/web/src/app/billing/members/add-sponsorship-dialog.component.html @@ -7,7 +7,7 @@
      - {{ "email" | i18n }}: + {{ "email" | i18n }}
      - {{ "notes" | i18n }}: + {{ "notes" | i18n }}
      diff --git a/apps/web/src/app/billing/members/add-sponsorship-dialog.component.ts b/apps/web/src/app/billing/members/add-sponsorship-dialog.component.ts index 1b044e66257..7e6c0d464c3 100644 --- a/apps/web/src/app/billing/members/add-sponsorship-dialog.component.ts +++ b/apps/web/src/app/billing/members/add-sponsorship-dialog.component.ts @@ -74,7 +74,9 @@ export class AddSponsorshipDialogComponent { asyncValidators: [this.isOrganizationMember.bind(this)], updateOn: "change", }), - sponsorshipNote: new FormControl("", {}), + sponsorshipNote: new FormControl("", { + validators: [Validators.maxLength(1000)], + }), }); } @@ -86,6 +88,8 @@ export class AddSponsorshipDialogComponent { } protected async save() { + this.sponsorshipEmailControl.markAllAsTouched(); + if (this.sponsorshipForm.invalid) { return; } From 93d4fe6d471dd2b4df607440d2a403647245c9fb Mon Sep 17 00:00:00 2001 From: Alex Morask <144709477+amorask-bitwarden@users.noreply.github.com> Date: Fri, 2 May 2025 12:18:19 -0400 Subject: [PATCH 053/116] Pass correct parameter to endpoint (#14607) --- .../app/billing/members/free-bitwarden-families.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/src/app/billing/members/free-bitwarden-families.component.ts b/apps/web/src/app/billing/members/free-bitwarden-families.component.ts index c141eaebd78..3fde4ded405 100644 --- a/apps/web/src/app/billing/members/free-bitwarden-families.component.ts +++ b/apps/web/src/app/billing/members/free-bitwarden-families.component.ts @@ -176,7 +176,7 @@ export class FreeBitwardenFamiliesComponent implements OnInit { return; } - await this.apiService.deleteRevokeSponsorship(sponsorship.sponsoringOrganizationUserId); + await this.apiService.deleteRevokeSponsorship(this.organizationId); this.toastService.showToast({ variant: "success", From fdfb0196d09989ee86067fe7c27c8595d90dd3bc Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Fri, 2 May 2025 18:51:01 +0200 Subject: [PATCH 054/116] [PM-20108] Update account recovery trust prompt, delete organization trust prompt (#14218) * Update trust prompt and move to shared km module * Delete organization trust component * Update org trust warning message --- apps/browser/src/_locales/en/messages.json | 24 +++++++ apps/desktop/src/locales/en/messages.json | 24 +++++++ .../manage/organization-trust.component.html | 23 ------- .../manage/organization-trust.component.ts | 69 ------------------- .../enroll-master-password-reset.component.ts | 4 +- .../src/app/shared/loose-components.module.ts | 3 - apps/web/src/locales/en/messages.json | 6 ++ .../account-recovery-trust.component.html | 4 +- 8 files changed, 59 insertions(+), 98 deletions(-) delete mode 100644 apps/web/src/app/admin-console/organizations/manage/organization-trust.component.html delete mode 100644 apps/web/src/app/admin-console/organizations/manage/organization-trust.component.ts diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 6e1e2ef57ac..2b6adfdd7e8 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -3564,6 +3564,30 @@ "deviceTrusted": { "message": "Device trusted" }, + "trustOrganization": { + "message": "Trust organization" + }, + "trust": { + "message": "Trust" + }, + "doNotTrust": { + "message": "Do not trust" + }, + "organizationNotTrusted": { + "message": "Organization is not trusted" + }, + "emergencyAccessTrustWarning": { + "message": "For the security of your account, only confirm if you have granted emergency access to this user and their fingerprint matches what is displayed in their account" + }, + "orgTrustWarning": { + "message": "For the security of your account, only proceed if you are a member of this organization, have account recovery enabled, and the fingerprint displayed below matches the organization's fingerprint." + }, + "orgTrustWarning1": { + "message": "This organization has an Enterprise policy that will enroll you in account recovery. Enrollment will allow organization administrators to change your password. Only proceed if you recognize this organization and the fingerprint phrase displayed below matches the organization's fingerprint." + }, + "trustUser":{ + "message": "Trust user" + }, "sendsNoItemsTitle": { "message": "No active Sends", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json index 2350e0df4c7..7dee2c111d9 100644 --- a/apps/desktop/src/locales/en/messages.json +++ b/apps/desktop/src/locales/en/messages.json @@ -3152,6 +3152,30 @@ "deviceTrusted": { "message": "Device trusted" }, + "trustOrganization": { + "message": "Trust organization" + }, + "trust": { + "message": "Trust" + }, + "doNotTrust": { + "message": "Do not trust" + }, + "organizationNotTrusted": { + "message": "Organization is not trusted" + }, + "emergencyAccessTrustWarning": { + "message": "For the security of your account, only confirm if you have granted emergency access to this user and their fingerprint matches what is displayed in their account" + }, + "orgTrustWarning": { + "message": "For the security of your account, only proceed if you are a member of this organization, have account recovery enabled, and the fingerprint displayed below matches the organization's fingerprint." + }, + "orgTrustWarning1": { + "message": "This organization has an Enterprise policy that will enroll you in account recovery. Enrollment will allow organization administrators to change your password. Only proceed if you recognize this organization and the fingerprint phrase displayed below matches the organization's fingerprint." + }, + "trustUser":{ + "message": "Trust user" + }, "inputRequired": { "message": "Input is required." }, diff --git a/apps/web/src/app/admin-console/organizations/manage/organization-trust.component.html b/apps/web/src/app/admin-console/organizations/manage/organization-trust.component.html deleted file mode 100644 index 9a0310fde2a..00000000000 --- a/apps/web/src/app/admin-console/organizations/manage/organization-trust.component.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - {{ "orgTrustWarning" | i18n }} -

      - {{ "fingerprintPhrase" | i18n }} {{ fingerprint }} -

      -
      - - - - -
      - diff --git a/apps/web/src/app/admin-console/organizations/manage/organization-trust.component.ts b/apps/web/src/app/admin-console/organizations/manage/organization-trust.component.ts deleted file mode 100644 index 3f013c9fc74..00000000000 --- a/apps/web/src/app/admin-console/organizations/manage/organization-trust.component.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { DialogRef, DIALOG_DATA } from "@angular/cdk/dialog"; -import { Component, OnInit, Inject } from "@angular/core"; -import { FormBuilder } from "@angular/forms"; - -import { OrganizationManagementPreferencesService } from "@bitwarden/common/admin-console/abstractions/organization-management-preferences/organization-management-preferences.service"; -import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { DialogService } from "@bitwarden/components"; -import { KeyService } from "@bitwarden/key-management"; - -type OrganizationTrustDialogData = { - /** display name of the organization */ - name: string; - /** identifies the organization */ - orgId: string; - /** org public key */ - publicKey: Uint8Array; -}; -@Component({ - selector: "organization-trust", - templateUrl: "organization-trust.component.html", -}) -export class OrganizationTrustComponent implements OnInit { - loading = true; - fingerprint: string = ""; - confirmForm = this.formBuilder.group({}); - - constructor( - @Inject(DIALOG_DATA) protected params: OrganizationTrustDialogData, - private formBuilder: FormBuilder, - private keyService: KeyService, - protected organizationManagementPreferencesService: OrganizationManagementPreferencesService, - private logService: LogService, - private dialogRef: DialogRef, - ) {} - - async ngOnInit() { - try { - const fingerprint = await this.keyService.getFingerprint( - this.params.orgId, - this.params.publicKey, - ); - if (fingerprint != null) { - this.fingerprint = fingerprint.join("-"); - } - } catch (e) { - this.logService.error(e); - } - this.loading = false; - } - - submit = async () => { - if (this.loading) { - return; - } - - this.dialogRef.close(true); - }; - - /** - * Strongly typed helper to open a OrganizationTrustComponent - * @param dialogService Instance of the dialog service that will be used to open the dialog - * @param data The data to pass to the dialog - */ - static open(dialogService: DialogService, data: OrganizationTrustDialogData) { - return dialogService.open(OrganizationTrustComponent, { - data, - }); - } -} diff --git a/apps/web/src/app/admin-console/organizations/users/enroll-master-password-reset.component.ts b/apps/web/src/app/admin-console/organizations/users/enroll-master-password-reset.component.ts index 15e7af1cd2d..b63171ae6e6 100644 --- a/apps/web/src/app/admin-console/organizations/users/enroll-master-password-reset.component.ts +++ b/apps/web/src/app/admin-console/organizations/users/enroll-master-password-reset.component.ts @@ -18,8 +18,8 @@ import { Utils } from "@bitwarden/common/platform/misc/utils"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; import { DialogService, ToastService } from "@bitwarden/components"; import { KeyService } from "@bitwarden/key-management"; +import { AccountRecoveryTrustComponent } from "@bitwarden/key-management-ui"; -import { OrganizationTrustComponent } from "../manage/organization-trust.component"; import { OrganizationUserResetPasswordService } from "../members/services/organization-user-reset-password/organization-user-reset-password.service"; interface EnrollMasterPasswordResetData { @@ -62,7 +62,7 @@ export class EnrollMasterPasswordReset { await userVerificationService.buildRequest( secret, ); - const dialogRef = OrganizationTrustComponent.open(dialogService, { + const dialogRef = AccountRecoveryTrustComponent.open(dialogService, { name: data.organization.name, orgId: data.organization.id, publicKey, diff --git a/apps/web/src/app/shared/loose-components.module.ts b/apps/web/src/app/shared/loose-components.module.ts index 90e4c6ba9c3..e43c73cc3fb 100644 --- a/apps/web/src/app/shared/loose-components.module.ts +++ b/apps/web/src/app/shared/loose-components.module.ts @@ -9,7 +9,6 @@ import { LayoutComponent, NavigationModule } from "@bitwarden/components"; import { OrganizationLayoutComponent } from "../admin-console/organizations/layouts/organization-layout.component"; import { EventsComponent as OrgEventsComponent } from "../admin-console/organizations/manage/events.component"; -import { OrganizationTrustComponent } from "../admin-console/organizations/manage/organization-trust.component"; import { UserConfirmComponent as OrgUserConfirmComponent } from "../admin-console/organizations/manage/user-confirm.component"; import { VerifyRecoverDeleteOrgComponent } from "../admin-console/organizations/manage/verify-recover-delete-org.component"; import { AcceptFamilySponsorshipComponent } from "../admin-console/organizations/sponsorships/accept-family-sponsorship.component"; @@ -111,7 +110,6 @@ import { SharedModule } from "./shared.module"; OrgReusedPasswordsReportComponent, OrgUnsecuredWebsitesReportComponent, OrgUserConfirmComponent, - OrganizationTrustComponent, OrgWeakPasswordsReportComponent, PreferencesComponent, PremiumBadgeComponent, @@ -157,7 +155,6 @@ import { SharedModule } from "./shared.module"; OrgReusedPasswordsReportComponent, OrgUnsecuredWebsitesReportComponent, OrgUserConfirmComponent, - OrganizationTrustComponent, OrgWeakPasswordsReportComponent, PreferencesComponent, PremiumBadgeComponent, diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 8a37db4afda..a1d8d794f03 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -10397,12 +10397,18 @@ "doNotTrust": { "message": "Do not trust" }, + "organizationNotTrusted": { + "message": "Organization is not trusted" + }, "emergencyAccessTrustWarning": { "message": "For the security of your account, only confirm if you have granted emergency access to this user and their fingerprint matches what is displayed in their account" }, "orgTrustWarning": { "message": "For the security of your account, only proceed if you are a member of this organization, have account recovery enabled, and the fingerprint displayed below matches the organization's fingerprint." }, + "orgTrustWarning1": { + "message": "This organization has an Enterprise policy that will enroll you in account recovery. Enrollment will allow organization administrators to change your password. Only proceed if you recognize this organization and the fingerprint phrase displayed below matches the organization's fingerprint." + }, "trustUser":{ "message": "Trust user" }, diff --git a/libs/key-management-ui/src/trust/account-recovery-trust.component.html b/libs/key-management-ui/src/trust/account-recovery-trust.component.html index 5829558dd69..6d4c5b34dc2 100644 --- a/libs/key-management-ui/src/trust/account-recovery-trust.component.html +++ b/libs/key-management-ui/src/trust/account-recovery-trust.component.html @@ -5,8 +5,10 @@ [subtitle]="params.name" > - {{ "orgTrustWarning" | i18n }}

      + {{ "orgTrustWarning1" | i18n }} +
      +
      {{ "fingerprintPhrase" | i18n }} {{ fingerprint }}

      From 237002b6332165f40664e767c6886b993fd2a2eb Mon Sep 17 00:00:00 2001 From: Jordan Aasen <166539328+jaasen-livefront@users.noreply.github.com> Date: Fri, 2 May 2025 14:21:08 -0700 Subject: [PATCH 055/116] [PM-20988][PM-20986][PM-20983][PM-20971][PM-21019] - Multiple defect fixes for desktop cipher form update (#14559) * multiple bug fixes * favor getters to local state * fix tests --- apps/desktop/src/locales/en/messages.json | 15 ++++++++++++++ .../app/vault/item-footer.component.html | 2 +- .../src/vault/app/vault/vault-v2.component.ts | 10 +++++++--- .../view-identity-sections.component.html | 6 +++--- .../view-identity-sections.component.spec.ts | 7 ------- .../view-identity-sections.component.ts | 20 +++++-------------- 6 files changed, 31 insertions(+), 29 deletions(-) diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json index 7dee2c111d9..e685c053529 100644 --- a/apps/desktop/src/locales/en/messages.json +++ b/apps/desktop/src/locales/en/messages.json @@ -2007,6 +2007,21 @@ "message": "Premium", "description": "Premium membership" }, + "freeOrgsCannotUseAttachments": { + "message": "Free organizations cannot use attachments" + }, + "singleFieldNeedsAttention": { + "message": "1 field needs your attention." + }, + "multipleFieldsNeedAttention": { + "message": "$COUNT$ fields need your attention.", + "placeholders": { + "count": { + "content": "$1", + "example": "2" + } + } + }, "cardExpiredTitle": { "message": "Expired card" }, diff --git a/apps/desktop/src/vault/app/vault/item-footer.component.html b/apps/desktop/src/vault/app/vault/item-footer.component.html index 6915555c08b..824a2d481cf 100644 --- a/apps/desktop/src/vault/app/vault/item-footer.component.html +++ b/apps/desktop/src/vault/app/vault/item-footer.component.html @@ -44,7 +44,7 @@
      +
      + + + {{ + "sshAgentPromptBehaviorDesc" | i18n + }} +
      @@ -88,30 +73,15 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the inline menu with
      @@ -139,30 +109,15 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the inline menu with
      @@ -190,30 +145,15 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the inline menu with
    @@ -248,13 +188,19 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f aria-hidden="true" fill="none" height="24" - viewBox="0 0 23 24" - width="23" + viewBox="0 0 24 24" + width="24" xmlns="http://www.w3.org/2000/svg" > + @@ -283,29 +229,23 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f
@@ -323,30 +263,15 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f
@@ -404,29 +329,23 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f
@@ -473,29 +392,23 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f @@ -528,29 +441,23 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f @@ -575,15 +482,15 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f @@ -613,29 +520,23 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f @@ -683,29 +584,23 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f @@ -753,29 +648,23 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f @@ -876,29 +765,23 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f @@ -958,29 +841,23 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f @@ -1027,29 +904,23 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f @@ -1082,29 +953,23 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f @@ -1129,15 +994,15 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f @@ -1167,29 +1032,23 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f @@ -1237,29 +1096,23 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f @@ -1307,29 +1160,23 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f @@ -1389,29 +1236,23 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f @@ -1458,29 +1299,23 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f @@ -1513,29 +1348,23 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f @@ -1560,15 +1389,15 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f @@ -1598,29 +1427,23 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f @@ -1668,29 +1491,23 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f @@ -1738,29 +1555,23 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f @@ -1814,19 +1625,15 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f @@ -1842,29 +1649,23 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f @@ -1903,19 +1704,15 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f @@ -1931,29 +1728,23 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f @@ -1992,19 +1783,15 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f @@ -2026,29 +1813,23 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f @@ -2099,29 +1880,23 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f @@ -2169,29 +1944,23 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f @@ -2239,29 +2008,23 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f @@ -2287,10 +2050,10 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f > @@ -2407,29 +2170,23 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f @@ -2525,29 +2282,23 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f @@ -2578,29 +2329,21 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the locked inline men unlockAccount @@ -2623,17 +2366,22 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the password generato diff --git a/apps/browser/src/autofill/popup/fido2/fido2.component.html b/apps/browser/src/autofill/popup/fido2/fido2.component.html index 80ea6726cb9..8d8394641e9 100644 --- a/apps/browser/src/autofill/popup/fido2/fido2.component.html +++ b/apps/browser/src/autofill/popup/fido2/fido2.component.html @@ -14,7 +14,7 @@ (click)="addCipher()" slot="end" > - + {{ "new" | i18n }} diff --git a/apps/browser/src/autofill/utils/svg-icons.ts b/apps/browser/src/autofill/utils/svg-icons.ts index b04d18608ec..343acc00b06 100644 --- a/apps/browser/src/autofill/utils/svg-icons.ts +++ b/apps/browser/src/autofill/utils/svg-icons.ts @@ -5,34 +5,34 @@ export const logoLockedIcon = ''; export const globeIcon = - ''; + ''; export const creditCardIcon = - ''; + ''; export const idCardIcon = - ''; + ''; export const lockIcon = - ''; + ''; export const plusIcon = - ''; + ''; export const viewCipherIcon = - ''; + ''; export const passkeyIcon = - ''; + ''; export const circleCheckIcon = - ''; + ''; export const spinnerIcon = - ''; + ''; export const keyIcon = - ''; + ''; export const refreshIcon = - ''; + ''; diff --git a/apps/browser/src/platform/popup/layout/popup-header.component.html b/apps/browser/src/platform/popup/layout/popup-header.component.html index ac52a630900..c58bc258bf6 100644 --- a/apps/browser/src/platform/popup/layout/popup-header.component.html +++ b/apps/browser/src/platform/popup/layout/popup-header.component.html @@ -11,7 +11,7 @@
`, @@ -405,26 +406,26 @@ const navButtons = (showBerry = false) => [ { label: "vault", page: "/tabs/vault", - iconKey: "lock", - iconKeyActive: "lock-f", + icon: Icons.VaultInactive, + iconActive: Icons.VaultActive, }, { label: "generator", page: "/tabs/generator", - iconKey: "generate", - iconKeyActive: "generate-f", + icon: Icons.GeneratorInactive, + iconActive: Icons.GeneratorActive, }, { label: "send", page: "/tabs/send", - iconKey: "send", - iconKeyActive: "send-f", + icon: Icons.SendInactive, + iconActive: Icons.SendActive, }, { label: "settings", page: "/tabs/settings", - iconKey: "cog", - iconKeyActive: "cog-f", + icon: Icons.SettingsInactive, + iconActive: Icons.SettingsActive, showBerry: showBerry, }, ]; diff --git a/apps/browser/src/platform/popup/layout/popup-tab-navigation.component.html b/apps/browser/src/platform/popup/layout/popup-tab-navigation.component.html index 27b546738c3..1170725a4b7 100644 --- a/apps/browser/src/platform/popup/layout/popup-tab-navigation.component.html +++ b/apps/browser/src/platform/popup/layout/popup-tab-navigation.component.html @@ -7,7 +7,7 @@
  • diff --git a/apps/browser/src/platform/popup/layout/popup-tab-navigation.component.ts b/apps/browser/src/platform/popup/layout/popup-tab-navigation.component.ts index f4b82dc56fc..4984d3749a1 100644 --- a/apps/browser/src/platform/popup/layout/popup-tab-navigation.component.ts +++ b/apps/browser/src/platform/popup/layout/popup-tab-navigation.component.ts @@ -4,13 +4,13 @@ import { RouterModule } from "@angular/router"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { LinkModule } from "@bitwarden/components"; +import { Icon, IconModule, LinkModule } from "@bitwarden/components"; export type NavButton = { label: string; page: string; - iconKey: string; - iconKeyActive: string; + icon: Icon; + iconActive: Icon; showBerry?: boolean; }; @@ -18,7 +18,7 @@ export type NavButton = { selector: "popup-tab-navigation", templateUrl: "popup-tab-navigation.component.html", standalone: true, - imports: [CommonModule, LinkModule, RouterModule, JslibModule], + imports: [CommonModule, LinkModule, RouterModule, JslibModule, IconModule], host: { class: "tw-block tw-h-full tw-w-full tw-flex tw-flex-col", }, diff --git a/apps/browser/src/popup/tabs-v2.component.ts b/apps/browser/src/popup/tabs-v2.component.ts index f5b56cf829f..63b539fddce 100644 --- a/apps/browser/src/popup/tabs-v2.component.ts +++ b/apps/browser/src/popup/tabs-v2.component.ts @@ -1,70 +1,60 @@ -import { Component, OnInit } from "@angular/core"; -import { combineLatest, firstValueFrom, map, Observable } from "rxjs"; +import { Component } from "@angular/core"; +import { combineLatest, map, Observable, switchMap } from "rxjs"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; -import { UserId } from "@bitwarden/common/types/guid"; +import { Icons } from "@bitwarden/components"; import { VaultNudgesService } from "@bitwarden/vault"; +import { NavButton } from "../platform/popup/layout/popup-tab-navigation.component"; + @Component({ selector: "app-tabs-v2", templateUrl: "./tabs-v2.component.html", }) -export class TabsV2Component implements OnInit { - private activeUserId: UserId | null = null; - protected navButtons$: Observable< - { - label: string; - page: string; - iconKey: string; - iconKeyActive: string; - showBerry?: boolean; - }[] - > = new Observable(); +export class TabsV2Component { + private hasActiveBadges$ = this.accountService.activeAccount$ + .pipe(getUserId) + .pipe(switchMap((userId) => this.vaultNudgesService.hasActiveBadges$(userId))); + protected navButtons$: Observable = combineLatest([ + this.configService.getFeatureFlag$(FeatureFlag.PM8851_BrowserOnboardingNudge), + this.hasActiveBadges$, + ]).pipe( + map(([onboardingFeatureEnabled, hasBadges]) => { + return [ + { + label: "vault", + page: "/tabs/vault", + icon: Icons.VaultInactive, + iconActive: Icons.VaultActive, + }, + { + label: "generator", + page: "/tabs/generator", + icon: Icons.GeneratorInactive, + iconActive: Icons.GeneratorActive, + }, + { + label: "send", + page: "/tabs/send", + icon: Icons.SendInactive, + iconActive: Icons.SendActive, + }, + { + label: "settings", + page: "/tabs/settings", + icon: Icons.SettingsInactive, + iconActive: Icons.SettingsActive, + showBerry: onboardingFeatureEnabled && hasBadges, + }, + ]; + }), + ); constructor( private vaultNudgesService: VaultNudgesService, private accountService: AccountService, private readonly configService: ConfigService, ) {} - - async ngOnInit() { - this.activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); - - this.navButtons$ = combineLatest([ - this.configService.getFeatureFlag$(FeatureFlag.PM8851_BrowserOnboardingNudge), - this.vaultNudgesService.hasActiveBadges$(this.activeUserId), - ]).pipe( - map(([onboardingFeatureEnabled, hasBadges]) => { - return [ - { - label: "vault", - page: "/tabs/vault", - iconKey: "lock", - iconKeyActive: "lock-f", - }, - { - label: "generator", - page: "/tabs/generator", - iconKey: "generate", - iconKeyActive: "generate-f", - }, - { - label: "send", - page: "/tabs/send", - iconKey: "send", - iconKeyActive: "send-f", - }, - { - label: "settings", - page: "/tabs/settings", - iconKey: "cog", - iconKeyActive: "cog-f", - showBerry: onboardingFeatureEnabled && hasBadges, - }, - ]; - }), - ); - } } diff --git a/apps/browser/src/tools/popup/settings/settings-v2.component.html b/apps/browser/src/tools/popup/settings/settings-v2.component.html index 6189221942c..ab61e5b10f8 100644 --- a/apps/browser/src/tools/popup/settings/settings-v2.component.html +++ b/apps/browser/src/tools/popup/settings/settings-v2.component.html @@ -54,7 +54,7 @@ - + {{ "appearance" | i18n }} diff --git a/apps/browser/src/vault/popup/components/vault-v2/new-item-dropdown/new-item-dropdown-v2.component.html b/apps/browser/src/vault/popup/components/vault-v2/new-item-dropdown/new-item-dropdown-v2.component.html index af627e22ef2..6b6e8728f19 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/new-item-dropdown/new-item-dropdown-v2.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/new-item-dropdown/new-item-dropdown-v2.component.html @@ -1,5 +1,5 @@ diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-list-filters/vault-list-filters.component.html b/apps/browser/src/vault/popup/components/vault-v2/vault-list-filters/vault-list-filters.component.html index c61562f9f90..ba4cbf71251 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-list-filters/vault-list-filters.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-list-filters/vault-list-filters.component.html @@ -20,7 +20,7 @@ *ngIf="collections.length" fullWidth formControlName="collection" - placeholderIcon="bwi-collection" + placeholderIcon="bwi-collection-shared" [placeholderText]="'collection' | i18n" [options]="collections" > diff --git a/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.spec.ts b/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.spec.ts index f9785bccd00..9498d953808 100644 --- a/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.spec.ts +++ b/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.spec.ts @@ -362,7 +362,7 @@ describe("VaultPopupListFiltersService", () => { it("sets collection icon", (done) => { service.collections$.subscribe((collections) => { - expect(collections.every(({ icon }) => icon === "bwi-collection")).toBeTruthy(); + expect(collections.every(({ icon }) => icon === "bwi-collection-shared")).toBeTruthy(); done(); }); }); diff --git a/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.ts b/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.ts index f11fa0f63f0..c726678c973 100644 --- a/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.ts +++ b/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.ts @@ -461,7 +461,7 @@ export class VaultPopupListFiltersService { }); }), map((collections) => - collections.nestedList.map((c) => this.convertToChipSelectOption(c, "bwi-collection")), + collections.nestedList.map((c) => this.convertToChipSelectOption(c, "bwi-collection-shared")), ), shareReplay({ refCount: true, bufferSize: 1 }), ); diff --git a/apps/browser/src/vault/popup/settings/folders-v2.component.html b/apps/browser/src/vault/popup/settings/folders-v2.component.html index 552547c0230..8cea05f9c17 100644 --- a/apps/browser/src/vault/popup/settings/folders-v2.component.html +++ b/apps/browser/src/vault/popup/settings/folders-v2.component.html @@ -8,7 +8,7 @@ type="button" (click)="openAddEditFolderDialog()" > - + {{ "new" | i18n }} @@ -47,7 +47,7 @@ (click)="openAddEditFolderDialog()" data-testid="empty-new-folder-button" > - + {{ "newFolder" | i18n }} diff --git a/apps/desktop/src/app/layout/nav.component.ts b/apps/desktop/src/app/layout/nav.component.ts index dff35ec12c3..dbc399c051d 100644 --- a/apps/desktop/src/app/layout/nav.component.ts +++ b/apps/desktop/src/app/layout/nav.component.ts @@ -14,12 +14,12 @@ export class NavComponent { items: any[] = [ { link: "/vault", - icon: "bwi-lock-f", + icon: "bwi-vault", label: this.i18nService.translate("myVault"), }, { link: "/send", - icon: "bwi-send-f", + icon: "bwi-send", label: "Send", }, ]; diff --git a/apps/desktop/src/vault/app/vault/add-edit-custom-fields.component.html b/apps/desktop/src/vault/app/vault/add-edit-custom-fields.component.html index c7efaad70ba..bc0c3876b71 100644 --- a/apps/desktop/src/vault/app/vault/add-edit-custom-fields.component.html +++ b/apps/desktop/src/vault/app/vault/add-edit-custom-fields.component.html @@ -110,7 +110,7 @@ *ngIf="!(!cipher.edit && editMode)" cdkDragHandle > - +
diff --git a/apps/desktop/src/vault/app/vault/add-edit.component.html b/apps/desktop/src/vault/app/vault/add-edit.component.html index 7904d7b7f34..8457e72bdc1 100644 --- a/apps/desktop/src/vault/app/vault/add-edit.component.html +++ b/apps/desktop/src/vault/app/vault/add-edit.component.html @@ -519,7 +519,7 @@ appA11yTitle="{{ 'importSshKeyFromClipboard' | i18n }}" (click)="importSshKeyFromClipboard()" > - + diff --git a/apps/desktop/src/vault/app/vault/vault-filter/filters/collection-filter.component.html b/apps/desktop/src/vault/app/vault/vault-filter/filters/collection-filter.component.html index 667cdf6798c..e123f4400c7 100644 --- a/apps/desktop/src/vault/app/vault/vault-filter/filters/collection-filter.component.html +++ b/apps/desktop/src/vault/app/vault/vault-filter/filters/collection-filter.component.html @@ -55,7 +55,7 @@ >  {{ c.node.name }} diff --git a/apps/desktop/src/vault/app/vault/vault-items.component.html b/apps/desktop/src/vault/app/vault/vault-items.component.html index b64bdaa1c80..8a869cd2a32 100644 --- a/apps/desktop/src/vault/app/vault/vault-items.component.html +++ b/apps/desktop/src/vault/app/vault/vault-items.component.html @@ -28,7 +28,7 @@ {{ c.name }} diff --git a/apps/web/src/app/admin-console/organizations/collections/vault-filter/vault-filter.component.ts b/apps/web/src/app/admin-console/organizations/collections/vault-filter/vault-filter.component.ts index 384390d738e..13e90d5275c 100644 --- a/apps/web/src/app/admin-console/organizations/collections/vault-filter/vault-filter.component.ts +++ b/apps/web/src/app/admin-console/organizations/collections/vault-filter/vault-filter.component.ts @@ -105,14 +105,14 @@ export class VaultFilterComponent id: "AllCollections", name: "collections", type: "all", - icon: "bwi-collection", + icon: "bwi-collection-shared", }, [ { id: "AllCollections", name: "Collections", type: "all", - icon: "bwi-collection", + icon: "bwi-collection-shared", }, ], ), diff --git a/apps/web/src/app/admin-console/organizations/collections/vault-header/vault-header.component.html b/apps/web/src/app/admin-console/organizations/collections/vault-header/vault-header.component.html index c2e80b7524f..3ef48ff9015 100644 --- a/apps/web/src/app/admin-console/organizations/collections/vault-header/vault-header.component.html +++ b/apps/web/src/app/admin-console/organizations/collections/vault-header/vault-header.component.html @@ -14,7 +14,7 @@ - + {{ "new" | i18n }} @@ -140,7 +140,7 @@ diff --git a/apps/web/src/app/admin-console/organizations/collections/vault-header/vault-header.component.ts b/apps/web/src/app/admin-console/organizations/collections/vault-header/vault-header.component.ts index 7efb79ebdb6..4c129e325c5 100644 --- a/apps/web/src/app/admin-console/organizations/collections/vault-header/vault-header.component.ts +++ b/apps/web/src/app/admin-console/organizations/collections/vault-header/vault-header.component.ts @@ -117,7 +117,7 @@ export class VaultHeaderComponent { } get icon() { - return this.filter.collectionId !== undefined ? "bwi-collection" : ""; + return this.filter.collectionId !== undefined ? "bwi-collection-shared" : ""; } protected get showBreadcrumbs() { diff --git a/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.html b/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.html index ad3879b3659..4e7c2403893 100644 --- a/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.html +++ b/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.html @@ -13,7 +13,7 @@ >