From b856b89c7b0042d7c9613046a5054a9602a5e14a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2024 14:51:08 -0500 Subject: [PATCH 01/75] [deps] Autofill: Update tldts to v6.1.69 (#12424) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- apps/cli/package.json | 2 +- package-lock.json | 18 +++++++++--------- package.json | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/cli/package.json b/apps/cli/package.json index a0ab4c3f74d..b7d54e78e1d 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -80,7 +80,7 @@ "papaparse": "5.4.1", "proper-lockfile": "4.1.2", "rxjs": "7.8.1", - "tldts": "6.1.66", + "tldts": "6.1.69", "zxcvbn": "4.4.2" } } diff --git a/package-lock.json b/package-lock.json index 0d743316bd4..a8ae6daf236 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.66", + "tldts": "6.1.69", "utf-8-validate": "6.0.5", "zone.js": "0.14.10", "zxcvbn": "4.4.2" @@ -221,7 +221,7 @@ "papaparse": "5.4.1", "proper-lockfile": "4.1.2", "rxjs": "7.8.1", - "tldts": "6.1.66", + "tldts": "6.1.69", "zxcvbn": "4.4.2" }, "bin": { @@ -31135,21 +31135,21 @@ } }, "node_modules/tldts": { - "version": "6.1.66", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.66.tgz", - "integrity": "sha512-l3ciXsYFel/jSRfESbyKYud1nOw7WfhrBEF9I3UiarYk/qEaOOwu3qXNECHw4fHGHGTEOuhf/VdKgoDX5M/dhQ==", + "version": "6.1.69", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.69.tgz", + "integrity": "sha512-Oh/CqRQ1NXNY7cy9NkTPUauOWiTro0jEYZTioGbOmcQh6EC45oribyIMJp0OJO3677r13tO6SKdWoGZUx2BDFw==", "license": "MIT", "dependencies": { - "tldts-core": "^6.1.66" + "tldts-core": "^6.1.69" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "6.1.66", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.66.tgz", - "integrity": "sha512-s07jJruSwndD2X8bVjwioPfqpIc1pDTzszPe9pL1Skbh4bjytL85KNQ3tolqLbCvpQHawIsGfFi9dgerWjqW4g==", + "version": "6.1.69", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.69.tgz", + "integrity": "sha512-nygxy9n2PBUFQUtAXAc122gGo+04/j5qr5TGQFZTHafTKYvmARVXt2cA5rgero2/dnXUfkdPtiJoKmrd3T+wdA==", "license": "MIT" }, "node_modules/tmp": { diff --git a/package.json b/package.json index 10799b9e451..03991f9d70b 100644 --- a/package.json +++ b/package.json @@ -198,7 +198,7 @@ "qrious": "4.0.2", "rxjs": "7.8.1", "tabbable": "6.2.0", - "tldts": "6.1.66", + "tldts": "6.1.69", "utf-8-validate": "6.0.5", "zone.js": "0.14.10", "zxcvbn": "4.4.2" From 997d40f65a6fb7eaa9712f1de1504a6f23218466 Mon Sep 17 00:00:00 2001 From: Colton Hurst Date: Thu, 19 Dec 2024 14:54:59 -0500 Subject: [PATCH 02/75] PM-16234: Move DesktopFido2UserInterfaceService and DesktopFido2UserInterfaceSession to autofill ownership (#12482) --- apps/desktop/src/app/services/services.module.ts | 2 +- .../services/desktop-fido2-user-interface.service.ts | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) rename apps/desktop/src/{platform => autofill}/services/desktop-fido2-user-interface.service.ts (98%) diff --git a/apps/desktop/src/app/services/services.module.ts b/apps/desktop/src/app/services/services.module.ts index 8fa33215eb5..0f541907995 100644 --- a/apps/desktop/src/app/services/services.module.ts +++ b/apps/desktop/src/app/services/services.module.ts @@ -101,9 +101,9 @@ import { DesktopLoginApprovalComponentService } from "../../auth/login/desktop-l import { DesktopLoginComponentService } from "../../auth/login/desktop-login-component.service"; import { DesktopAutofillSettingsService } from "../../autofill/services/desktop-autofill-settings.service"; import { DesktopAutofillService } from "../../autofill/services/desktop-autofill.service"; +import { DesktopFido2UserInterfaceService } from "../../autofill/services/desktop-fido2-user-interface.service"; import { ElectronBiometricsService } from "../../key-management/biometrics/electron-biometrics.service"; import { flagEnabled } from "../../platform/flags"; -import { DesktopFido2UserInterfaceService } from "../../platform/services/desktop-fido2-user-interface.service"; import { DesktopSettingsService } from "../../platform/services/desktop-settings.service"; import { ElectronKeyService } from "../../platform/services/electron-key.service"; import { ElectronLogRendererService } from "../../platform/services/electron-log.renderer.service"; diff --git a/apps/desktop/src/platform/services/desktop-fido2-user-interface.service.ts b/apps/desktop/src/autofill/services/desktop-fido2-user-interface.service.ts similarity index 98% rename from apps/desktop/src/platform/services/desktop-fido2-user-interface.service.ts rename to apps/desktop/src/autofill/services/desktop-fido2-user-interface.service.ts index 116e8989e02..684b19e7394 100644 --- a/apps/desktop/src/platform/services/desktop-fido2-user-interface.service.ts +++ b/apps/desktop/src/autofill/services/desktop-fido2-user-interface.service.ts @@ -19,8 +19,6 @@ import { LoginUriView } from "@bitwarden/common/vault/models/view/login-uri.view import { LoginView } from "@bitwarden/common/vault/models/view/login.view"; import { SecureNoteView } from "@bitwarden/common/vault/models/view/secure-note.view"; -// TODO: This should be moved to the directory of whatever team takes this on - export class DesktopFido2UserInterfaceService implements Fido2UserInterfaceServiceAbstraction { From 40943b5929c81b0ed3bfdb85811afea0aecdb7c3 Mon Sep 17 00:00:00 2001 From: Jonathan Prusik Date: Thu, 19 Dec 2024 16:47:55 -0500 Subject: [PATCH 03/75] [CL-510] Create slot for banners in popup-page component (#12279) * add full-width-notice slot to popup page component * update storybook popup layout docs with notice example * remove unneeded container/classes --- .../platform/popup/layout/popup-layout.mdx | 27 ++++++++++++++++ .../popup/layout/popup-layout.stories.ts | 31 +++++++++++++++++++ .../popup/layout/popup-page.component.html | 1 + 3 files changed, 59 insertions(+) diff --git a/apps/browser/src/platform/popup/layout/popup-layout.mdx b/apps/browser/src/platform/popup/layout/popup-layout.mdx index aa11b4099a9..5723bef44b1 100644 --- a/apps/browser/src/platform/popup/layout/popup-layout.mdx +++ b/apps/browser/src/platform/popup/layout/popup-layout.mdx @@ -44,6 +44,9 @@ page looks nice when the extension is popped out. - `above-scroll-area` - When the page content overflows, this content will be "stuck" to the top of the page upon scrolling. +- `full-width-notice` + - Similar to `above-scroll-area`, this content will display before `above-scroll-area` without + container margin or padding. - default - Whatever content you want in `main`. @@ -108,6 +111,30 @@ Common interactive elements to insert into the `end` slot are: - "Add" button: this can be accomplished with the Button component and any custom functionality for that particular page +### Notice + + + + + +Common interactive elements to insert into the `full-width-notice` slot are: + +- `bit-banner`: shows a full-width notice + +Usage example: + +```html + + + + This is an important note about these ciphers + + + + + +``` + ## Popup footer Popup footer should be used when the page displays action buttons. It functions similarly to the diff --git a/apps/browser/src/platform/popup/layout/popup-layout.stories.ts b/apps/browser/src/platform/popup/layout/popup-layout.stories.ts index 11f2d34df50..cd467cecbd2 100644 --- a/apps/browser/src/platform/popup/layout/popup-layout.stories.ts +++ b/apps/browser/src/platform/popup/layout/popup-layout.stories.ts @@ -11,6 +11,7 @@ import { SendService } from "@bitwarden/common/tools/send/services/send.service. import { AvatarModule, BadgeModule, + BannerModule, ButtonModule, I18nMockService, IconButtonModule, @@ -125,6 +126,18 @@ class MockCurrentAccountComponent {} }) class MockSearchComponent {} +@Component({ + selector: "mock-banner", + template: ` + + This is an important note about these ciphers + + `, + standalone: true, + imports: [BannerModule], +}) +class MockBannerComponent {} + @Component({ selector: "mock-vault-page", template: ` @@ -298,6 +311,8 @@ export default { CommonModule, RouterModule, ExtensionContainerComponent, + MockBannerComponent, + MockSearchComponent, MockVaultSubpageComponent, MockVaultPageComponent, MockSendPageComponent, @@ -517,6 +532,22 @@ export const TransparentHeader: Story = { }), }; +export const Notice: Story = { + render: (args) => ({ + props: args, + template: /* HTML */ ` + + + + + + + + + `, + }), +}; + export const WidthOptions: Story = { render: (args) => ({ props: args, diff --git a/apps/browser/src/platform/popup/layout/popup-page.component.html b/apps/browser/src/platform/popup/layout/popup-page.component.html index 8eefc14ab55..ba9a108a504 100644 --- a/apps/browser/src/platform/popup/layout/popup-page.component.html +++ b/apps/browser/src/platform/popup/layout/popup-page.component.html @@ -1,5 +1,6 @@
+
Date: Thu, 19 Dec 2024 22:49:45 +0100 Subject: [PATCH 04/75] Run clippy and rustfmt on CI (#12388) * Run clippy and rustfmt on CI * Error on warnings and fix a couple of missed lints * Move import inside function * Fix unix lints * Fix windows lints * Missed some async tests * Remove unneeded reference --- .github/workflows/lint.yml | 28 +++++ .../desktop_native/core/src/autofill/mod.rs | 1 + .../desktop_native/core/src/autofill/unix.rs | 2 +- .../core/src/autofill/windows.rs | 2 +- .../desktop_native/core/src/biometric/mod.rs | 5 +- .../desktop_native/core/src/biometric/unix.rs | 14 +-- .../core/src/biometric/windows.rs | 29 ++--- .../desktop_native/core/src/crypto/crypto.rs | 10 +- .../desktop_native/core/src/crypto/mod.rs | 1 + .../desktop_native/core/src/password/macos.rs | 10 +- .../desktop_native/core/src/password/mod.rs | 1 + .../desktop_native/core/src/password/unix.rs | 4 +- .../core/src/password/windows.rs | 2 +- .../core/src/powermonitor/mod.rs | 1 + .../core/src/powermonitor/unimplemented.rs | 2 +- .../core/src/process_isolation/mod.rs | 1 + .../core/src/ssh_agent/generator.rs | 8 +- .../core/src/ssh_agent/importer.rs | 108 +++++++----------- .../desktop_native/core/src/ssh_agent/mod.rs | 8 +- .../ssh_agent/named_pipe_listener_stream.rs | 2 +- apps/desktop/desktop_native/napi/src/lib.rs | 14 +-- apps/desktop/desktop_native/objc/build.rs | 3 +- apps/desktop/desktop_native/objc/src/lib.rs | 4 +- 23 files changed, 132 insertions(+), 128 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 9dc72c7fdda..a907618bd36 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -58,3 +58,31 @@ jobs: run: | npm ci npm run lint + + rust: + name: Run Rust lint on ${{ matrix.os }} + runs-on: ${{ matrix.os || 'ubuntu-latest' }} + + strategy: + matrix: + os: + - ubuntu-latest + - macos-latest + - windows-latest + + steps: + - name: Checkout repo + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Check Rust version + run: rustup --version + + - name: Run cargo fmt + working-directory: ./apps/desktop/desktop_native + run: cargo fmt --check + + - name: Run Clippy + working-directory: ./apps/desktop/desktop_native + run: cargo clippy --all-features --tests + env: + RUSTFLAGS: "-D warnings" diff --git a/apps/desktop/desktop_native/core/src/autofill/mod.rs b/apps/desktop/desktop_native/core/src/autofill/mod.rs index 5997add240f..aacec852e90 100644 --- a/apps/desktop/desktop_native/core/src/autofill/mod.rs +++ b/apps/desktop/desktop_native/core/src/autofill/mod.rs @@ -1,3 +1,4 @@ +#[allow(clippy::module_inception)] #[cfg_attr(target_os = "linux", path = "unix.rs")] #[cfg_attr(target_os = "windows", path = "windows.rs")] #[cfg_attr(target_os = "macos", path = "macos.rs")] diff --git a/apps/desktop/desktop_native/core/src/autofill/unix.rs b/apps/desktop/desktop_native/core/src/autofill/unix.rs index d77130176a4..a6a961f46b1 100644 --- a/apps/desktop/desktop_native/core/src/autofill/unix.rs +++ b/apps/desktop/desktop_native/core/src/autofill/unix.rs @@ -1,5 +1,5 @@ use anyhow::Result; -pub async fn run_command(value: String) -> Result { +pub async fn run_command(_value: String) -> Result { todo!("Unix does not support autofill"); } diff --git a/apps/desktop/desktop_native/core/src/autofill/windows.rs b/apps/desktop/desktop_native/core/src/autofill/windows.rs index 2e442263c1b..6e0543af36c 100644 --- a/apps/desktop/desktop_native/core/src/autofill/windows.rs +++ b/apps/desktop/desktop_native/core/src/autofill/windows.rs @@ -1,5 +1,5 @@ use anyhow::Result; -pub async fn run_command(value: String) -> Result { +pub async fn run_command(_value: String) -> Result { todo!("Windows does not support autofill"); } diff --git a/apps/desktop/desktop_native/core/src/biometric/mod.rs b/apps/desktop/desktop_native/core/src/biometric/mod.rs index dd480f817b6..7ad9bcb032e 100644 --- a/apps/desktop/desktop_native/core/src/biometric/mod.rs +++ b/apps/desktop/desktop_native/core/src/biometric/mod.rs @@ -1,6 +1,7 @@ use aes::cipher::generic_array::GenericArray; use anyhow::{anyhow, Result}; +#[allow(clippy::module_inception)] #[cfg_attr(target_os = "linux", path = "unix.rs")] #[cfg_attr(target_os = "windows", path = "windows.rs")] #[cfg_attr(target_os = "macos", path = "macos.rs")] @@ -41,6 +42,7 @@ pub trait BiometricTrait { ) -> Result; } +#[allow(unused)] fn encrypt(secret: &str, key_material: &KeyMaterial, iv_b64: &str) -> Result { let iv = base64_engine .decode(iv_b64)? @@ -52,9 +54,10 @@ fn encrypt(secret: &str, key_material: &KeyMaterial, iv_b64: &str) -> Result Result { if let CipherString::AesCbc256_B64 { iv, data } = secret { - let decrypted = crypto::decrypt_aes256(&iv, &data, key_material.derive_key()?)?; + let decrypted = crypto::decrypt_aes256(iv, data, key_material.derive_key()?)?; Ok(String::from_utf8(decrypted)?) } else { diff --git a/apps/desktop/desktop_native/core/src/biometric/unix.rs b/apps/desktop/desktop_native/core/src/biometric/unix.rs index 5153fc5ed87..e57b77515e3 100644 --- a/apps/desktop/desktop_native/core/src/biometric/unix.rs +++ b/apps/desktop/desktop_native/core/src/biometric/unix.rs @@ -33,12 +33,10 @@ impl super::BiometricTrait for Biometric { .await; match result { - Ok(result) => { - return Ok(result.is_authorized); - } + Ok(result) => Ok(result.is_authorized), Err(e) => { println!("polkit biometric error: {:?}", e); - return Ok(false); + Ok(false) } } } @@ -52,7 +50,7 @@ impl super::BiometricTrait for Biometric { return Ok(true); } } - return Ok(false); + Ok(false) } fn derive_key_material(challenge_str: Option<&str>) -> Result { @@ -68,8 +66,8 @@ impl super::BiometricTrait for Biometric { // so we use a a key derived from the iv. this key is not intended to add any security // but only a place-holder let key = Sha256::digest(challenge); - let key_b64 = base64_engine.encode(&key); - let iv_b64 = base64_engine.encode(&challenge); + let key_b64 = base64_engine.encode(key); + let iv_b64 = base64_engine.encode(challenge); Ok(OsDerivedKey { key_b64, iv_b64 }) } @@ -100,7 +98,7 @@ impl super::BiometricTrait for Biometric { let encrypted_secret = crate::password::get_password(service, account).await?; let secret = CipherString::from_str(&encrypted_secret)?; - return Ok(decrypt(&secret, &key_material)?); + decrypt(&secret, &key_material) } } diff --git a/apps/desktop/desktop_native/core/src/biometric/windows.rs b/apps/desktop/desktop_native/core/src/biometric/windows.rs index d17ea752878..c91b379c226 100644 --- a/apps/desktop/desktop_native/core/src/biometric/windows.rs +++ b/apps/desktop/desktop_native/core/src/biometric/windows.rs @@ -88,14 +88,14 @@ impl super::BiometricTrait for Biometric { let bitwarden = h!("Bitwarden"); let result = KeyCredentialManager::RequestCreateAsync( - &bitwarden, + bitwarden, KeyCredentialCreationOption::FailIfExists, )? .get()?; let result = match result.Status()? { KeyCredentialStatus::CredentialAlreadyExists => { - KeyCredentialManager::OpenAsync(&bitwarden)?.get()? + KeyCredentialManager::OpenAsync(bitwarden)?.get()? } KeyCredentialStatus::Success => result, _ => return Err(anyhow!("Failed to create key credential")), @@ -116,8 +116,8 @@ impl super::BiometricTrait for Biometric { CryptographicBuffer::CopyToByteArray(&signature_buffer, &mut signature_value)?; let key = Sha256::digest(&*signature_value); - let key_b64 = base64_engine.encode(&key); - let iv_b64 = base64_engine.encode(&challenge); + let key_b64 = base64_engine.encode(key); + let iv_b64 = base64_engine.encode(challenge); Ok(OsDerivedKey { key_b64, iv_b64 }) } @@ -151,12 +151,12 @@ impl super::BiometricTrait for Biometric { Ok(secret) => { // If the secret is a CipherString, it is encrypted and we need to decrypt it. let secret = decrypt(&secret, &key_material)?; - return Ok(secret); + Ok(secret) } Err(_) => { // If the secret is not a CipherString, it is not encrypted and we can return it // directly. - return Ok(encrypted_secret); + Ok(encrypted_secret) } } } @@ -206,8 +206,8 @@ fn set_focus(window: HWND) { pressed = true; keybd_event(VK_MENU.0 as u8, 0, KEYEVENTF_EXTENDEDKEY, 0); } - SetForegroundWindow(window); - SetFocus(window); + let _ = SetForegroundWindow(window); + let _ = SetFocus(window); if pressed { keybd_event( VK_MENU.0 as u8, @@ -245,20 +245,21 @@ mod tests { assert_eq!(iv.len(), 16); } - #[test] + #[tokio::test] #[cfg(feature = "manual_test")] - fn test_prompt() { + async fn test_prompt() { ::prompt( vec![0, 0, 0, 0, 0, 0, 0, 0], String::from("Hello from Rust"), ) + .await .unwrap(); } - #[test] + #[tokio::test] #[cfg(feature = "manual_test")] - fn test_available() { - assert!(::available().unwrap()) + async fn test_available() { + assert!(::available().await.unwrap()) } #[test] @@ -275,7 +276,7 @@ mod tests { match secret { CipherString::AesCbc256_B64 { iv, data: _ } => { - assert_eq!(iv_b64, base64_engine.encode(&iv)); + assert_eq!(iv_b64, base64_engine.encode(iv)); } _ => panic!("Invalid cipher string"), } diff --git a/apps/desktop/desktop_native/core/src/crypto/crypto.rs b/apps/desktop/desktop_native/core/src/crypto/crypto.rs index 4427138cb1d..a254d34c434 100644 --- a/apps/desktop/desktop_native/core/src/crypto/crypto.rs +++ b/apps/desktop/desktop_native/core/src/crypto/crypto.rs @@ -9,13 +9,9 @@ use crate::error::{CryptoError, KdfParamError, Result}; use super::CipherString; -pub fn decrypt_aes256( - iv: &[u8; 16], - data: &Vec, - key: GenericArray, -) -> Result> { +pub fn decrypt_aes256(iv: &[u8; 16], data: &[u8], key: GenericArray) -> Result> { let iv = GenericArray::from_slice(iv); - let mut data = data.clone(); + let mut data = data.to_vec(); let decrypted_key_slice = cbc::Decryptor::::new(&key, iv) .decrypt_padded_mut::(&mut data) .map_err(|_| CryptoError::KeyDecrypt)?; @@ -54,7 +50,7 @@ pub fn argon2( let mut hash = [0u8; 32]; argon - .hash_password_into(secret, &salt, &mut hash) + .hash_password_into(secret, salt, &mut hash) .map_err(|e| KdfParamError::InvalidParams(format!("Argon2 hashing failed: {e}",)))?; // Argon2 is using some stack memory that is not zeroed. Eventually some function will diff --git a/apps/desktop/desktop_native/core/src/crypto/mod.rs b/apps/desktop/desktop_native/core/src/crypto/mod.rs index 13ac4b947bd..72f189509f8 100644 --- a/apps/desktop/desktop_native/core/src/crypto/mod.rs +++ b/apps/desktop/desktop_native/core/src/crypto/mod.rs @@ -2,4 +2,5 @@ pub use cipher_string::*; pub use crypto::*; mod cipher_string; +#[allow(clippy::module_inception)] mod crypto; diff --git a/apps/desktop/desktop_native/core/src/password/macos.rs b/apps/desktop/desktop_native/core/src/password/macos.rs index b69854905d9..1af005acb4d 100644 --- a/apps/desktop/desktop_native/core/src/password/macos.rs +++ b/apps/desktop/desktop_native/core/src/password/macos.rs @@ -4,18 +4,18 @@ use security_framework::passwords::{ }; pub async fn get_password(service: &str, account: &str) -> Result { - let result = String::from_utf8(get_generic_password(&service, &account)?)?; + let result = String::from_utf8(get_generic_password(service, account)?)?; Ok(result) } pub async fn set_password(service: &str, account: &str, password: &str) -> Result<()> { - let result = set_generic_password(&service, &account, password.as_bytes())?; - Ok(result) + set_generic_password(service, account, password.as_bytes())?; + Ok(()) } pub async fn delete_password(service: &str, account: &str) -> Result<()> { - let result = delete_generic_password(&service, &account)?; - Ok(result) + delete_generic_password(service, account)?; + Ok(()) } pub async fn is_available() -> Result { diff --git a/apps/desktop/desktop_native/core/src/password/mod.rs b/apps/desktop/desktop_native/core/src/password/mod.rs index 3cc0c28e044..351e896c40e 100644 --- a/apps/desktop/desktop_native/core/src/password/mod.rs +++ b/apps/desktop/desktop_native/core/src/password/mod.rs @@ -1,3 +1,4 @@ +#[allow(clippy::module_inception)] #[cfg_attr(target_os = "linux", path = "unix.rs")] #[cfg_attr(target_os = "windows", path = "windows.rs")] #[cfg_attr(target_os = "macos", path = "macos.rs")] diff --git a/apps/desktop/desktop_native/core/src/password/unix.rs b/apps/desktop/desktop_native/core/src/password/unix.rs index f73b41de8c1..c02ef619624 100644 --- a/apps/desktop/desktop_native/core/src/password/unix.rs +++ b/apps/desktop/desktop_native/core/src/password/unix.rs @@ -13,7 +13,7 @@ async fn get_password_new(service: &str, account: &str) -> Result { let keyring = oo7::Keyring::new().await?; let attributes = HashMap::from([("service", service), ("account", account)]); let results = keyring.search_items(&attributes).await?; - let res = results.get(0); + let res = results.first(); match res { Some(res) => { let secret = res.secret().await?; @@ -31,7 +31,7 @@ async fn get_password_legacy(service: &str, account: &str) -> Result { let keyring = oo7::Keyring::DBus(collection); let attributes = HashMap::from([("service", service), ("account", account)]); let results = keyring.search_items(&attributes).await?; - let res = results.get(0); + let res = results.first(); match res { Some(res) => { let secret = res.secret().await?; diff --git a/apps/desktop/desktop_native/core/src/password/windows.rs b/apps/desktop/desktop_native/core/src/password/windows.rs index 2a66640286f..32300b9f81f 100644 --- a/apps/desktop/desktop_native/core/src/password/windows.rs +++ b/apps/desktop/desktop_native/core/src/password/windows.rs @@ -42,7 +42,7 @@ pub async fn get_password<'a>(service: &str, account: &str) -> Result { .to_string_lossy() }; - Ok(String::from(password)) + Ok(password) } pub async fn set_password(service: &str, account: &str, password: &str) -> Result<()> { diff --git a/apps/desktop/desktop_native/core/src/powermonitor/mod.rs b/apps/desktop/desktop_native/core/src/powermonitor/mod.rs index 2a996395508..8329b291177 100644 --- a/apps/desktop/desktop_native/core/src/powermonitor/mod.rs +++ b/apps/desktop/desktop_native/core/src/powermonitor/mod.rs @@ -1,3 +1,4 @@ +#[allow(clippy::module_inception)] #[cfg_attr(target_os = "linux", path = "linux.rs")] #[cfg_attr(target_os = "windows", path = "unimplemented.rs")] #[cfg_attr(target_os = "macos", path = "unimplemented.rs")] diff --git a/apps/desktop/desktop_native/core/src/powermonitor/unimplemented.rs b/apps/desktop/desktop_native/core/src/powermonitor/unimplemented.rs index 0078c0bf921..efe36bcb3c7 100644 --- a/apps/desktop/desktop_native/core/src/powermonitor/unimplemented.rs +++ b/apps/desktop/desktop_native/core/src/powermonitor/unimplemented.rs @@ -3,5 +3,5 @@ pub async fn on_lock(_: tokio::sync::mpsc::Sender<()>) -> Result<(), Box bool { - return false; + false } diff --git a/apps/desktop/desktop_native/core/src/process_isolation/mod.rs b/apps/desktop/desktop_native/core/src/process_isolation/mod.rs index 7c9aaf3bcf7..30f4dbf689a 100644 --- a/apps/desktop/desktop_native/core/src/process_isolation/mod.rs +++ b/apps/desktop/desktop_native/core/src/process_isolation/mod.rs @@ -1,3 +1,4 @@ +#[allow(clippy::module_inception)] #[cfg_attr(target_os = "linux", path = "linux.rs")] #[cfg_attr(target_os = "windows", path = "windows.rs")] #[cfg_attr(target_os = "macos", path = "macos.rs")] diff --git a/apps/desktop/desktop_native/core/src/ssh_agent/generator.rs b/apps/desktop/desktop_native/core/src/ssh_agent/generator.rs index fe639f20e7f..f532d0ab280 100644 --- a/apps/desktop/desktop_native/core/src/ssh_agent/generator.rs +++ b/apps/desktop/desktop_native/core/src/ssh_agent/generator.rs @@ -19,24 +19,24 @@ pub async fn generate_keypair(key_algorithm: String) -> Result return Err(anyhow::anyhow!("Unsupported RSA key size")), }; let rsa_keypair = ssh_key::private::RsaKeypair::random(&mut rng, bits) - .or_else(|e| Err(anyhow::anyhow!(e.to_string())))?; + .map_err(|e| anyhow::anyhow!(e.to_string()))?; let private_key = ssh_key::PrivateKey::new( ssh_key::private::KeypairData::from(rsa_keypair), "".to_string(), ) - .or_else(|e| Err(anyhow::anyhow!(e.to_string())))?; + .map_err(|e| anyhow::anyhow!(e.to_string()))?; Ok(private_key) } _ => { return Err(anyhow::anyhow!("Unsupported key algorithm")); } } - .or_else(|e| Err(anyhow::anyhow!(e.to_string())))?; + .map_err(|e| anyhow::anyhow!(e.to_string()))?; let private_key_openssh = key .to_openssh(LineEnding::LF) - .or_else(|e| Err(anyhow::anyhow!(e.to_string())))?; + .map_err(|e| anyhow::anyhow!(e.to_string()))?; Ok(SshKey { private_key: private_key_openssh.to_string(), public_key: key.public_key().to_string(), diff --git a/apps/desktop/desktop_native/core/src/ssh_agent/importer.rs b/apps/desktop/desktop_native/core/src/ssh_agent/importer.rs index c9f66107cc8..52464487ec5 100644 --- a/apps/desktop/desktop_native/core/src/ssh_agent/importer.rs +++ b/apps/desktop/desktop_native/core/src/ssh_agent/importer.rs @@ -27,61 +27,43 @@ pub fn import_key( password: String, ) -> Result { match encoded_key.lines().next() { - Some(PKCS1_HEADER) => { - return Ok(SshKeyImportResult { - status: SshKeyImportStatus::UnsupportedKeyType, + Some(PKCS1_HEADER) => Ok(SshKeyImportResult { + status: SshKeyImportStatus::UnsupportedKeyType, + ssh_key: None, + }), + Some(PKCS8_UNENCRYPTED_HEADER) => match import_pkcs8_key(encoded_key, None) { + Ok(result) => Ok(result), + Err(_) => Ok(SshKeyImportResult { + status: SshKeyImportStatus::ParsingError, ssh_key: None, - }); - } - Some(PKCS8_UNENCRYPTED_HEADER) => { - return match import_pkcs8_key(encoded_key, None) { - Ok(result) => Ok(result), - Err(_) => Ok(SshKeyImportResult { + }), + }, + Some(PKCS8_ENCRYPTED_HEADER) => match import_pkcs8_key(encoded_key, Some(password)) { + Ok(result) => Ok(result), + Err(err) => match err { + SshKeyImportError::PasswordRequired => Ok(SshKeyImportResult { + status: SshKeyImportStatus::PasswordRequired, + ssh_key: None, + }), + SshKeyImportError::WrongPassword => Ok(SshKeyImportResult { + status: SshKeyImportStatus::WrongPassword, + ssh_key: None, + }), + SshKeyImportError::ParsingError => Ok(SshKeyImportResult { status: SshKeyImportStatus::ParsingError, ssh_key: None, }), - }; - } - Some(PKCS8_ENCRYPTED_HEADER) => match import_pkcs8_key(encoded_key, Some(password)) { - Ok(result) => { - return Ok(result); - } - Err(err) => match err { - SshKeyImportError::PasswordRequired => { - return Ok(SshKeyImportResult { - status: SshKeyImportStatus::PasswordRequired, - ssh_key: None, - }); - } - SshKeyImportError::WrongPassword => { - return Ok(SshKeyImportResult { - status: SshKeyImportStatus::WrongPassword, - ssh_key: None, - }); - } - SshKeyImportError::ParsingError => { - return Ok(SshKeyImportResult { - status: SshKeyImportStatus::ParsingError, - ssh_key: None, - }); - } }, }, - Some(OPENSSH_HEADER) => { - return import_openssh_key(encoded_key, password); - } - Some(_) => { - return Ok(SshKeyImportResult { - status: SshKeyImportStatus::ParsingError, - ssh_key: None, - }); - } - None => { - return Ok(SshKeyImportResult { - status: SshKeyImportStatus::ParsingError, - ssh_key: None, - }); - } + Some(OPENSSH_HEADER) => import_openssh_key(encoded_key, password), + Some(_) => Ok(SshKeyImportResult { + status: SshKeyImportStatus::ParsingError, + ssh_key: None, + }), + None => Ok(SshKeyImportResult { + status: SshKeyImportStatus::ParsingError, + ssh_key: None, + }), } } @@ -152,14 +134,14 @@ fn import_pkcs8_key( let pk: Ed25519Keypair = Ed25519Keypair::from(Ed25519PrivateKey::from_bytes(&pk.secret_key)); let private_key = ssh_key::private::PrivateKey::from(pk); - return Ok(SshKeyImportResult { + Ok(SshKeyImportResult { status: SshKeyImportStatus::Success, ssh_key: Some(SshKey { private_key: private_key.to_openssh(LineEnding::LF).unwrap().to_string(), public_key: private_key.public_key().to_string(), key_fingerprint: private_key.fingerprint(HashAlg::Sha256).to_string(), }), - }); + }) } KeyType::Rsa => { let pk: rsa::RsaPrivateKey = match password { @@ -179,7 +161,7 @@ fn import_pkcs8_key( match rsa_keypair { Ok(rsa_keypair) => { let private_key = ssh_key::private::PrivateKey::from(rsa_keypair); - return Ok(SshKeyImportResult { + Ok(SshKeyImportResult { status: SshKeyImportStatus::Success, ssh_key: Some(SshKey { private_key: private_key @@ -189,22 +171,18 @@ fn import_pkcs8_key( public_key: private_key.public_key().to_string(), key_fingerprint: private_key.fingerprint(HashAlg::Sha256).to_string(), }), - }); - } - Err(_) => { - return Ok(SshKeyImportResult { - status: SshKeyImportStatus::ParsingError, - ssh_key: None, - }); + }) } + Err(_) => Ok(SshKeyImportResult { + status: SshKeyImportStatus::ParsingError, + ssh_key: None, + }), } } - _ => { - return Ok(SshKeyImportResult { - status: SshKeyImportStatus::UnsupportedKeyType, - ssh_key: None, - }); - } + _ => Ok(SshKeyImportResult { + status: SshKeyImportStatus::UnsupportedKeyType, + ssh_key: None, + }), } } diff --git a/apps/desktop/desktop_native/core/src/ssh_agent/mod.rs b/apps/desktop/desktop_native/core/src/ssh_agent/mod.rs index 82b90c7bff9..eced6e89545 100644 --- a/apps/desktop/desktop_native/core/src/ssh_agent/mod.rs +++ b/apps/desktop/desktop_native/core/src/ssh_agent/mod.rs @@ -129,7 +129,7 @@ impl BitwardenDesktopAgent { .store(true, std::sync::atomic::Ordering::Relaxed); for (key, name, cipher_id) in new_keys.iter() { - match parse_key_safe(&key) { + match parse_key_safe(key) { Ok(private_key) => { let public_key_bytes = private_key .public_key() @@ -187,10 +187,8 @@ impl BitwardenDesktopAgent { return 0; } - let request_id = self - .request_id - .fetch_add(1, std::sync::atomic::Ordering::Relaxed); - request_id + self.request_id + .fetch_add(1, std::sync::atomic::Ordering::Relaxed) } pub fn is_running(&self) -> bool { diff --git a/apps/desktop/desktop_native/core/src/ssh_agent/named_pipe_listener_stream.rs b/apps/desktop/desktop_native/core/src/ssh_agent/named_pipe_listener_stream.rs index 1358abe32e0..094144effe1 100644 --- a/apps/desktop/desktop_native/core/src/ssh_agent/named_pipe_listener_stream.rs +++ b/apps/desktop/desktop_native/core/src/ssh_agent/named_pipe_listener_stream.rs @@ -61,7 +61,7 @@ impl NamedPipeServerStream { } }; - let peer_info = peerinfo::gather::get_peer_info(pid as u32); + let peer_info = peerinfo::gather::get_peer_info(pid); let peer_info = match peer_info { Err(err) => { println!("Failed getting process info for pid {} {}", pid, err); diff --git a/apps/desktop/desktop_native/napi/src/lib.rs b/apps/desktop/desktop_native/napi/src/lib.rs index 170d7bca4f9..3ceef666d80 100644 --- a/apps/desktop/desktop_native/napi/src/lib.rs +++ b/apps/desktop/desktop_native/napi/src/lib.rs @@ -89,11 +89,9 @@ pub mod biometrics { account: String, key_material: Option, ) -> napi::Result { - let result = - Biometric::get_biometric_secret(&service, &account, key_material.map(|m| m.into())) - .await - .map_err(|e| napi::Error::from_reason(e.to_string())); - result + Biometric::get_biometric_secret(&service, &account, key_material.map(|m| m.into())) + .await + .map_err(|e| napi::Error::from_reason(e.to_string())) } /// Derives key material from biometric data. Returns a string encoded with a @@ -409,8 +407,8 @@ pub mod powermonitors { .await .map_err(|e| napi::Error::from_reason(e.to_string()))?; tokio::spawn(async move { - while let Some(message) = rx.recv().await { - callback.call(Ok(message.into()), ThreadsafeFunctionCallMode::NonBlocking); + while let Some(()) = rx.recv().await { + callback.call(Ok(()), ThreadsafeFunctionCallMode::NonBlocking); } }); Ok(()) @@ -812,6 +810,6 @@ pub mod crypto { desktop_core::crypto::argon2(&secret, &salt, iterations, memory, parallelism) .map_err(|e| napi::Error::from_reason(e.to_string())) .map(|v| v.to_vec()) - .map(|v| Buffer::from(v)) + .map(Buffer::from) } } diff --git a/apps/desktop/desktop_native/objc/build.rs b/apps/desktop/desktop_native/objc/build.rs index 57f3b626bf7..1f9be6bc02f 100644 --- a/apps/desktop/desktop_native/objc/build.rs +++ b/apps/desktop/desktop_native/objc/build.rs @@ -1,7 +1,6 @@ -use glob::glob; - #[cfg(target_os = "macos")] fn main() { + use glob::glob; let mut builder = cc::Build::new(); // Auto compile all .m files in the src/native directory diff --git a/apps/desktop/desktop_native/objc/src/lib.rs b/apps/desktop/desktop_native/objc/src/lib.rs index 64475b7956d..eb969cb5f56 100644 --- a/apps/desktop/desktop_native/objc/src/lib.rs +++ b/apps/desktop/desktop_native/objc/src/lib.rs @@ -100,7 +100,7 @@ mod objc { } }; - return true; + true } } @@ -115,7 +115,7 @@ pub async fn run_command(input: String) -> Result { unsafe { objc::runCommand(context.as_ptr(), c_input.as_ptr()) }; // Convert output from ObjC code to Rust string - let objc_output = rx.await?.try_into()?; + let objc_output = rx.await?; // Convert output from ObjC code to Rust string // let objc_output = output.try_into()?; From 69d42a73e38822d3bed53a08cdd09457b9b7a65d Mon Sep 17 00:00:00 2001 From: Shane Melton Date: Thu, 19 Dec 2024 14:47:58 -0800 Subject: [PATCH 05/75] [PM-16194] Do not sort Favorite ciphers by last used date (#12466) * [PM-16194] Do not sort Favorite ciphers by last used date * [PM-16194] Remove test --- .../vault/popup/services/vault-popup-items.service.spec.ts | 7 ------- .../src/vault/popup/services/vault-popup-items.service.ts | 5 +---- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/apps/browser/src/vault/popup/services/vault-popup-items.service.spec.ts b/apps/browser/src/vault/popup/services/vault-popup-items.service.spec.ts index 1900d35d9d9..5b0eb63998d 100644 --- a/apps/browser/src/vault/popup/services/vault-popup-items.service.spec.ts +++ b/apps/browser/src/vault/popup/services/vault-popup-items.service.spec.ts @@ -262,13 +262,6 @@ describe("VaultPopupItemsService", () => { }); }); - it("should sort by last used then by name", (done) => { - service.favoriteCiphers$.subscribe((ciphers) => { - expect(cipherServiceMock.sortCiphersByLastUsedThenName).toHaveBeenCalled(); - done(); - }); - }); - it("should filter favoriteCiphers$ down to search term", (done) => { const cipherList = Object.values(allCiphers); const searchText = "Card 2"; diff --git a/apps/browser/src/vault/popup/services/vault-popup-items.service.ts b/apps/browser/src/vault/popup/services/vault-popup-items.service.ts index b093fa7131c..93aa8cdaba9 100644 --- a/apps/browser/src/vault/popup/services/vault-popup-items.service.ts +++ b/apps/browser/src/vault/popup/services/vault-popup-items.service.ts @@ -164,16 +164,13 @@ export class VaultPopupItemsService { /** * List of favorite ciphers that are not currently suggested for autofill. - * Ciphers are sorted by last used date, then by name. + * Ciphers are sorted by name. */ favoriteCiphers$: Observable = this.autoFillCiphers$.pipe( withLatestFrom(this._filteredCipherList$), map(([autoFillCiphers, ciphers]) => ciphers.filter((cipher) => cipher.favorite && !autoFillCiphers.includes(cipher)), ), - map((ciphers) => - ciphers.sort((a, b) => this.cipherService.sortCiphersByLastUsedThenName(a, b)), - ), shareReplay({ refCount: false, bufferSize: 1 }), ); From 40ac5132ba93c4e34e9f804412e58a7b4c8bd23e Mon Sep 17 00:00:00 2001 From: "bw-ghapp[bot]" <178206702+bw-ghapp[bot]@users.noreply.github.com> Date: Fri, 20 Dec 2024 13:01:35 +0100 Subject: [PATCH 06/75] Autosync the updated translations (#12493) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/browser/src/_locales/ar/messages.json | 42 ++- apps/browser/src/_locales/az/messages.json | 50 +++- apps/browser/src/_locales/be/messages.json | 42 ++- apps/browser/src/_locales/bg/messages.json | 42 ++- apps/browser/src/_locales/bn/messages.json | 42 ++- apps/browser/src/_locales/bs/messages.json | 42 ++- apps/browser/src/_locales/ca/messages.json | 42 ++- apps/browser/src/_locales/cs/messages.json | 42 ++- apps/browser/src/_locales/cy/messages.json | 42 ++- apps/browser/src/_locales/da/messages.json | 42 ++- apps/browser/src/_locales/de/messages.json | 42 ++- apps/browser/src/_locales/el/messages.json | 42 ++- apps/browser/src/_locales/en_GB/messages.json | 42 ++- apps/browser/src/_locales/en_IN/messages.json | 42 ++- apps/browser/src/_locales/es/messages.json | 42 ++- apps/browser/src/_locales/et/messages.json | 42 ++- apps/browser/src/_locales/eu/messages.json | 42 ++- apps/browser/src/_locales/fa/messages.json | 42 ++- apps/browser/src/_locales/fi/messages.json | 42 ++- apps/browser/src/_locales/fil/messages.json | 42 ++- apps/browser/src/_locales/fr/messages.json | 42 ++- apps/browser/src/_locales/gl/messages.json | 42 ++- apps/browser/src/_locales/he/messages.json | 42 ++- apps/browser/src/_locales/hi/messages.json | 42 ++- apps/browser/src/_locales/hr/messages.json | 264 ++++++++++-------- apps/browser/src/_locales/hu/messages.json | 42 ++- apps/browser/src/_locales/id/messages.json | 220 +++++++++------ apps/browser/src/_locales/it/messages.json | 42 ++- apps/browser/src/_locales/ja/messages.json | 240 +++++++++------- apps/browser/src/_locales/ka/messages.json | 42 ++- apps/browser/src/_locales/km/messages.json | 42 ++- apps/browser/src/_locales/kn/messages.json | 42 ++- apps/browser/src/_locales/ko/messages.json | 58 +++- apps/browser/src/_locales/lt/messages.json | 42 ++- apps/browser/src/_locales/lv/messages.json | 42 ++- apps/browser/src/_locales/ml/messages.json | 42 ++- apps/browser/src/_locales/mr/messages.json | 42 ++- apps/browser/src/_locales/my/messages.json | 42 ++- apps/browser/src/_locales/nb/messages.json | 42 ++- apps/browser/src/_locales/ne/messages.json | 42 ++- apps/browser/src/_locales/nl/messages.json | 42 ++- apps/browser/src/_locales/nn/messages.json | 42 ++- apps/browser/src/_locales/or/messages.json | 42 ++- apps/browser/src/_locales/pl/messages.json | 182 +++++++----- apps/browser/src/_locales/pt_BR/messages.json | 42 ++- apps/browser/src/_locales/pt_PT/messages.json | 44 ++- apps/browser/src/_locales/ro/messages.json | 42 ++- apps/browser/src/_locales/ru/messages.json | 42 ++- apps/browser/src/_locales/si/messages.json | 42 ++- apps/browser/src/_locales/sk/messages.json | 42 ++- apps/browser/src/_locales/sl/messages.json | 42 ++- apps/browser/src/_locales/sr/messages.json | 42 ++- apps/browser/src/_locales/sv/messages.json | 42 ++- apps/browser/src/_locales/te/messages.json | 42 ++- apps/browser/src/_locales/th/messages.json | 42 ++- apps/browser/src/_locales/tr/messages.json | 62 +++- apps/browser/src/_locales/uk/messages.json | 42 ++- apps/browser/src/_locales/vi/messages.json | 42 ++- apps/browser/src/_locales/zh_CN/messages.json | 48 +++- apps/browser/src/_locales/zh_TW/messages.json | 42 ++- 60 files changed, 2735 insertions(+), 575 deletions(-) diff --git a/apps/browser/src/_locales/ar/messages.json b/apps/browser/src/_locales/ar/messages.json index 725eef3bd07..7ba554b13b7 100644 --- a/apps/browser/src/_locales/ar/messages.json +++ b/apps/browser/src/_locales/ar/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "قيِّم هذه الإضافة" }, - "rateExtensionDesc": { - "message": "يرجى النظر في مساعدتنا بكتابة تعليق إيجابي!" - }, "browserNotSupportClipboard": { "message": "متصفح الويب الخاص بك لا يدعم خاصية النسخ السهل. يرجى استخدام النسخ اليدوي." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/az/messages.json b/apps/browser/src/_locales/az/messages.json index 4b83341dce5..aea4e46abeb 100644 --- a/apps/browser/src/_locales/az/messages.json +++ b/apps/browser/src/_locales/az/messages.json @@ -193,10 +193,10 @@ "message": "Kimliyi avto-doldur" }, "fillVerificationCode": { - "message": "Fill verification code" + "message": "Doğrulama kodunu doldur" }, "fillVerificationCodeAria": { - "message": "Fill Verification Code", + "message": "Doğrulama Kodunu Doldur", "description": "Aria label for the heading displayed the inline menu for totp code autofill" }, "generatePasswordCopied": { @@ -648,9 +648,6 @@ "rateExtension": { "message": "Uzantını qiymətləndir" }, - "rateExtensionDesc": { - "message": "Gözəl bir rəy ilə bizə dəstək ola bilərsiniz!" - }, "browserNotSupportClipboard": { "message": "Veb brauzeriniz lövhəyə kopyalamağı dəstəkləmir. Əvəzində əllə kopyalayın." }, @@ -3588,11 +3585,11 @@ "description": "Screen reader text (aria-label) for unlock account button in overlay" }, "totpCodeAria": { - "message": "Time-based One-Time Password Verification Code", + "message": "Vaxt əsaslı Təkistifadəlik Parol Doğrulama Kodu", "description": "Aria label for the totp code displayed in the inline menu for autofill" }, "totpSecondsSpanAria": { - "message": "Time remaining before current TOTP expires", + "message": "Hazırkı TOTP-nin bitməsinə qalan vaxt", "description": "Aria label for the totp seconds displayed in the inline menu for autofill" }, "fillCredentialsFor": { @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Uzantı ikonunda giriş üçün avto-doldurma təklif sayını göstər" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "İlkin sistem" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Vacib bildiriş" + }, + "setupTwoStepLogin": { + "message": "İki addımlı girişi qur" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden, 2025-ci ilin Fevral ayından etibarən yeni cihazlardan gələn girişləri doğrulamaq üçün hesabınızın e-poçtuna bir kod göndərəcək." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Hesabınızı qorumaq üçün alternativ bir yol kimi iki addımlı girişi qura və ya e-poçtunuzu müraciət edə biləcəyiniz e-poçtla dəyişdirə bilərsiniz." + }, + "remindMeLater": { + "message": "Daha sonra xatırlat" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "$EMAIL$ e-poçtunuza güvənli şəkildə müraciət edə bilirsiniz?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Xeyr, edə bilmirəm" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Bəli, e-poçtuma güvənli şəkildə müraciət edə bilirəm" + }, + "turnOnTwoStepLogin": { + "message": "İki addımlı girişi işə sal" + }, + "changeAcctEmail": { + "message": "Hesabın e-poçtunu dəyişdir" + }, "extensionWidth": { "message": "Uzantı eni" }, diff --git a/apps/browser/src/_locales/be/messages.json b/apps/browser/src/_locales/be/messages.json index 3010bb6b6c6..37b234105c3 100644 --- a/apps/browser/src/_locales/be/messages.json +++ b/apps/browser/src/_locales/be/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Ацаніць пашырэнне" }, - "rateExtensionDesc": { - "message": "Падумайце пра тое, каб дапамагчы нам добрым водгукам!" - }, "browserNotSupportClipboard": { "message": "Ваш вэб-браўзер не падтрымлівае капіяванне даных у буфер абмену. Скапіюйце іх уручную." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/bg/messages.json b/apps/browser/src/_locales/bg/messages.json index 56d2ce80ea1..554650d5800 100644 --- a/apps/browser/src/_locales/bg/messages.json +++ b/apps/browser/src/_locales/bg/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Оценяване на разширението" }, - "rateExtensionDesc": { - "message": "Молим да ни помогнете, като оставите положителен отзив!" - }, "browserNotSupportClipboard": { "message": "Браузърът не поддържа копиране в буфера, затова копирайте на ръка." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Показване на броя предложения за автоматично попълване на данни за вписване върху иконката на добавката" }, + "showQuickCopyActions": { + "message": "Показване на действията за бързо копиране в трезора" + }, "systemDefault": { "message": "По подразбиране за системата" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Бета" }, + "importantNotice": { + "message": "Важно съобщение" + }, + "setupTwoStepLogin": { + "message": "Настройте двустепенно удостоверяване" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Битуорден ще изпрати код до е-пощата Ви, за потвърждаване на вписването от нови устройства. Това ще започне от февруари 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Можете да настроите двустепенно удостоверяване, като различен метод на защита, или ако е необходимо да промените е-пощата си с такава, до която имате достъп." + }, + "remindMeLater": { + "message": "Напомнете ми по-късно" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Имате ли сигурен достъп до е-пощата си – $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Не, нямам" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Да, имам достъп до е-пощата си" + }, + "turnOnTwoStepLogin": { + "message": "Включване на двустепенното удостоверяване" + }, + "changeAcctEmail": { + "message": "Промяна на е-пощата" + }, "extensionWidth": { "message": "Ширина на разширението" }, diff --git a/apps/browser/src/_locales/bn/messages.json b/apps/browser/src/_locales/bn/messages.json index a03e86b860b..c31f3511a5f 100644 --- a/apps/browser/src/_locales/bn/messages.json +++ b/apps/browser/src/_locales/bn/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "এক্সটেনশনটি মূল্যায়ন করুন" }, - "rateExtensionDesc": { - "message": "দয়া করে একটি ভাল পর্যালোচনার মাধ্যমে সাহায্য করতে আমাদের বিবেচনা করুন!" - }, "browserNotSupportClipboard": { "message": "আপনার ওয়েব ব্রাউজার সহজে ক্লিপবোর্ড অনুলিপি সমর্থন করে না। পরিবর্তে এটি নিজেই অনুলিপি করুন।" }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/bs/messages.json b/apps/browser/src/_locales/bs/messages.json index 97997f677b2..d6cb591a524 100644 --- a/apps/browser/src/_locales/bs/messages.json +++ b/apps/browser/src/_locales/bs/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Rate the extension" }, - "rateExtensionDesc": { - "message": "Please consider helping us out with a good review!" - }, "browserNotSupportClipboard": { "message": "Your web browser does not support easy clipboard copying. Copy it manually instead." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/ca/messages.json b/apps/browser/src/_locales/ca/messages.json index 9ca06f2b50a..e405a18f885 100644 --- a/apps/browser/src/_locales/ca/messages.json +++ b/apps/browser/src/_locales/ca/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Valora aquesta extensió" }, - "rateExtensionDesc": { - "message": "Considereu ajudar-nos amb una bona valoració!" - }, "browserNotSupportClipboard": { "message": "El vostre navegador web no admet la còpia fàcil del porta-retalls. Copieu-ho manualment." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/cs/messages.json b/apps/browser/src/_locales/cs/messages.json index b74fe0c9cfd..cf3ae3e2fd0 100644 --- a/apps/browser/src/_locales/cs/messages.json +++ b/apps/browser/src/_locales/cs/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Ohodnotit rozšíření" }, - "rateExtensionDesc": { - "message": "Pomozte nám napsáním dobré recenze!" - }, "browserNotSupportClipboard": { "message": "Váš webový prohlížeč nepodporuje automatické kopírování do schránky. Musíte ho zkopírovat ručně." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Zobrazit počet návrhů automatického vyplňování přihlášení na ikoně rozšíření" }, + "showQuickCopyActions": { + "message": "Zobrazit akce rychlé kopie v trezoru" + }, "systemDefault": { "message": "Systémový výchozí" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Důležité upozornění" + }, + "setupTwoStepLogin": { + "message": "Nastavit dvoufázové přihlášení" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden odešle kód na e-mail Vašeho účtu pro ověření přihlášení z nových zařízení počínaje únorem 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Dvoufázové přihlášení můžete nastavit jako alternativní způsob ochrany Vašeho účtu nebo změnit svůj e-mail na ten, k němuž můžete přistupovat." + }, + "remindMeLater": { + "message": "Připomenout později" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Máte spolehlivý přístup ke svému e-mailu $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Ne, nemám" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Ano, ke svému e-mailu mám přístup" + }, + "turnOnTwoStepLogin": { + "message": "Zapnout dvoufázové přihlášení" + }, + "changeAcctEmail": { + "message": "Změnit e-mail účtu" + }, "extensionWidth": { "message": "Šířka rozšíření" }, diff --git a/apps/browser/src/_locales/cy/messages.json b/apps/browser/src/_locales/cy/messages.json index 3d3866097f2..3d4f1c73c02 100644 --- a/apps/browser/src/_locales/cy/messages.json +++ b/apps/browser/src/_locales/cy/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Rhoi eich barn ar yr estyniad" }, - "rateExtensionDesc": { - "message": "Ystyriwch ein helpu ni gydag adolygiad da!" - }, "browserNotSupportClipboard": { "message": "Dyw eich porwr gwe ddim yn cefnogi copïo drwy'r clipfwrdd yn hawdd. Copïwch â llaw yn lle." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/da/messages.json b/apps/browser/src/_locales/da/messages.json index 78229082235..207f409378e 100644 --- a/apps/browser/src/_locales/da/messages.json +++ b/apps/browser/src/_locales/da/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Bedøm udvidelsen" }, - "rateExtensionDesc": { - "message": "Overvej om du vil hjælpe os med en god anmeldelse!" - }, "browserNotSupportClipboard": { "message": "Din webbrowser understøtter ikke udklipsholder kopiering. Kopiér det manuelt i stedet." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Vis antal login-autoudfyldningsforslag på udvidelsesikon" }, + "showQuickCopyActions": { + "message": "Vis hurtig-kopihandlinger på Boks" + }, "systemDefault": { "message": "Systemstandard" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Vigtig notits" + }, + "setupTwoStepLogin": { + "message": "Opsæt totrins-login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Startende i februar 2025, sender Bitwarden en kode til kontoe-mailadressen for at bekræfte logins fra nye enheder." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Man kan opsætte totrins-login som en alternativ måde at beskytte sin konto på eller ændre sin e-mail til en, man kan tilgå." + }, + "remindMeLater": { + "message": "Påmind senere" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Er der pålidelig adgang til e-mailadressen, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Nej, jeg gør ikke" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Ja, e-mailadressen kan pålideligt tilgås" + }, + "turnOnTwoStepLogin": { + "message": "Slå totrins-login til" + }, + "changeAcctEmail": { + "message": "Skift kontoe-mailadresse" + }, "extensionWidth": { "message": "Udvidelsesbredde" }, diff --git a/apps/browser/src/_locales/de/messages.json b/apps/browser/src/_locales/de/messages.json index 098af701cd6..169cdf6aa78 100644 --- a/apps/browser/src/_locales/de/messages.json +++ b/apps/browser/src/_locales/de/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Erweiterung bewerten" }, - "rateExtensionDesc": { - "message": "Wir würden uns freuen, wenn du uns mit einer positiven Bewertung helfen könntest!" - }, "browserNotSupportClipboard": { "message": "Den Browser unterstützt das einfache Kopieren nicht. Bitte kopiere es manuell." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Anzahl der Vorschläge zum Auto-Ausfüllen von Zugangsdaten auf dem Erweiterungssymbol anzeigen" }, + "showQuickCopyActions": { + "message": "Schnellkopier-Aktionen im Tresor anzeigen" + }, "systemDefault": { "message": "Systemstandard" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Wichtiger Hinweis" + }, + "setupTwoStepLogin": { + "message": "Richte die zweistufige Anmeldung ein" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden wird einen Code an deine Konto-E-Mail-Adresse senden, um Anmeldungen von neuen Geräten ab Februar 2025 zu überprüfen." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Du kannst die zweistufige Anmeldung als eine alternative Methode einrichten, um deinen Account zu schützen, oder ändere deine E-Mail-Adresse zu einer, auf die du zugreifen kannst." + }, + "remindMeLater": { + "message": "Erinnere mich später" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Hast du zuverlässigen Zugriff auf deine E-Mail-Adresse $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Nein, habe ich nicht" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Ja, ich kann zuverlässig auf meine E-Mails zugreifen" + }, + "turnOnTwoStepLogin": { + "message": "Aktiviere die zweistufige Anmeldung" + }, + "changeAcctEmail": { + "message": "Ändere die E-Mail-Adresse des Kontos" + }, "extensionWidth": { "message": "Breite der Erweiterung" }, diff --git a/apps/browser/src/_locales/el/messages.json b/apps/browser/src/_locales/el/messages.json index be853922400..d6ae3c52333 100644 --- a/apps/browser/src/_locales/el/messages.json +++ b/apps/browser/src/_locales/el/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Βαθμολογήστε την επέκταση" }, - "rateExtensionDesc": { - "message": "Παρακαλούμε σκεφτείτε να μας βοηθήσετε με μια καλή κριτική!" - }, "browserNotSupportClipboard": { "message": "Το πρόγραμμα περιήγησης ιστού δεν υποστηρίζει εύκολη αντιγραφή πρόχειρου. Αντιγράψτε το με το χέρι αντ'αυτού." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Εμφάνιση αριθμού προτάσεων αυτόματης συμπλήρωσης σύνδεσης στο εικονίδιο επέκτασης" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "Προεπιλογή συστήματος" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/en_GB/messages.json b/apps/browser/src/_locales/en_GB/messages.json index 13db7b5080b..a771b390b1b 100644 --- a/apps/browser/src/_locales/en_GB/messages.json +++ b/apps/browser/src/_locales/en_GB/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Rate the extension" }, - "rateExtensionDesc": { - "message": "Please consider helping us out with a good review!" - }, "browserNotSupportClipboard": { "message": "Your web browser does not support easy clipboard copying. Copy it manually instead." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/en_IN/messages.json b/apps/browser/src/_locales/en_IN/messages.json index 173fe8e788a..17c06b57c2a 100644 --- a/apps/browser/src/_locales/en_IN/messages.json +++ b/apps/browser/src/_locales/en_IN/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Rate the extension" }, - "rateExtensionDesc": { - "message": "Please consider helping us out with a good review!" - }, "browserNotSupportClipboard": { "message": "Your web browser does not support easy clipboard copying. Copy it manually instead." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/es/messages.json b/apps/browser/src/_locales/es/messages.json index 4b3dc12f684..eaff576d94b 100644 --- a/apps/browser/src/_locales/es/messages.json +++ b/apps/browser/src/_locales/es/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Valora la extensión" }, - "rateExtensionDesc": { - "message": "¡Por favor, considera ayudarnos con una buena reseña!" - }, "browserNotSupportClipboard": { "message": "Tu navegador web no soporta copiar al portapapeles facilmente. Cópialo manualmente." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/et/messages.json b/apps/browser/src/_locales/et/messages.json index 4f17a302633..c772a7b23e7 100644 --- a/apps/browser/src/_locales/et/messages.json +++ b/apps/browser/src/_locales/et/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Hinda seda laiendust" }, - "rateExtensionDesc": { - "message": "Soovi korral võid meid positiivse hinnanguga toetada!" - }, "browserNotSupportClipboard": { "message": "Kasutatav brauser ei toeta lihtsat lõikelaua kopeerimist. Kopeeri see käsitsi." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/eu/messages.json b/apps/browser/src/_locales/eu/messages.json index d98ae43081f..117751b3794 100644 --- a/apps/browser/src/_locales/eu/messages.json +++ b/apps/browser/src/_locales/eu/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Baloratu gehigarria" }, - "rateExtensionDesc": { - "message": "Mesedez, aipu on batekin lagundu!" - }, "browserNotSupportClipboard": { "message": "Zure web nabigatzaileak ez du onartzen arbelean erraz kopiatzea. Eskuz kopiatu." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/fa/messages.json b/apps/browser/src/_locales/fa/messages.json index 6b88d9e5234..47bf414db2f 100644 --- a/apps/browser/src/_locales/fa/messages.json +++ b/apps/browser/src/_locales/fa/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "به این افزونه امتیاز دهید" }, - "rateExtensionDesc": { - "message": "لطفاً با یک بررسی خوب به ما کمک کنید!" - }, "browserNotSupportClipboard": { "message": "مرورگر شما از کپی کلیپ بورد آسان پشتیبانی نمی‌کند. به جای آن به صورت دستی کپی کنید." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/fi/messages.json b/apps/browser/src/_locales/fi/messages.json index 0a2313d7dca..f0104488d32 100644 --- a/apps/browser/src/_locales/fi/messages.json +++ b/apps/browser/src/_locales/fi/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Arvioi laajennus" }, - "rateExtensionDesc": { - "message": "Harkitse tukemistamme hyvällä arvostelulla!" - }, "browserNotSupportClipboard": { "message": "Selaimesi ei tue helppoa leikepöydälle kopiointia. Kopioi kohde manuaalisesti." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Näytä automaattitäytön ehdotusten määrä laajennuksen kuvakkeessa" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "Järjestelmän oletus" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Laajennuksen leveys" }, diff --git a/apps/browser/src/_locales/fil/messages.json b/apps/browser/src/_locales/fil/messages.json index 33cc408a0d6..ebaf884a141 100644 --- a/apps/browser/src/_locales/fil/messages.json +++ b/apps/browser/src/_locales/fil/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "I-rate ang extension" }, - "rateExtensionDesc": { - "message": "Paki-isipan ang pagtulong sa amin sa pamamagitan ng isang magandang review!" - }, "browserNotSupportClipboard": { "message": "Hindi suportado ng iyong web browser ang madaling pag-copy ng clipboard. Kopya ito manually sa halip." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/fr/messages.json b/apps/browser/src/_locales/fr/messages.json index 679b6248033..0ff35958be0 100644 --- a/apps/browser/src/_locales/fr/messages.json +++ b/apps/browser/src/_locales/fr/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Noter l'extension" }, - "rateExtensionDesc": { - "message": "Merci de nous aider en mettant une bonne note !" - }, "browserNotSupportClipboard": { "message": "Votre navigateur web ne supporte pas la copie rapide depuis le presse-papier. Copiez-le manuellement à la place." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Afficher le nombre de suggestions de saisie automatique d'identifiant sur l'icône d'extension" }, + "showQuickCopyActions": { + "message": "Afficher les actions de copie rapide dans le coffre" + }, "systemDefault": { "message": "Par défaut" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Largeur de l'extension" }, diff --git a/apps/browser/src/_locales/gl/messages.json b/apps/browser/src/_locales/gl/messages.json index 7240d88a587..beeae76232a 100644 --- a/apps/browser/src/_locales/gl/messages.json +++ b/apps/browser/src/_locales/gl/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Valorar a extensión" }, - "rateExtensionDesc": { - "message": "Por favor, considera axudarnos cunha boa recensión!" - }, "browserNotSupportClipboard": { "message": "O teu navegador web non soporta copia doada ao portapapeis. Cópiao manualmente no seu lugar." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/he/messages.json b/apps/browser/src/_locales/he/messages.json index cb2c8782c9e..a069afdc69a 100644 --- a/apps/browser/src/_locales/he/messages.json +++ b/apps/browser/src/_locales/he/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "דירוג הרחבה" }, - "rateExtensionDesc": { - "message": "אם נהנית מהתוכנה, בבקשה דרג את התוכנה וכתוב דירוג עם חוות דעת טובה!" - }, "browserNotSupportClipboard": { "message": "הדפדפן שלך לא תומך בהעתקה ללוח. אנא העתק בצורה ידנית." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/hi/messages.json b/apps/browser/src/_locales/hi/messages.json index b14aa89def1..288aa1171ce 100644 --- a/apps/browser/src/_locales/hi/messages.json +++ b/apps/browser/src/_locales/hi/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Rate the Extension" }, - "rateExtensionDesc": { - "message": "कृपया एक अच्छी समीक्षा के साथ हमारी मदत करने पर विचार करें!" - }, "browserNotSupportClipboard": { "message": "आपका वेब ब्राउज़र आसान क्लिपबोर्ड कॉपीिंग का समर्थन नहीं करता है। इसके बजाय इसे मैन्युअल रूप से कॉपी करें।" }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/hr/messages.json b/apps/browser/src/_locales/hr/messages.json index 4e280dcff43..a8c680ac666 100644 --- a/apps/browser/src/_locales/hr/messages.json +++ b/apps/browser/src/_locales/hr/messages.json @@ -20,16 +20,16 @@ "message": "Stvori račun" }, "newToBitwarden": { - "message": "New to Bitwarden?" + "message": "Novi u Bitwardenu?" }, "logInWithPasskey": { - "message": "Log in with passkey" + "message": "Prijava pristupnim ključem" }, "useSingleSignOn": { - "message": "Use single sign-on" + "message": "Jedinstvena prijava (SSO)" }, "welcomeBack": { - "message": "Welcome back" + "message": "Dobro došli natrag" }, "setAStrongPassword": { "message": "Postavi jaku lozinku" @@ -84,7 +84,7 @@ "message": "Pridruži se organizaciji" }, "joinOrganizationName": { - "message": "Join $ORGANIZATIONNAME$", + "message": "Pidruži se $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -120,7 +120,7 @@ "message": "Kopiraj lozinku" }, "copyPassphrase": { - "message": "Copy passphrase" + "message": "Kopiraj fraznu lozinku" }, "copyNote": { "message": "Kopiraj bilješku" @@ -153,13 +153,13 @@ "message": "Kopiraj OIB" }, "copyPrivateKey": { - "message": "Copy private key" + "message": "Kopiraj privatni ključ" }, "copyPublicKey": { - "message": "Copy public key" + "message": "Kopiraj javni ključ" }, "copyFingerprint": { - "message": "Copy fingerprint" + "message": "Kopiraj otisak prsta" }, "copyCustomField": { "message": "Kopiraj $FIELD$", @@ -177,7 +177,7 @@ "message": "Kopiraj bilješke" }, "fill": { - "message": "Fill", + "message": "Ispuni", "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." }, "autoFill": { @@ -193,10 +193,10 @@ "message": "Auto-ispuna identiteta" }, "fillVerificationCode": { - "message": "Fill verification code" + "message": "Ispuni kôd za provjeru" }, "fillVerificationCodeAria": { - "message": "Fill Verification Code", + "message": "Ispuni kôd za provjeru", "description": "Aria label for the heading displayed the inline menu for totp code autofill" }, "generatePasswordCopied": { @@ -284,7 +284,7 @@ "message": "Nastavi na web aplikaciju?" }, "continueToWebAppDesc": { - "message": "Pronađi viđe značajki svojeg Bitwarden računa u web aplikaciji." + "message": "Pronađi više značajki svojeg Bitwarden računa u web aplikaciji." }, "continueToHelpCenter": { "message": "Nastavi u centar za pomoć?" @@ -349,10 +349,10 @@ "message": "Stvori laka i sigurna iskustva prijave bez tradicionalnih lozinki uz Passwordless.dev. Saznaj više na web stranici bitwarden.com." }, "freeBitwardenFamilies": { - "message": "Besplatan obiteljski Bitwarden" + "message": "Besplatni Bitwarden Families" }, "freeBitwardenFamiliesPageDesc": { - "message": "Ispunjavaš uvjete za besplatni obiteljski Bitwarden. Iskoristi ovu ponudu u web aplikaciji već danas." + "message": "Ispunjavaš uvjete za besplatni Bitwarden Families. Iskoristi ovu ponudu u web aplikaciji već danas." }, "version": { "message": "Verzija" @@ -443,7 +443,7 @@ "message": "Generiraj lozinku" }, "generatePassphrase": { - "message": "Generate passphrase" + "message": "Generiraj frazu lozinke" }, "regeneratePassword": { "message": "Ponovno generiraj lozinku" @@ -607,7 +607,7 @@ "message": "Pokreni web stranicu" }, "launchWebsiteName": { - "message": "Launch website $ITEMNAME$", + "message": "Otvori stranicu $ITEMNAME$", "placeholders": { "itemname": { "content": "$1", @@ -648,9 +648,6 @@ "rateExtension": { "message": "Ocijeni proširenje" }, - "rateExtensionDesc": { - "message": "Razmotri da nam pomogneš dobrom recenzijom!" - }, "browserNotSupportClipboard": { "message": "Web preglednik ne podržava jednostavno kopiranje međuspremnika. Umjesto toga ručno kopirajte." }, @@ -804,7 +801,7 @@ "message": "Poslali smo e-poštu s podsjetnikom glavne lozinke." }, "verificationCodeRequired": { - "message": "Potvrdni kôd je obavezan." + "message": "Kôd za provjeru je obavezan." }, "webauthnCancelOrTimeout": { "message": "Autentifikacija je otkazana ili je trajala predugo. Molimo pokušaj ponovno." @@ -862,7 +859,7 @@ "message": "Prijavi se" }, "logInToBitwarden": { - "message": "Log in to Bitwarden" + "message": "Prijavi se u Bitwarden" }, "restartRegistration": { "message": "Ponovno pokreni registraciju" @@ -1133,7 +1130,7 @@ "description": "WARNING (should stay in capitalized letters if the language permits)" }, "warningCapitalized": { - "message": "Warning", + "message": "Upozorenje", "description": "Warning (should maintain locale-relevant capitalization)" }, "confirmVaultExport": { @@ -1312,7 +1309,7 @@ "message": "Automatski kopiraj TOTP" }, "disableAutoTotpCopyDesc": { - "message": "Ako za prijavu postoji autentifikatorski ključ, kopiraj TOTP kontrolni kôd u međuspremnik nakon auto-ispune prijave." + "message": "Ako za prijavu postoji autentifikatorski ključ, kopiraj TOTP kôd za provjeru u međuspremnik nakon auto-ispune prijave." }, "enableAutoBiometricsPrompt": { "message": "Traži biometrijsku autentifikaciju pri pokretanju" @@ -1324,16 +1321,16 @@ "message": "Za korištenje ove značajke potrebno je Premium članstvo." }, "enterVerificationCodeApp": { - "message": "Unesi 6-znamenkasti kontrolni kôd iz autentifikatorske aplikacije." + "message": "Unesi 6-znamenkasti kôd za provjeru iz autentifikatorske aplikacije." }, "authenticationTimeout": { - "message": "Authentication timeout" + "message": "Istek vremena za autentifikaciju" }, "authenticationSessionTimedOut": { - "message": "The authentication session timed out. Please restart the login process." + "message": "Sesija za autentifikaciju je istekla. Ponovi proces prijave." }, "enterVerificationCodeEmail": { - "message": "Unesi 6-znamenkasti kontrolni kôd poslan e-poštom na $EMAIL$.", + "message": "Unesi 6-znamenkasti kôd za provjeru poslan e-poštom na $EMAIL$.", "placeholders": { "email": { "content": "$1", @@ -1450,7 +1447,7 @@ "message": "URL poslužitelja" }, "selfHostBaseUrl": { - "message": "Self-host server URL", + "message": "URL vlastitog poslužitelja", "description": "Label for field requesting a self-hosted integration service URL" }, "apiUrl": { @@ -1482,10 +1479,10 @@ "message": "Prikaži prijedloge auto-ispune na poljima obrazaca" }, "showInlineMenuIdentitiesLabel": { - "message": "Display identities as suggestions" + "message": "Prikaži identitete kao prijedloge" }, "showInlineMenuCardsLabel": { - "message": "Display cards as suggestions" + "message": "Prikaži platne kartice kao prijedloge" }, "showInlineMenuOnIconSelectionLabel": { "message": "Prikaži prijedloge kada je odabrana ikona" @@ -1604,7 +1601,7 @@ "description": "This describes a value that is 'linked' (tied) to another value." }, "popup2faCloseMessage": { - "message": "Ako klikneš izvan iskočnog prozora, za provjeru kontrolnog kôda iz e-pošte, on će se zatvoriti. Želiš li ovaj iskočni prozor otvoriti u novom prozoru kako se ne bi zatvorio?" + "message": "Ako klikneš izvan iskočnog prozora, za provjeru kôda za provjeru iz e-pošte, on će se zatvoriti. Želiš li ovaj iskočni prozor otvoriti u novom prozoru kako se ne bi zatvorio?" }, "popupU2fCloseMessage": { "message": "Ovaj preglednik ne može obraditi U2F zahtjeve u ovom iskočnom prozoru. Želiš li otvoriti ovaj iskočni prozor u novom prozoru za prijavu putem U2F?" @@ -1679,7 +1676,7 @@ "message": "prosinac" }, "securityCode": { - "message": "Kontrolni broj" + "message": "Sigurnosni kôd" }, "ex": { "message": "npr." @@ -1778,7 +1775,7 @@ "message": "Identitet" }, "typeSshKey": { - "message": "SSH key" + "message": "SSH ključ" }, "newItemHeader": { "message": "Novi $TYPE$", @@ -1811,13 +1808,13 @@ "message": "Povijest" }, "generatorHistory": { - "message": "Generator history" + "message": "Povijest generatora" }, "clearGeneratorHistoryTitle": { - "message": "Clear generator history" + "message": "Očisti povijest generatora" }, "cleargGeneratorHistoryDescription": { - "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + "message": "Cijela povijest generatora biti će trajno izbirsana. Sigurno želiš nastaviti?" }, "back": { "message": "Natrag" @@ -1856,7 +1853,7 @@ "message": "Sigurne bilješke" }, "sshKeys": { - "message": "SSH Keys" + "message": "SSH ključevi" }, "clear": { "message": "Očisti", @@ -1939,10 +1936,10 @@ "message": "Očisti povijest" }, "nothingToShow": { - "message": "Nothing to show" + "message": "Ništa za prikazati" }, "nothingGeneratedRecently": { - "message": "You haven't generated anything recently" + "message": "Ništa nije generirano" }, "remove": { "message": "Ukloni" @@ -2531,7 +2528,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendPasswordDescV3": { - "message": "Add an optional password for recipients to access this Send.", + "message": "Dodaj opcionalnu lozinku za primatelje ovog Senda.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2729,7 +2726,7 @@ "description": "Used as a card title description on the set password page to explain why the user is there" }, "cardMetrics": { - "message": "out of $TOTAL$", + "message": "od $TOTAL$", "placeholders": { "total": { "content": "$1", @@ -2894,10 +2891,10 @@ "message": "Generiraj korisničko ime" }, "generateEmail": { - "message": "Generate email" + "message": "Generiraj e-poštu" }, "spinboxBoundariesHint": { - "message": "Value must be between $MIN$ and $MAX$.", + "message": "Vrijednost mora biti u rasponu $MIN$ - $MAX$.", "description": "Explains spin box minimum and maximum values to the user", "placeholders": { "min": { @@ -2911,7 +2908,7 @@ } }, "passwordLengthRecommendationHint": { - "message": " Use $RECOMMENDED$ characters or more to generate a strong password.", + "message": " Koristi $RECOMMENDED$ i više znakova za generiranje jake lozinke.", "description": "Appended to `spinboxBoundariesHint` to recommend a length to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -2921,7 +2918,7 @@ } }, "passphraseNumWordsRecommendationHint": { - "message": " Use $RECOMMENDED$ words or more to generate a strong passphrase.", + "message": " Koristi $RECOMMENDED$ i više riječi za generiranje jake frazne lozinke.", "description": "Appended to `spinboxBoundariesHint` to recommend a number of words to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -2971,11 +2968,11 @@ "message": "Generiraj pseudonim e-pošte s vanjskom uslugom prosljeđivanja." }, "forwarderDomainName": { - "message": "Email domain", + "message": "Domena e-pošte", "description": "Labels the domain name email forwarder service option" }, "forwarderDomainNameHint": { - "message": "Choose a domain that is supported by the selected service", + "message": "Odaberi domenu koju podržava odabrani servis", "description": "Guidance provided for email forwarding services that support multiple email domains." }, "forwarderError": { @@ -3178,25 +3175,25 @@ "message": "Ponovno pošalji obavijest" }, "viewAllLogInOptions": { - "message": "View all log in options" + "message": "Pogledaj sve mogućnosti prijave" }, "viewAllLoginOptionsV1": { - "message": "View all log in options" + "message": "Pogledaj sve mogućnosti prijave" }, "notificationSentDevice": { "message": "Obavijest je poslana na tvoj uređaj." }, "aNotificationWasSentToYourDevice": { - "message": "A notification was sent to your device" + "message": "Obavijest je poslana na tvoj uređaj" }, "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { - "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + "message": "Provjeri je li trezor otključan i slaže li se jedinstvena fraza s drugim uređajem" }, "youWillBeNotifiedOnceTheRequestIsApproved": { - "message": "You will be notified once the request is approved" + "message": "Dobiti ćeš obavijest kada je tvoj zahtjev odobren" }, "needAnotherOptionV1": { - "message": "Need another option?" + "message": "Trebaš drugu opciju?" }, "loginInitiated": { "message": "Prijava pokrenuta" @@ -3292,16 +3289,16 @@ "message": "Otvara u novom prozoru" }, "rememberThisDeviceToMakeFutureLoginsSeamless": { - "message": "Remember this device to make future logins seamless" + "message": "Zapamti ovaj uređaj kako bi buduće prijave bile brže" }, "deviceApprovalRequired": { "message": "Potrebno je odobriti uređaj. Odaberi metodu odobravanja:" }, "deviceApprovalRequiredV2": { - "message": "Device approval required" + "message": "Potrebno odobrenje uređaja" }, "selectAnApprovalOptionBelow": { - "message": "Select an approval option below" + "message": "Odaberi opciju odobrenja" }, "rememberThisDevice": { "message": "Zapamti ovaj uređaj" @@ -3377,7 +3374,7 @@ "message": "Nedostaje e-pošta korisnika" }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "Nije pronađena e-pošta aktivnog korisnika. Odjava u tijeku..." }, "deviceTrusted": { "message": "Uređaj pouzdan" @@ -3588,11 +3585,11 @@ "description": "Screen reader text (aria-label) for unlock account button in overlay" }, "totpCodeAria": { - "message": "Time-based One-Time Password Verification Code", + "message": "Kôd za provjeru jednokratne lozinka zasnovane na vremenu (TOTP) ", "description": "Aria label for the totp code displayed in the inline menu for autofill" }, "totpSecondsSpanAria": { - "message": "Time remaining before current TOTP expires", + "message": "Preostalo vrijeme koda za provjeru", "description": "Aria label for the totp seconds displayed in the inline menu for autofill" }, "fillCredentialsFor": { @@ -3824,7 +3821,7 @@ "message": "Pristupanje" }, "loggedInExclamation": { - "message": "Logged in!" + "message": "Prijava uspješna!" }, "passkeyNotCopied": { "message": "Pristupni ključ neće biti kopiran" @@ -4288,7 +4285,7 @@ "message": "Najveća veličina datoteke je 500 MB" }, "deleteAttachmentName": { - "message": "Izbriši privitak", + "message": "Izbriši privitak $NAME$", "placeholders": { "name": { "content": "$1", @@ -4318,13 +4315,13 @@ "message": "Filtri" }, "filterVault": { - "message": "Filter vault" + "message": "Filtriraj trezor" }, "filterApplied": { - "message": "One filter applied" + "message": "Uključen jedan filter" }, "filterAppliedPlural": { - "message": "$COUNT$ filters applied", + "message": "Uključeno filtera: $COUNT$", "placeholders": { "count": { "content": "$1", @@ -4656,13 +4653,13 @@ "message": "Lokacija stavke" }, "fileSend": { - "message": "File Send" + "message": "Send datoteke" }, "fileSends": { "message": "Send datoteke" }, "textSend": { - "message": "Text Send" + "message": "Send teksta" }, "textSends": { "message": "Send tekstovi" @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Prikaži broj prijedloga auto-ispune na ikoni proširenja" }, + "showQuickCopyActions": { + "message": "Prikaži akcije brzog kopiranja na trezoru" + }, "systemDefault": { "message": "Zadano sustavom" }, @@ -4686,16 +4686,16 @@ "message": "Pravila tvrtke primijenjena su na ovu postavku" }, "sshPrivateKey": { - "message": "Private key" + "message": "Privatni ključ" }, "sshPublicKey": { - "message": "Public key" + "message": "Javni ključ" }, "sshFingerprint": { - "message": "Fingerprint" + "message": "Otisak prsta" }, "sshKeyAlgorithm": { - "message": "Key type" + "message": "Vrsta ključa" }, "sshKeyAlgorithmED25519": { "message": "ED25519" @@ -4752,171 +4752,207 @@ "message": "Autentifikacija" }, "fillGeneratedPassword": { - "message": "Fill generated password", + "message": "Ispuni generiranu lozinku", "description": "Heading for the password generator within the inline menu" }, "passwordRegenerated": { - "message": "Password regenerated", + "message": "Lozinka re-generirana", "description": "Notification message for when a password has been regenerated" }, "saveLoginToBitwarden": { - "message": "Save login to Bitwarden?", + "message": "Spremi prijavu u Bitwarden?", "description": "Confirmation message for saving a login to Bitwarden" }, "spaceCharacterDescriptor": { - "message": "Space", + "message": "Razmak", "description": "Represents the space key in screen reader content as a readable word" }, "tildeCharacterDescriptor": { - "message": "Tilde", + "message": "znak ˜", "description": "Represents the ~ key in screen reader content as a readable word" }, "backtickCharacterDescriptor": { - "message": "Backtick", + "message": "znak `", "description": "Represents the ` key in screen reader content as a readable word" }, "exclamationCharacterDescriptor": { - "message": "Exclamation mark", + "message": "znak !", "description": "Represents the ! key in screen reader content as a readable word" }, "atSignCharacterDescriptor": { - "message": "At sign", + "message": "znak @", "description": "Represents the @ key in screen reader content as a readable word" }, "hashSignCharacterDescriptor": { - "message": "Hash sign", + "message": "znak #", "description": "Represents the # key in screen reader content as a readable word" }, "dollarSignCharacterDescriptor": { - "message": "Dollar sign", + "message": "znak $", "description": "Represents the $ key in screen reader content as a readable word" }, "percentSignCharacterDescriptor": { - "message": "Percent sign", + "message": "znak %", "description": "Represents the % key in screen reader content as a readable word" }, "caretCharacterDescriptor": { - "message": "Caret", + "message": "znak ^", "description": "Represents the ^ key in screen reader content as a readable word" }, "ampersandCharacterDescriptor": { - "message": "Ampersand", + "message": "znak &", "description": "Represents the & key in screen reader content as a readable word" }, "asteriskCharacterDescriptor": { - "message": "Asterisk", + "message": "znak *", "description": "Represents the * key in screen reader content as a readable word" }, "parenLeftCharacterDescriptor": { - "message": "Left parenthesis", + "message": "lijeva zagrada (", "description": "Represents the ( key in screen reader content as a readable word" }, "parenRightCharacterDescriptor": { - "message": "Right parenthesis", + "message": "desna zagrada )", "description": "Represents the ) key in screen reader content as a readable word" }, "hyphenCharacterDescriptor": { - "message": "Underscore", + "message": "donja crtica _", "description": "Represents the _ key in screen reader content as a readable word" }, "underscoreCharacterDescriptor": { - "message": "Hyphen", + "message": "crtica -", "description": "Represents the - key in screen reader content as a readable word" }, "plusCharacterDescriptor": { - "message": "Plus", + "message": "znak +", "description": "Represents the + key in screen reader content as a readable word" }, "equalsCharacterDescriptor": { - "message": "Equals", + "message": "znak =", "description": "Represents the = key in screen reader content as a readable word" }, "braceLeftCharacterDescriptor": { - "message": "Left brace", + "message": "znak {", "description": "Represents the { key in screen reader content as a readable word" }, "braceRightCharacterDescriptor": { - "message": "Right brace", + "message": "znak }", "description": "Represents the } key in screen reader content as a readable word" }, "bracketLeftCharacterDescriptor": { - "message": "Left bracket", + "message": "znak [", "description": "Represents the [ key in screen reader content as a readable word" }, "bracketRightCharacterDescriptor": { - "message": "Right bracket", + "message": "zank ]", "description": "Represents the ] key in screen reader content as a readable word" }, "pipeCharacterDescriptor": { - "message": "Pipe", + "message": "znak |", "description": "Represents the | key in screen reader content as a readable word" }, "backSlashCharacterDescriptor": { - "message": "Back slash", + "message": "znak \\", "description": "Represents the back slash key in screen reader content as a readable word" }, "colonCharacterDescriptor": { - "message": "Colon", + "message": "znak :", "description": "Represents the : key in screen reader content as a readable word" }, "semicolonCharacterDescriptor": { - "message": "Semicolon", + "message": "znak ;", "description": "Represents the ; key in screen reader content as a readable word" }, "doubleQuoteCharacterDescriptor": { - "message": "Double quote", + "message": "znak \"", "description": "Represents the double quote key in screen reader content as a readable word" }, "singleQuoteCharacterDescriptor": { - "message": "Single quote", + "message": "znak '", "description": "Represents the ' key in screen reader content as a readable word" }, "lessThanCharacterDescriptor": { - "message": "Less than", + "message": "znak <", "description": "Represents the < key in screen reader content as a readable word" }, "greaterThanCharacterDescriptor": { - "message": "Greater than", + "message": "znak >", "description": "Represents the > key in screen reader content as a readable word" }, "commaCharacterDescriptor": { - "message": "Comma", + "message": "znak ,", "description": "Represents the , key in screen reader content as a readable word" }, "periodCharacterDescriptor": { - "message": "Period", + "message": "znak .", "description": "Represents the . key in screen reader content as a readable word" }, "questionCharacterDescriptor": { - "message": "Question mark", + "message": "znak ?", "description": "Represents the ? key in screen reader content as a readable word" }, "forwardSlashCharacterDescriptor": { - "message": "Forward slash", + "message": "znak /", "description": "Represents the / key in screen reader content as a readable word" }, "lowercaseAriaLabel": { - "message": "Lowercase" + "message": "Mala slova" }, "uppercaseAriaLabel": { - "message": "Uppercase" + "message": "Velika slova" }, "generatedPassword": { - "message": "Generated password" + "message": "Generiraj lozinku" }, "compactMode": { - "message": "Compact mode" + "message": "Kompaktni način" }, "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { - "message": "Extension width" + "message": "Širina proširenja" }, "wide": { - "message": "Wide" + "message": "Široko" }, "extraWide": { - "message": "Extra wide" + "message": "Ekstra široko" } } diff --git a/apps/browser/src/_locales/hu/messages.json b/apps/browser/src/_locales/hu/messages.json index 04644910c63..07662b83157 100644 --- a/apps/browser/src/_locales/hu/messages.json +++ b/apps/browser/src/_locales/hu/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Bővítmény értékelése" }, - "rateExtensionDesc": { - "message": "Kérlek, fontold meg egy jó értékelés hagyását, ezzel segítve nekünk!" - }, "browserNotSupportClipboard": { "message": "A webböngésződ nem támogat könnyű vágólap másolást. Másold manuálisan inkább." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Az automatikus bejelentkezési kitöltési javaslatok számának megjelenítése a bővítmény ikonján" }, + "showQuickCopyActions": { + "message": "Gyors másolási műveletek megjelenítése a Széfen" + }, "systemDefault": { "message": "Rendszer alapértelmezett" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Béta" }, + "importantNotice": { + "message": "Fontos megjegyzés" + }, + "setupTwoStepLogin": { + "message": "Kétlépéses bejelentkezés beüzemelése" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "A Bitwarden 2025 februárjától kódot küld a fiókhoz tartozó email-címre, amellyel ellenőrizhetők az új eszközökről történő bejelentkezések." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "A fiók védelmének alternatív módjaként beállíthatunk kétlépcsős bejelentkezést vagy módosíthatjuk az email címet egy elérhetőre." + }, + "remindMeLater": { + "message": "Emlékeztetés később" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Megbízható a hozzáférés $EMAIL$ email címhez?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Nem, nem érem el" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Igen, megbízhatóan hozzáférek az emailjeimhez" + }, + "turnOnTwoStepLogin": { + "message": "Kétlépéses bejelentkezés bekapcsolása" + }, + "changeAcctEmail": { + "message": "Fiók email cím megváltoztatása" + }, "extensionWidth": { "message": "Kiterjesztés szélesség" }, diff --git a/apps/browser/src/_locales/id/messages.json b/apps/browser/src/_locales/id/messages.json index 7de07f17f10..ef8e1431284 100644 --- a/apps/browser/src/_locales/id/messages.json +++ b/apps/browser/src/_locales/id/messages.json @@ -3,7 +3,7 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden Pengelola Sandi", + "message": "Pengelola Sandi Bitwarden", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { @@ -193,10 +193,10 @@ "message": "Autofill identitas" }, "fillVerificationCode": { - "message": "Fill verification code" + "message": "Isikan kode verifikasi" }, "fillVerificationCodeAria": { - "message": "Fill Verification Code", + "message": "Isikan Kode Verifikasi", "description": "Aria label for the heading displayed the inline menu for totp code autofill" }, "generatePasswordCopied": { @@ -648,9 +648,6 @@ "rateExtension": { "message": "Nilai Ekstensi" }, - "rateExtensionDesc": { - "message": "Mohon pertimbangkan membantu kami dengan ulasan yang baik!" - }, "browserNotSupportClipboard": { "message": "Peramban Anda tidak mendukung menyalin clipboard dengan mudah. Salin secara manual." }, @@ -3181,7 +3178,7 @@ "message": "Lihat semua pilihan masuk" }, "viewAllLoginOptionsV1": { - "message": "View all log in options" + "message": "Lihat semua pilihan masuk" }, "notificationSentDevice": { "message": "Sebuah pemberitahuan dikirim ke perangkat Anda." @@ -3588,11 +3585,11 @@ "description": "Screen reader text (aria-label) for unlock account button in overlay" }, "totpCodeAria": { - "message": "Time-based One-Time Password Verification Code", + "message": "Kode Verifikasi Kata Sandi Sekali-Waktu Berbasis Waktu", "description": "Aria label for the totp code displayed in the inline menu for autofill" }, "totpSecondsSpanAria": { - "message": "Time remaining before current TOTP expires", + "message": "Waktu tersisa sebelum TOTP sekarang kadaluwarsa", "description": "Aria label for the totp seconds displayed in the inline menu for autofill" }, "fillCredentialsFor": { @@ -4339,10 +4336,10 @@ "message": "Pengenalan" }, "contactInfo": { - "message": "Contact info" + "message": "Info kontak" }, "downloadAttachment": { - "message": "Download - $ITEMNAME$", + "message": "Unduh - $ITEMNAME$", "placeholders": { "itemname": { "content": "$1", @@ -4351,23 +4348,23 @@ } }, "cardNumberEndsWith": { - "message": "card number ends with", + "message": "nomor kartu berakhiran", "description": "Used within the inline menu to provide an aria description when users are attempting to fill a card cipher." }, "loginCredentials": { - "message": "Login credentials" + "message": "Kredensial login" }, "authenticatorKey": { - "message": "Authenticator key" + "message": "Kunci Otentikator" }, "autofillOptions": { - "message": "Autofill options" + "message": "Pilihan isi otomatis" }, "websiteUri": { - "message": "Website (URI)" + "message": "Situs web (URI)" }, "websiteUriCount": { - "message": "Website (URI) $COUNT$", + "message": "$COUNT$ Situs web (URI)", "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": { @@ -4377,16 +4374,16 @@ } }, "websiteAdded": { - "message": "Website added" + "message": "Situs web ditambahkan" }, "addWebsite": { - "message": "Add website" + "message": "Tambah situs web" }, "deleteWebsite": { - "message": "Delete website" + "message": "Hapus situs web" }, "defaultLabel": { - "message": "Default ($VALUE$)", + "message": "Bawaan ($VALUE$)", "description": "A label that indicates the default value for a field with the current default value in parentheses.", "placeholders": { "value": { @@ -4396,7 +4393,7 @@ } }, "showMatchDetection": { - "message": "Show match detection $WEBSITE$", + "message": "Tampilkan deteksi kecocokan $WEBSITE$", "placeholders": { "website": { "content": "$1", @@ -4405,7 +4402,7 @@ } }, "hideMatchDetection": { - "message": "Hide match detection $WEBSITE$", + "message": "Sembunyikan deteksi kecocokan $WEBSITE$", "placeholders": { "website": { "content": "$1", @@ -4414,19 +4411,19 @@ } }, "autoFillOnPageLoad": { - "message": "Autofill on page load?" + "message": "Isi otomatis ketika halaman dimuat?" }, "cardExpiredTitle": { - "message": "Expired card" + "message": "Kartu kadaluwarsa" }, "cardExpiredMessage": { - "message": "If you've renewed it, update the card's information" + "message": "Jika Anda telah memperpanjangnya, perbarui informasi kartu" }, "cardDetails": { - "message": "Card details" + "message": "Rincian kartu" }, "cardBrandDetails": { - "message": "$BRAND$ details", + "message": "Rincian $BRAND$", "placeholders": { "brand": { "content": "$1", @@ -4435,43 +4432,43 @@ } }, "enableAnimations": { - "message": "Enable animations" + "message": "Nyalakan animasi" }, "showAnimations": { - "message": "Show animations" + "message": "Tampilkan animasi" }, "addAccount": { - "message": "Add account" + "message": "Tambah akun" }, "loading": { - "message": "Loading" + "message": "Memuat" }, "data": { "message": "Data" }, "passkeys": { - "message": "Passkeys", + "message": "Kunci sandi", "description": "A section header for a list of passkeys." }, "passwords": { - "message": "Passwords", + "message": "Kata Sandi", "description": "A section header for a list of passwords." }, "logInWithPasskeyAriaLabel": { - "message": "Log in with passkey", + "message": "Masuk dengan kunci sandi", "description": "ARIA label for the inline menu button that logs in with a passkey." }, "assign": { - "message": "Assign" + "message": "Terapkan" }, "bulkCollectionAssignmentDialogDescriptionSingular": { - "message": "Only organization members with access to these collections will be able to see the item." + "message": "Hanya anggota organisasi dengan akses ke koleksi berikut yang dapat melihat isinya." }, "bulkCollectionAssignmentDialogDescriptionPlural": { - "message": "Only organization members with access to these collections will be able to see the items." + "message": "Hanya anggota organisasi dengan akses ke koleksi berikut yang dapat melihat isinya." }, "bulkCollectionAssignmentWarning": { - "message": "You have selected $TOTAL_COUNT$ items. You cannot update $READONLY_COUNT$ of the items because you do not have edit permissions.", + "message": "Anda telah memilih $TOTAL_COUNT$ benda. Anda tidak dapat memperbarui $READONLY_COUNT$ dari benda karena Anda tidak memiliki izin untuk menyunting.", "placeholders": { "total_count": { "content": "$1", @@ -4483,37 +4480,37 @@ } }, "addField": { - "message": "Add field" + "message": "Tambahkan bidang" }, "add": { - "message": "Add" + "message": "Tambah" }, "fieldType": { - "message": "Field type" + "message": "Jenis bidang" }, "fieldLabel": { - "message": "Field label" + "message": "Label bidang" }, "textHelpText": { - "message": "Use text fields for data like security questions" + "message": "Gunakan bidang teks untuk data seperti pertanyaan keamanan" }, "hiddenHelpText": { - "message": "Use hidden fields for sensitive data like a password" + "message": "Gunakan bidang tersembunyi untuk data sensitif seperti kata sandi" }, "checkBoxHelpText": { - "message": "Use checkboxes if you'd like to autofill a form's checkbox, like a remember email" + "message": "Gunakan kotak centang jika Anda ingin mengisi sebuah kotak centang di formullir, seperti mengingat surel" }, "linkedHelpText": { - "message": "Use a linked field when you are experiencing autofill issues for a specific website." + "message": "Gunakan bidang tertaut ketika Anda mengalami masalah pengisian otomatis untuk situs web tertentu." }, "linkedLabelHelpText": { - "message": "Enter the the field's html id, name, aria-label, or placeholder." + "message": "Masukkan id, name, aria-label, atau placeholder html dari bidang." }, "editField": { - "message": "Edit field" + "message": "Sunting bidang" }, "editFieldLabel": { - "message": "Edit $LABEL$", + "message": "Sunting $LABEL$", "placeholders": { "label": { "content": "$1", @@ -4522,7 +4519,7 @@ } }, "deleteCustomField": { - "message": "Delete $LABEL$", + "message": "Hapus $LABEL$", "placeholders": { "label": { "content": "$1", @@ -4531,7 +4528,7 @@ } }, "fieldAdded": { - "message": "$LABEL$ added", + "message": "$LABEL$ ditambahkan", "placeholders": { "label": { "content": "$1", @@ -4540,7 +4537,7 @@ } }, "reorderToggleButton": { - "message": "Reorder $LABEL$. Use arrow key to move item up or down.", + "message": "Urutkan $LABEL$. Gunakan tombol panah untuk memindahkan benda ke atas atau ke bawah.", "placeholders": { "label": { "content": "$1", @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4768,155 +4768,191 @@ "description": "Represents the space key in screen reader content as a readable word" }, "tildeCharacterDescriptor": { - "message": "Tilde", + "message": "Tanda gelombang", "description": "Represents the ~ key in screen reader content as a readable word" }, "backtickCharacterDescriptor": { - "message": "Backtick", + "message": "Tanda petik terbalik", "description": "Represents the ` key in screen reader content as a readable word" }, "exclamationCharacterDescriptor": { - "message": "Exclamation mark", + "message": "Tanda seru", "description": "Represents the ! key in screen reader content as a readable word" }, "atSignCharacterDescriptor": { - "message": "At sign", + "message": "Tanda pada", "description": "Represents the @ key in screen reader content as a readable word" }, "hashSignCharacterDescriptor": { - "message": "Hash sign", + "message": "Tanda pagar", "description": "Represents the # key in screen reader content as a readable word" }, "dollarSignCharacterDescriptor": { - "message": "Dollar sign", + "message": "Tanda dolar", "description": "Represents the $ key in screen reader content as a readable word" }, "percentSignCharacterDescriptor": { - "message": "Percent sign", + "message": "Tanda persen", "description": "Represents the % key in screen reader content as a readable word" }, "caretCharacterDescriptor": { - "message": "Caret", + "message": "Tanda sisipan", "description": "Represents the ^ key in screen reader content as a readable word" }, "ampersandCharacterDescriptor": { - "message": "Ampersand", + "message": "Tanda dan", "description": "Represents the & key in screen reader content as a readable word" }, "asteriskCharacterDescriptor": { - "message": "Asterisk", + "message": "Tanda bintang", "description": "Represents the * key in screen reader content as a readable word" }, "parenLeftCharacterDescriptor": { - "message": "Left parenthesis", + "message": "Tanda kurung kiri", "description": "Represents the ( key in screen reader content as a readable word" }, "parenRightCharacterDescriptor": { - "message": "Right parenthesis", + "message": "Tanda kurung kanan", "description": "Represents the ) key in screen reader content as a readable word" }, "hyphenCharacterDescriptor": { - "message": "Underscore", + "message": "Garis bawah", "description": "Represents the _ key in screen reader content as a readable word" }, "underscoreCharacterDescriptor": { - "message": "Hyphen", + "message": "Tanda penghubung", "description": "Represents the - key in screen reader content as a readable word" }, "plusCharacterDescriptor": { - "message": "Plus", + "message": "Tanda tambah", "description": "Represents the + key in screen reader content as a readable word" }, "equalsCharacterDescriptor": { - "message": "Equals", + "message": "Tanda sama dengan", "description": "Represents the = key in screen reader content as a readable word" }, "braceLeftCharacterDescriptor": { - "message": "Left brace", + "message": "Kurung kurawal kiri", "description": "Represents the { key in screen reader content as a readable word" }, "braceRightCharacterDescriptor": { - "message": "Right brace", + "message": "Kurung kurawal kanan", "description": "Represents the } key in screen reader content as a readable word" }, "bracketLeftCharacterDescriptor": { - "message": "Left bracket", + "message": "Tanda kurung siku kiri", "description": "Represents the [ key in screen reader content as a readable word" }, "bracketRightCharacterDescriptor": { - "message": "Right bracket", + "message": "Tanda kurung siku kanan", "description": "Represents the ] key in screen reader content as a readable word" }, "pipeCharacterDescriptor": { - "message": "Pipe", + "message": "Garis tegak lurus", "description": "Represents the | key in screen reader content as a readable word" }, "backSlashCharacterDescriptor": { - "message": "Back slash", + "message": "Garis miring terbalik", "description": "Represents the back slash key in screen reader content as a readable word" }, "colonCharacterDescriptor": { - "message": "Colon", + "message": "Tanda titik dua", "description": "Represents the : key in screen reader content as a readable word" }, "semicolonCharacterDescriptor": { - "message": "Semicolon", + "message": "Tanda titik koma", "description": "Represents the ; key in screen reader content as a readable word" }, "doubleQuoteCharacterDescriptor": { - "message": "Double quote", + "message": "Tanda petik ganda", "description": "Represents the double quote key in screen reader content as a readable word" }, "singleQuoteCharacterDescriptor": { - "message": "Single quote", + "message": "Tanda petik tunggal", "description": "Represents the ' key in screen reader content as a readable word" }, "lessThanCharacterDescriptor": { - "message": "Less than", + "message": "Tanda kurang dari", "description": "Represents the < key in screen reader content as a readable word" }, "greaterThanCharacterDescriptor": { - "message": "Greater than", + "message": "Tanda lebih besar dari", "description": "Represents the > key in screen reader content as a readable word" }, "commaCharacterDescriptor": { - "message": "Comma", + "message": "Tanda koma", "description": "Represents the , key in screen reader content as a readable word" }, "periodCharacterDescriptor": { - "message": "Period", + "message": "Tanda titik", "description": "Represents the . key in screen reader content as a readable word" }, "questionCharacterDescriptor": { - "message": "Question mark", + "message": "Tanda tanya", "description": "Represents the ? key in screen reader content as a readable word" }, "forwardSlashCharacterDescriptor": { - "message": "Forward slash", + "message": "Tanda garis miring ke depan", "description": "Represents the / key in screen reader content as a readable word" }, "lowercaseAriaLabel": { - "message": "Lowercase" + "message": "Huruf kecil" }, "uppercaseAriaLabel": { - "message": "Uppercase" + "message": "Huruf kapital" }, "generatedPassword": { - "message": "Generated password" + "message": "Kata sandi yang dihasilkan" }, "compactMode": { - "message": "Compact mode" + "message": "Mode ringkas" }, "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { - "message": "Extension width" + "message": "Lebar ekstensi" }, "wide": { - "message": "Wide" + "message": "Lebar" }, "extraWide": { - "message": "Extra wide" + "message": "Ekstra lebar" } } diff --git a/apps/browser/src/_locales/it/messages.json b/apps/browser/src/_locales/it/messages.json index 1261106cf32..5b563d69f75 100644 --- a/apps/browser/src/_locales/it/messages.json +++ b/apps/browser/src/_locales/it/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Valuta l'estensione" }, - "rateExtensionDesc": { - "message": "Aiutaci lasciando una buona recensione!" - }, "browserNotSupportClipboard": { "message": "Il tuo browser non supporta copiare dagli appunti. Copialo manualmente." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Mostra il numero di suggerimenti di riempimento automatico sull'icona dell'estensione" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "Predefinito del sistema" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/ja/messages.json b/apps/browser/src/_locales/ja/messages.json index f0f80b745af..f5998a02f87 100644 --- a/apps/browser/src/_locales/ja/messages.json +++ b/apps/browser/src/_locales/ja/messages.json @@ -20,16 +20,16 @@ "message": "アカウントの作成" }, "newToBitwarden": { - "message": "New to Bitwarden?" + "message": "Bitwarden は初めてですか?" }, "logInWithPasskey": { - "message": "Log in with passkey" + "message": "パスキーでログイン" }, "useSingleSignOn": { - "message": "Use single sign-on" + "message": "シングルサインオンを使用する" }, "welcomeBack": { - "message": "Welcome back" + "message": "ようこそ" }, "setAStrongPassword": { "message": "強力なパスワードを設定する" @@ -84,7 +84,7 @@ "message": "組織に参加" }, "joinOrganizationName": { - "message": "Join $ORGANIZATIONNAME$", + "message": "$ORGANIZATIONNAME$ に参加", "placeholders": { "organizationName": { "content": "$1", @@ -120,7 +120,7 @@ "message": "パスワードをコピー" }, "copyPassphrase": { - "message": "Copy passphrase" + "message": "パスフレーズをコピー" }, "copyNote": { "message": "メモをコピー" @@ -153,13 +153,13 @@ "message": "免許証番号をコピー" }, "copyPrivateKey": { - "message": "Copy private key" + "message": "秘密鍵をコピー" }, "copyPublicKey": { - "message": "Copy public key" + "message": "公開鍵をコピー" }, "copyFingerprint": { - "message": "Copy fingerprint" + "message": "フィンガープリントをコピー" }, "copyCustomField": { "message": "$FIELD$ をコピー", @@ -177,7 +177,7 @@ "message": "メモをコピー" }, "fill": { - "message": "Fill", + "message": "入力", "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." }, "autoFill": { @@ -193,10 +193,10 @@ "message": "自動入力 ID" }, "fillVerificationCode": { - "message": "Fill verification code" + "message": "認証コードを入力" }, "fillVerificationCodeAria": { - "message": "Fill Verification Code", + "message": "認証コードを入力", "description": "Aria label for the heading displayed the inline menu for totp code autofill" }, "generatePasswordCopied": { @@ -443,7 +443,7 @@ "message": "パスワードの自動生成" }, "generatePassphrase": { - "message": "Generate passphrase" + "message": "パスフレーズを生成" }, "regeneratePassword": { "message": "パスワードの再生成" @@ -607,7 +607,7 @@ "message": "ウェブサイトを開く" }, "launchWebsiteName": { - "message": "Launch website $ITEMNAME$", + "message": "ウェブサイト $ITEMNAME$ を開く", "placeholders": { "itemname": { "content": "$1", @@ -648,9 +648,6 @@ "rateExtension": { "message": "拡張機能の評価" }, - "rateExtensionDesc": { - "message": "良いレビューで私たちを助けてください!" - }, "browserNotSupportClipboard": { "message": "お使いのブラウザはクリップボードへのコピーに対応していません。手動でコピーしてください" }, @@ -862,7 +859,7 @@ "message": "ログイン" }, "logInToBitwarden": { - "message": "Log in to Bitwarden" + "message": "Bitwarden にログイン" }, "restartRegistration": { "message": "登録を再度始める" @@ -1133,7 +1130,7 @@ "description": "WARNING (should stay in capitalized letters if the language permits)" }, "warningCapitalized": { - "message": "Warning", + "message": "注意", "description": "Warning (should maintain locale-relevant capitalization)" }, "confirmVaultExport": { @@ -1327,10 +1324,10 @@ "message": "認証アプリに表示された6桁の認証コードを入力してください。" }, "authenticationTimeout": { - "message": "Authentication timeout" + "message": "認証のタイムアウト" }, "authenticationSessionTimedOut": { - "message": "The authentication session timed out. Please restart the login process." + "message": "認証セッションの有効期限が切れました。ログインプロセスを再開してください。" }, "enterVerificationCodeEmail": { "message": "$EMAIL$に送信された6桁の認証コードを入力してください。", @@ -1450,7 +1447,7 @@ "message": "サーバー URL" }, "selfHostBaseUrl": { - "message": "Self-host server URL", + "message": "自己ホスト型サーバー URL", "description": "Label for field requesting a self-hosted integration service URL" }, "apiUrl": { @@ -1778,7 +1775,7 @@ "message": "ID" }, "typeSshKey": { - "message": "SSH key" + "message": "SSH 鍵" }, "newItemHeader": { "message": "$TYPE$ を新規作成", @@ -1811,13 +1808,13 @@ "message": "パスワードの履歴" }, "generatorHistory": { - "message": "Generator history" + "message": "生成履歴" }, "clearGeneratorHistoryTitle": { - "message": "Clear generator history" + "message": "生成履歴を消去" }, "cleargGeneratorHistoryDescription": { - "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + "message": "続行すると、すべてのエントリは生成履歴から完全に削除されます。続行してもよろしいですか?" }, "back": { "message": "戻る" @@ -1856,7 +1853,7 @@ "message": "セキュアメモ" }, "sshKeys": { - "message": "SSH Keys" + "message": "SSH 鍵" }, "clear": { "message": "消去する", @@ -1939,10 +1936,10 @@ "message": "履歴を消去" }, "nothingToShow": { - "message": "Nothing to show" + "message": "表示するものがありません" }, "nothingGeneratedRecently": { - "message": "You haven't generated anything recently" + "message": "最近生成したものはありません" }, "remove": { "message": "削除" @@ -2531,7 +2528,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendPasswordDescV3": { - "message": "Add an optional password for recipients to access this Send.", + "message": "受信者がこの Send にアクセスするための任意のパスワードを追加します。", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2894,10 +2891,10 @@ "message": "ユーザー名を生成" }, "generateEmail": { - "message": "Generate email" + "message": "メールアドレスを生成" }, "spinboxBoundariesHint": { - "message": "Value must be between $MIN$ and $MAX$.", + "message": "値は $MIN$ から $MAX$ の間でなければなりません。", "description": "Explains spin box minimum and maximum values to the user", "placeholders": { "min": { @@ -2911,7 +2908,7 @@ } }, "passwordLengthRecommendationHint": { - "message": " Use $RECOMMENDED$ characters or more to generate a strong password.", + "message": " 強力なパスワードを生成するには、 $RECOMMENDED$ 文字以上を使用してください。", "description": "Appended to `spinboxBoundariesHint` to recommend a length to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -2921,7 +2918,7 @@ } }, "passphraseNumWordsRecommendationHint": { - "message": " Use $RECOMMENDED$ words or more to generate a strong passphrase.", + "message": " 強力なパスフレーズを生成するには、 $RECOMMENDED$ 単語以上を使用してください。", "description": "Appended to `spinboxBoundariesHint` to recommend a number of words to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -2971,11 +2968,11 @@ "message": "外部転送サービスを使用してメールエイリアスを生成します。" }, "forwarderDomainName": { - "message": "Email domain", + "message": "メールアドレスのドメイン", "description": "Labels the domain name email forwarder service option" }, "forwarderDomainNameHint": { - "message": "Choose a domain that is supported by the selected service", + "message": "選択したサービスでサポートされているドメインを選択してください", "description": "Guidance provided for email forwarding services that support multiple email domains." }, "forwarderError": { @@ -3178,25 +3175,25 @@ "message": "通知を再送信する" }, "viewAllLogInOptions": { - "message": "View all log in options" + "message": "すべてのログインオプションを表示" }, "viewAllLoginOptionsV1": { - "message": "View all log in options" + "message": "すべてのログインオプションを表示" }, "notificationSentDevice": { "message": "デバイスに通知を送信しました。" }, "aNotificationWasSentToYourDevice": { - "message": "A notification was sent to your device" + "message": "お使いのデバイスに通知が送信されました" }, "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { - "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + "message": "アカウントがロック解除されていることと、フィンガープリントフレーズが他の端末で一致していることを確認してください" }, "youWillBeNotifiedOnceTheRequestIsApproved": { - "message": "You will be notified once the request is approved" + "message": "リクエストが承認されると通知されます" }, "needAnotherOptionV1": { - "message": "Need another option?" + "message": "別の選択肢が必要ですか?" }, "loginInitiated": { "message": "ログイン開始" @@ -3292,16 +3289,16 @@ "message": "新しいウィンドウで開く" }, "rememberThisDeviceToMakeFutureLoginsSeamless": { - "message": "Remember this device to make future logins seamless" + "message": "このデバイスを記憶して今後のログインをシームレスにする" }, "deviceApprovalRequired": { "message": "デバイスの承認が必要です。以下から承認オプションを選択してください:" }, "deviceApprovalRequiredV2": { - "message": "Device approval required" + "message": "デバイスの承認が必要です" }, "selectAnApprovalOptionBelow": { - "message": "Select an approval option below" + "message": "以下の承認オプションを選択してください" }, "rememberThisDevice": { "message": "このデバイスを記憶する" @@ -3377,7 +3374,7 @@ "message": "ユーザーのメールアドレスがありません" }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "アクティブなユーザーメールアドレスが見つかりません。ログアウトします。" }, "deviceTrusted": { "message": "信頼されたデバイス" @@ -3588,11 +3585,11 @@ "description": "Screen reader text (aria-label) for unlock account button in overlay" }, "totpCodeAria": { - "message": "Time-based One-Time Password Verification Code", + "message": "時間ベースのワンタイムパスワード認証コード", "description": "Aria label for the totp code displayed in the inline menu for autofill" }, "totpSecondsSpanAria": { - "message": "Time remaining before current TOTP expires", + "message": "現在の TOTP 有効期限が切れるまでの残り時間", "description": "Aria label for the totp seconds displayed in the inline menu for autofill" }, "fillCredentialsFor": { @@ -3824,7 +3821,7 @@ "message": "アクセス中" }, "loggedInExclamation": { - "message": "Logged in!" + "message": "ログインしました!" }, "passkeyNotCopied": { "message": "パスキーはコピーされません" @@ -4318,13 +4315,13 @@ "message": "フィルター" }, "filterVault": { - "message": "Filter vault" + "message": "保管庫をフィルター" }, "filterApplied": { - "message": "One filter applied" + "message": "1 個のフィルタを適用しました" }, "filterAppliedPlural": { - "message": "$COUNT$ filters applied", + "message": "$COUNT$ 個のフィルタを適用しました", "placeholders": { "count": { "content": "$1", @@ -4656,13 +4653,13 @@ "message": "アイテムの場所" }, "fileSend": { - "message": "File Send" + "message": "ファイル Send" }, "fileSends": { "message": "ファイル Send" }, "textSend": { - "message": "Text Send" + "message": "テキスト Send" }, "textSends": { "message": "テキスト Send" @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "拡張機能アイコンにログイン自動入力の候補の数を表示する" }, + "showQuickCopyActions": { + "message": "保管庫にクイックコピー操作を表示する" + }, "systemDefault": { "message": "システムのデフォルト" }, @@ -4686,16 +4686,16 @@ "message": "エンタープライズポリシー要件がこの設定に適用されました" }, "sshPrivateKey": { - "message": "Private key" + "message": "秘密鍵" }, "sshPublicKey": { - "message": "Public key" + "message": "公開鍵" }, "sshFingerprint": { - "message": "Fingerprint" + "message": "フィンガープリント" }, "sshKeyAlgorithm": { - "message": "Key type" + "message": "鍵の種類" }, "sshKeyAlgorithmED25519": { "message": "ED25519" @@ -4752,171 +4752,207 @@ "message": "認証中" }, "fillGeneratedPassword": { - "message": "Fill generated password", + "message": "生成したパスワードを入力", "description": "Heading for the password generator within the inline menu" }, "passwordRegenerated": { - "message": "Password regenerated", + "message": "パスワードを再生成しました", "description": "Notification message for when a password has been regenerated" }, "saveLoginToBitwarden": { - "message": "Save login to Bitwarden?", + "message": "Bitwarden にログイン情報を保存しますか?", "description": "Confirmation message for saving a login to Bitwarden" }, "spaceCharacterDescriptor": { - "message": "Space", + "message": "スペース", "description": "Represents the space key in screen reader content as a readable word" }, "tildeCharacterDescriptor": { - "message": "Tilde", + "message": "チルダ", "description": "Represents the ~ key in screen reader content as a readable word" }, "backtickCharacterDescriptor": { - "message": "Backtick", + "message": "バッククォート", "description": "Represents the ` key in screen reader content as a readable word" }, "exclamationCharacterDescriptor": { - "message": "Exclamation mark", + "message": "エクスクラメーションマーク", "description": "Represents the ! key in screen reader content as a readable word" }, "atSignCharacterDescriptor": { - "message": "At sign", + "message": "アットマーク", "description": "Represents the @ key in screen reader content as a readable word" }, "hashSignCharacterDescriptor": { - "message": "Hash sign", + "message": "ハッシュ記号", "description": "Represents the # key in screen reader content as a readable word" }, "dollarSignCharacterDescriptor": { - "message": "Dollar sign", + "message": "ドル記号", "description": "Represents the $ key in screen reader content as a readable word" }, "percentSignCharacterDescriptor": { - "message": "Percent sign", + "message": "パーセント記号", "description": "Represents the % key in screen reader content as a readable word" }, "caretCharacterDescriptor": { - "message": "Caret", + "message": "キャレット", "description": "Represents the ^ key in screen reader content as a readable word" }, "ampersandCharacterDescriptor": { - "message": "Ampersand", + "message": "アンパサンド", "description": "Represents the & key in screen reader content as a readable word" }, "asteriskCharacterDescriptor": { - "message": "Asterisk", + "message": "アスタリスク", "description": "Represents the * key in screen reader content as a readable word" }, "parenLeftCharacterDescriptor": { - "message": "Left parenthesis", + "message": "左かっこ", "description": "Represents the ( key in screen reader content as a readable word" }, "parenRightCharacterDescriptor": { - "message": "Right parenthesis", + "message": "右かっこ", "description": "Represents the ) key in screen reader content as a readable word" }, "hyphenCharacterDescriptor": { - "message": "Underscore", + "message": "アンダースコア", "description": "Represents the _ key in screen reader content as a readable word" }, "underscoreCharacterDescriptor": { - "message": "Hyphen", + "message": "ハイフン", "description": "Represents the - key in screen reader content as a readable word" }, "plusCharacterDescriptor": { - "message": "Plus", + "message": "プラス", "description": "Represents the + key in screen reader content as a readable word" }, "equalsCharacterDescriptor": { - "message": "Equals", + "message": "イコール", "description": "Represents the = key in screen reader content as a readable word" }, "braceLeftCharacterDescriptor": { - "message": "Left brace", + "message": "左中かっこ", "description": "Represents the { key in screen reader content as a readable word" }, "braceRightCharacterDescriptor": { - "message": "Right brace", + "message": "右中かっこ", "description": "Represents the } key in screen reader content as a readable word" }, "bracketLeftCharacterDescriptor": { - "message": "Left bracket", + "message": "左大かっこ", "description": "Represents the [ key in screen reader content as a readable word" }, "bracketRightCharacterDescriptor": { - "message": "Right bracket", + "message": "右大かっこ", "description": "Represents the ] key in screen reader content as a readable word" }, "pipeCharacterDescriptor": { - "message": "Pipe", + "message": "パイプ", "description": "Represents the | key in screen reader content as a readable word" }, "backSlashCharacterDescriptor": { - "message": "Back slash", + "message": "バックスラッシュ", "description": "Represents the back slash key in screen reader content as a readable word" }, "colonCharacterDescriptor": { - "message": "Colon", + "message": "コロン", "description": "Represents the : key in screen reader content as a readable word" }, "semicolonCharacterDescriptor": { - "message": "Semicolon", + "message": "セミコロン", "description": "Represents the ; key in screen reader content as a readable word" }, "doubleQuoteCharacterDescriptor": { - "message": "Double quote", + "message": "ダブルクォート", "description": "Represents the double quote key in screen reader content as a readable word" }, "singleQuoteCharacterDescriptor": { - "message": "Single quote", + "message": "シングルクォート", "description": "Represents the ' key in screen reader content as a readable word" }, "lessThanCharacterDescriptor": { - "message": "Less than", + "message": "小なり", "description": "Represents the < key in screen reader content as a readable word" }, "greaterThanCharacterDescriptor": { - "message": "Greater than", + "message": "大なり", "description": "Represents the > key in screen reader content as a readable word" }, "commaCharacterDescriptor": { - "message": "Comma", + "message": "コンマ", "description": "Represents the , key in screen reader content as a readable word" }, "periodCharacterDescriptor": { - "message": "Period", + "message": "ピリオド", "description": "Represents the . key in screen reader content as a readable word" }, "questionCharacterDescriptor": { - "message": "Question mark", + "message": "クエスチョンマーク", "description": "Represents the ? key in screen reader content as a readable word" }, "forwardSlashCharacterDescriptor": { - "message": "Forward slash", + "message": "スラッシュ", "description": "Represents the / key in screen reader content as a readable word" }, "lowercaseAriaLabel": { - "message": "Lowercase" + "message": "小文字" }, "uppercaseAriaLabel": { - "message": "Uppercase" + "message": "大文字" }, "generatedPassword": { - "message": "Generated password" + "message": "生成したパスワード" }, "compactMode": { - "message": "Compact mode" + "message": "コンパクトモード" }, "beta": { - "message": "Beta" + "message": "ベータ" + }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" }, "extensionWidth": { - "message": "Extension width" + "message": "拡張機能の幅" }, "wide": { - "message": "Wide" + "message": "ワイド" }, "extraWide": { - "message": "Extra wide" + "message": "エクストラワイド" } } diff --git a/apps/browser/src/_locales/ka/messages.json b/apps/browser/src/_locales/ka/messages.json index 33ccbdbb1a1..8cef6ece782 100644 --- a/apps/browser/src/_locales/ka/messages.json +++ b/apps/browser/src/_locales/ka/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Rate the extension" }, - "rateExtensionDesc": { - "message": "Please consider helping us out with a good review!" - }, "browserNotSupportClipboard": { "message": "Your web browser does not support easy clipboard copying. Copy it manually instead." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "სისტემურად ნაგულისხმევი" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/km/messages.json b/apps/browser/src/_locales/km/messages.json index 779ff917578..8f7673803cb 100644 --- a/apps/browser/src/_locales/km/messages.json +++ b/apps/browser/src/_locales/km/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Rate the extension" }, - "rateExtensionDesc": { - "message": "Please consider helping us out with a good review!" - }, "browserNotSupportClipboard": { "message": "Your web browser does not support easy clipboard copying. Copy it manually instead." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/kn/messages.json b/apps/browser/src/_locales/kn/messages.json index 8b21fc61e56..7507e254447 100644 --- a/apps/browser/src/_locales/kn/messages.json +++ b/apps/browser/src/_locales/kn/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "ವಿಸ್ತರಣೆಯನ್ನು ರೇಟ್ ಮಾಡಿ" }, - "rateExtensionDesc": { - "message": "ಉತ್ತಮ ವಿಮರ್ಶೆಯೊಂದಿಗೆ ನಮಗೆ ಸಹಾಯ ಮಾಡಲು ದಯವಿಟ್ಟು ಪರಿಗಣಿಸಿ!" - }, "browserNotSupportClipboard": { "message": "ನಿಮ್ಮ ವೆಬ್ ಬ್ರೌಸರ್ ಸುಲಭವಾದ ಕ್ಲಿಪ್‌ಬೋರ್ಡ್ ನಕಲು ಮಾಡುವುದನ್ನು ಬೆಂಬಲಿಸುವುದಿಲ್ಲ. ಬದಲಿಗೆ ಅದನ್ನು ಹಸ್ತಚಾಲಿತವಾಗಿ ನಕಲಿಸಿ." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/ko/messages.json b/apps/browser/src/_locales/ko/messages.json index 91207cd0e34..6ab1a467475 100644 --- a/apps/browser/src/_locales/ko/messages.json +++ b/apps/browser/src/_locales/ko/messages.json @@ -193,10 +193,10 @@ "message": "신원 자동 완성" }, "fillVerificationCode": { - "message": "Fill verification code" + "message": "인증 코드를 입력하세요" }, "fillVerificationCodeAria": { - "message": "Fill Verification Code", + "message": "인증 코드를 입력하세요", "description": "Aria label for the heading displayed the inline menu for totp code autofill" }, "generatePasswordCopied": { @@ -648,9 +648,6 @@ "rateExtension": { "message": "확장 프로그램 평가" }, - "rateExtensionDesc": { - "message": "좋은 리뷰를 남겨 저희를 도와주세요!" - }, "browserNotSupportClipboard": { "message": "사용하고 있는 웹 브라우저가 쉬운 클립보드 복사를 지원하지 않습니다. 직접 복사하세요." }, @@ -2069,11 +2066,11 @@ "message": "보안 비밀번호가 생성되었습니다! 웹사이트에서 비밀번호를 업데이트하는 것도 잊지 마세요." }, "useGeneratorHelpTextPartOne": { - "message": "생성기를 사용하세요", + "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": "강력한 고유 비밀번호를 만들기 위해서는", + "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'" }, "vaultTimeoutAction": { @@ -3181,7 +3178,7 @@ "message": "모든 로그인 방식 보기" }, "viewAllLoginOptionsV1": { - "message": "View all log in options" + "message": "모든 로그인 옵션 보기" }, "notificationSentDevice": { "message": "기기에 알림이 전송되었습니다." @@ -3588,11 +3585,11 @@ "description": "Screen reader text (aria-label) for unlock account button in overlay" }, "totpCodeAria": { - "message": "Time-based One-Time Password Verification Code", + "message": "TOTP 인증 코드", "description": "Aria label for the totp code displayed in the inline menu for autofill" }, "totpSecondsSpanAria": { - "message": "Time remaining before current TOTP expires", + "message": "TOTP 만료까지 남은 시간", "description": "Aria label for the totp seconds displayed in the inline menu for autofill" }, "fillCredentialsFor": { @@ -3669,7 +3666,7 @@ "message": "데이터 가져오기 성공" }, "importSuccessNumberOfItems": { - "message": "A total of $AMOUNT$ items were imported.", + "message": "총 $AMOUNT$개의 항목을 가져왔습니다.", "placeholders": { "amount": { "content": "$1", @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "확장 아이콘에 로그인 자동 완성 제안 수 표시" }, + "showQuickCopyActions": { + "message": "보관함에서 빠른 복사 기능 표시" + }, "systemDefault": { "message": "시스템 기본 설정" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "베타" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "확장 폭" }, diff --git a/apps/browser/src/_locales/lt/messages.json b/apps/browser/src/_locales/lt/messages.json index 7fd47194b7c..f55fb8b3e25 100644 --- a/apps/browser/src/_locales/lt/messages.json +++ b/apps/browser/src/_locales/lt/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Įvertinkite šį plėtinį" }, - "rateExtensionDesc": { - "message": "Apsvarstykite galimybę mums padėti palikdami gerą atsiliepimą!" - }, "browserNotSupportClipboard": { "message": "Jūsų žiniatinklio naršyklė nepalaiko automatinio kopijavimo. Vietoj to nukopijuokite rankiniu būdu." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/lv/messages.json b/apps/browser/src/_locales/lv/messages.json index 3fa9085d005..7d0f789280b 100644 --- a/apps/browser/src/_locales/lv/messages.json +++ b/apps/browser/src/_locales/lv/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Novērtēt paplašinājumu" }, - "rateExtensionDesc": { - "message": "Lūgums apsvērt palīdzēt mums ar labu atsauksmi." - }, "browserNotSupportClipboard": { "message": "Pārlūks neatbalsta vienkāršo ievietošanu starpliktuvē. Tā vietā tas jāievieto starpliktuvē pašrocīgi." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Paplašinājuma ikonā rādīt pieteikšanās automātiskās aizpildes ieteikumu skaitu" }, + "showQuickCopyActions": { + "message": "Glabātavā rādīt ātrās kopēšanas darbības" + }, "systemDefault": { "message": "Sistēmas noklusējums" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Paplašinājuma platums" }, diff --git a/apps/browser/src/_locales/ml/messages.json b/apps/browser/src/_locales/ml/messages.json index 80e8cd90052..e3c377b2e8c 100644 --- a/apps/browser/src/_locales/ml/messages.json +++ b/apps/browser/src/_locales/ml/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "എക്സ്റ്റൻഷൻ റേറ്റ് ചെയ്യുക " }, - "rateExtensionDesc": { - "message": "ഒരു നല്ല അവലോകനത്തിന് ഞങ്ങളെ സഹായിക്കുന്നത് പരിഗണിക്കുക!" - }, "browserNotSupportClipboard": { "message": "നിങ്ങളുടെ ബ്രൌസർ എളുപ്പമുള്ള ക്ലിപ്പ്ബോർഡ് പകർത്തൽ പിന്തുണയ്ക്കത്തില്ല. പകരം അത് സ്വമേധയാ പകർക്കുക ." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/mr/messages.json b/apps/browser/src/_locales/mr/messages.json index be0b2627b8a..02c66ea8f69 100644 --- a/apps/browser/src/_locales/mr/messages.json +++ b/apps/browser/src/_locales/mr/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "विस्तारकाचे मूल्यांकन करा" }, - "rateExtensionDesc": { - "message": "चांगला अभिप्राय देऊन आम्हाला मदत करा!" - }, "browserNotSupportClipboard": { "message": "Your web browser does not support easy clipboard copying. Copy it manually instead." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/my/messages.json b/apps/browser/src/_locales/my/messages.json index 779ff917578..8f7673803cb 100644 --- a/apps/browser/src/_locales/my/messages.json +++ b/apps/browser/src/_locales/my/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Rate the extension" }, - "rateExtensionDesc": { - "message": "Please consider helping us out with a good review!" - }, "browserNotSupportClipboard": { "message": "Your web browser does not support easy clipboard copying. Copy it manually instead." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/nb/messages.json b/apps/browser/src/_locales/nb/messages.json index ef36839dd53..604597091ef 100644 --- a/apps/browser/src/_locales/nb/messages.json +++ b/apps/browser/src/_locales/nb/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Gi denne utvidelsen en vurdering" }, - "rateExtensionDesc": { - "message": "Tenk gjerne på om du vil skrive en anmeldelse om oss!" - }, "browserNotSupportClipboard": { "message": "Nettleseren din støtter ikke kopiering til utklippstavlen på noe enkelt vis. Prøv å kopiere det manuelt i stedet." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "Systemforvalg" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/ne/messages.json b/apps/browser/src/_locales/ne/messages.json index 779ff917578..8f7673803cb 100644 --- a/apps/browser/src/_locales/ne/messages.json +++ b/apps/browser/src/_locales/ne/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Rate the extension" }, - "rateExtensionDesc": { - "message": "Please consider helping us out with a good review!" - }, "browserNotSupportClipboard": { "message": "Your web browser does not support easy clipboard copying. Copy it manually instead." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/nl/messages.json b/apps/browser/src/_locales/nl/messages.json index 23fbcd3d265..170c682aa1c 100644 --- a/apps/browser/src/_locales/nl/messages.json +++ b/apps/browser/src/_locales/nl/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Deze extensie beoordelen" }, - "rateExtensionDesc": { - "message": "Je kunt ons helpen door een goede recensie achter te laten!" - }, "browserNotSupportClipboard": { "message": "Je webbrowser ondersteunt kopiëren naar plakbord niet. Kopieer handmatig." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Aantal login-autofill-suggesties op het extensie-pictogram weergeven" }, + "showQuickCopyActions": { + "message": "Toon snelle kopieeracties in de kluis" + }, "systemDefault": { "message": "Systeemstandaard" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Belangrijke mededeling" + }, + "setupTwoStepLogin": { + "message": "Tweestapsaanmelding instellen" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Vanaf februari 2025 stuurt Bitwarden een code naar het e-mailadres van je account om inloggen op nieuwe apparaten te verifiëren." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Je kunt tweestapsaanmelding instellen als een alternatieve manier om je account te beschermen of je e-mailadres te veranderen naar een waar je toegang toe hebt." + }, + "remindMeLater": { + "message": "Herinner me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Heb je betrouwbare toegang tot je e-mail, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Nee, dat heb ik niet" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Ja, ik heb betrouwbare toegang tot mijn e-mail" + }, + "turnOnTwoStepLogin": { + "message": "Tweestapsaanmelding inschakelen" + }, + "changeAcctEmail": { + "message": "E-mailadres van het account veranderen" + }, "extensionWidth": { "message": "Extensiebreedte" }, diff --git a/apps/browser/src/_locales/nn/messages.json b/apps/browser/src/_locales/nn/messages.json index 779ff917578..8f7673803cb 100644 --- a/apps/browser/src/_locales/nn/messages.json +++ b/apps/browser/src/_locales/nn/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Rate the extension" }, - "rateExtensionDesc": { - "message": "Please consider helping us out with a good review!" - }, "browserNotSupportClipboard": { "message": "Your web browser does not support easy clipboard copying. Copy it manually instead." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/or/messages.json b/apps/browser/src/_locales/or/messages.json index 779ff917578..8f7673803cb 100644 --- a/apps/browser/src/_locales/or/messages.json +++ b/apps/browser/src/_locales/or/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Rate the extension" }, - "rateExtensionDesc": { - "message": "Please consider helping us out with a good review!" - }, "browserNotSupportClipboard": { "message": "Your web browser does not support easy clipboard copying. Copy it manually instead." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/pl/messages.json b/apps/browser/src/_locales/pl/messages.json index 0db4fc4dd8b..7025862cb72 100644 --- a/apps/browser/src/_locales/pl/messages.json +++ b/apps/browser/src/_locales/pl/messages.json @@ -23,19 +23,19 @@ "message": "New to Bitwarden?" }, "logInWithPasskey": { - "message": "Log in with passkey" + "message": "Zaloguj się używając passkey" }, "useSingleSignOn": { - "message": "Use single sign-on" + "message": "Użyj jednokrotnego logowania" }, "welcomeBack": { - "message": "Welcome back" + "message": "Witaj ponownie" }, "setAStrongPassword": { "message": "Ustaw silne hasło" }, "finishCreatingYourAccountBySettingAPassword": { - "message": "Ukończ tworzenie konta poprzez utworzenie hasła" + "message": "Ukończ tworzenie konta poprzez ustawienie hasła" }, "enterpriseSingleSignOn": { "message": "Logowanie jednokrotne" @@ -120,7 +120,7 @@ "message": "Kopiuj hasło" }, "copyPassphrase": { - "message": "Copy passphrase" + "message": "Kopiuj frazę bezpieczeństwa" }, "copyNote": { "message": "Kopiuj notatkę" @@ -153,13 +153,13 @@ "message": "Kopiuj numer licencji" }, "copyPrivateKey": { - "message": "Copy private key" + "message": "Skopiuj klucz prywatny" }, "copyPublicKey": { - "message": "Copy public key" + "message": "Skopiuj klucz publiczny" }, "copyFingerprint": { - "message": "Copy fingerprint" + "message": "Skopiuj odcisk palca" }, "copyCustomField": { "message": "Kopiuj $FIELD$", @@ -193,10 +193,10 @@ "message": "Autouzupełnianie tożsamości" }, "fillVerificationCode": { - "message": "Fill verification code" + "message": "Wypełnij kod weryfikacyjny" }, "fillVerificationCodeAria": { - "message": "Fill Verification Code", + "message": "Wypełnij kod weryfikacyjny", "description": "Aria label for the heading displayed the inline menu for totp code autofill" }, "generatePasswordCopied": { @@ -443,7 +443,7 @@ "message": "Wygeneruj hasło" }, "generatePassphrase": { - "message": "Generate passphrase" + "message": "Wygenruj frazę zabezpieczającą" }, "regeneratePassword": { "message": "Wygeneruj ponownie hasło" @@ -607,7 +607,7 @@ "message": "Otwórz stronę" }, "launchWebsiteName": { - "message": "Launch website $ITEMNAME$", + "message": "Otwórz stronę internetową $ITEMNAME$", "placeholders": { "itemname": { "content": "$1", @@ -648,9 +648,6 @@ "rateExtension": { "message": "Oceń rozszerzenie" }, - "rateExtensionDesc": { - "message": "Wesprzyj nas pozytywną opinią!" - }, "browserNotSupportClipboard": { "message": "Przeglądarka nie obsługuje łatwego kopiowania schowka. Skopiuj element ręcznie." }, @@ -862,7 +859,7 @@ "message": "Zaloguj się" }, "logInToBitwarden": { - "message": "Log in to Bitwarden" + "message": "Zaloguj się do Bitwarden" }, "restartRegistration": { "message": "Zrestartuj rejestrację" @@ -1133,7 +1130,7 @@ "description": "WARNING (should stay in capitalized letters if the language permits)" }, "warningCapitalized": { - "message": "Warning", + "message": "Ostrzeżenie", "description": "Warning (should maintain locale-relevant capitalization)" }, "confirmVaultExport": { @@ -1327,7 +1324,7 @@ "message": "Wpisz 6-cyfrowy kod weryfikacyjny z aplikacji uwierzytelniającej." }, "authenticationTimeout": { - "message": "Authentication timeout" + "message": "Limit czasu uwierzytelniania" }, "authenticationSessionTimedOut": { "message": "The authentication session timed out. Please restart the login process." @@ -1450,7 +1447,7 @@ "message": "Adres URL serwera" }, "selfHostBaseUrl": { - "message": "Self-host server URL", + "message": "URL samodzielnie hostowanego serwera", "description": "Label for field requesting a self-hosted integration service URL" }, "apiUrl": { @@ -2531,7 +2528,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendPasswordDescV3": { - "message": "Add an optional password for recipients to access this Send.", + "message": "Zabezpiecz tę wiadomość hasłem, które będzie wymagane, aby uzyskać do niej dostęp.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { @@ -2616,7 +2613,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendFilePopoutDialogText": { - "message": "Pop out extension?", + "message": "Otworzyć rozszerzenie w nowym oknie?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendFilePopoutDialogDesc": { @@ -2894,7 +2891,7 @@ "message": "Wygeneruj nazwę użytkownika" }, "generateEmail": { - "message": "Generate email" + "message": "Wygeneruj e-mail" }, "spinboxBoundariesHint": { "message": "Wartość musi być pomiędzy $MIN$ a $MAX$.", @@ -2971,11 +2968,11 @@ "message": "Wygeneruj alias adresu e-mail z zewnętrznej usługi przekierowania." }, "forwarderDomainName": { - "message": "Email domain", + "message": "Domena adresu e-mail", "description": "Labels the domain name email forwarder service option" }, "forwarderDomainNameHint": { - "message": "Choose a domain that is supported by the selected service", + "message": "Wybierz domenę, która jest obsługiwana przez wybraną usługę", "description": "Guidance provided for email forwarding services that support multiple email domains." }, "forwarderError": { @@ -3178,16 +3175,16 @@ "message": "Wyślij ponownie powiadomienie" }, "viewAllLogInOptions": { - "message": "View all log in options" + "message": "Zobacz wszystkie sposoby logowania" }, "viewAllLoginOptionsV1": { - "message": "View all log in options" + "message": "Zobacz wszystkie sposoby logowania" }, "notificationSentDevice": { "message": "Powiadomienie zostało wysłane na urządzenie." }, "aNotificationWasSentToYourDevice": { - "message": "A notification was sent to your device" + "message": "Powiadomienie zostało wysłane na twoje urządzenie" }, "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" @@ -3196,7 +3193,7 @@ "message": "You will be notified once the request is approved" }, "needAnotherOptionV1": { - "message": "Need another option?" + "message": "Potrzebujesz innego sposobu?" }, "loginInitiated": { "message": "Logowanie rozpoczęte" @@ -3292,16 +3289,16 @@ "message": "Otwiera w nowym oknie" }, "rememberThisDeviceToMakeFutureLoginsSeamless": { - "message": "Remember this device to make future logins seamless" + "message": "Zapamiętaj to urządzenie, aby przyszłe logowania były bezproblemowe" }, "deviceApprovalRequired": { "message": "Wymagane zatwierdzenie urządzenia. Wybierz opcję zatwierdzenia poniżej:" }, "deviceApprovalRequiredV2": { - "message": "Device approval required" + "message": "Wymagane zatwierdzenie urządzenia" }, "selectAnApprovalOptionBelow": { - "message": "Select an approval option below" + "message": "Wybierz opcję zatwierdzenia poniżej" }, "rememberThisDevice": { "message": "Zapamiętaj to urządzenie" @@ -3377,7 +3374,7 @@ "message": "Brak adresu e-mail użytkownika" }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "Nie znaleziono aktywnego adresu e-mail. Trwa wylogowanie." }, "deviceTrusted": { "message": "Zaufano urządzeniu" @@ -3592,7 +3589,7 @@ "description": "Aria label for the totp code displayed in the inline menu for autofill" }, "totpSecondsSpanAria": { - "message": "Time remaining before current TOTP expires", + "message": "Pozostały czas do wygaśnięcia bieżącego TOTP", "description": "Aria label for the totp seconds displayed in the inline menu for autofill" }, "fillCredentialsFor": { @@ -3824,7 +3821,7 @@ "message": "Uzyskiwanie dostępu" }, "loggedInExclamation": { - "message": "Logged in!" + "message": "Zalogowano!" }, "passkeyNotCopied": { "message": "Passkey nie zostanie skopiowany" @@ -4318,13 +4315,13 @@ "message": "Filtry" }, "filterVault": { - "message": "Filter vault" + "message": "Filtruj sejf" }, "filterApplied": { - "message": "One filter applied" + "message": "Zastosowano jeden filtr" }, "filterAppliedPlural": { - "message": "$COUNT$ filters applied", + "message": "$COUNT$ filtrów zastosowanych", "placeholders": { "count": { "content": "$1", @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Pokaż liczbę sugestii autouzupełniania logowania na ikonie rozszerzenia" }, + "showQuickCopyActions": { + "message": "Pokaż akcje szybkiego kopiowania w Sejfie" + }, "systemDefault": { "message": "Domyślny systemu" }, @@ -4692,7 +4692,7 @@ "message": "Klucz publiczny" }, "sshFingerprint": { - "message": "Fingerprint" + "message": "Odcisk palca" }, "sshKeyAlgorithm": { "message": "Typ klucza" @@ -4701,13 +4701,13 @@ "message": "ED25519" }, "sshKeyAlgorithmRSA2048": { - "message": "RSA 2048-Bitowy" + "message": "RSA 2048-bitowy" }, "sshKeyAlgorithmRSA3072": { - "message": "RSA 3072-Bitowy" + "message": "RSA 3072-bitowy" }, "sshKeyAlgorithmRSA4096": { - "message": "RSA 4096-Bitowy" + "message": "RSA 4096-bitowy" }, "retry": { "message": "Powtórz" @@ -4756,19 +4756,19 @@ "description": "Heading for the password generator within the inline menu" }, "passwordRegenerated": { - "message": "Password regenerated", + "message": "Hasło zostało ponownie wygenerowane", "description": "Notification message for when a password has been regenerated" }, "saveLoginToBitwarden": { - "message": "Save login to Bitwarden?", + "message": "Zapisać dane logowania w Bitwarden?", "description": "Confirmation message for saving a login to Bitwarden" }, "spaceCharacterDescriptor": { - "message": "Space", + "message": "Spacja", "description": "Represents the space key in screen reader content as a readable word" }, "tildeCharacterDescriptor": { - "message": "Tilde", + "message": "Tylda", "description": "Represents the ~ key in screen reader content as a readable word" }, "backtickCharacterDescriptor": { @@ -4776,23 +4776,23 @@ "description": "Represents the ` key in screen reader content as a readable word" }, "exclamationCharacterDescriptor": { - "message": "Exclamation mark", + "message": "Wykrzyknik", "description": "Represents the ! key in screen reader content as a readable word" }, "atSignCharacterDescriptor": { - "message": "At sign", + "message": "Małpa", "description": "Represents the @ key in screen reader content as a readable word" }, "hashSignCharacterDescriptor": { - "message": "Hash sign", + "message": "Hashtag", "description": "Represents the # key in screen reader content as a readable word" }, "dollarSignCharacterDescriptor": { - "message": "Dollar sign", + "message": "Znak dolara", "description": "Represents the $ key in screen reader content as a readable word" }, "percentSignCharacterDescriptor": { - "message": "Percent sign", + "message": "Znak procenta", "description": "Represents the % key in screen reader content as a readable word" }, "caretCharacterDescriptor": { @@ -4804,23 +4804,23 @@ "description": "Represents the & key in screen reader content as a readable word" }, "asteriskCharacterDescriptor": { - "message": "Asterisk", + "message": "Gwiazdka", "description": "Represents the * key in screen reader content as a readable word" }, "parenLeftCharacterDescriptor": { - "message": "Left parenthesis", + "message": "Prawy nawias okrągły", "description": "Represents the ( key in screen reader content as a readable word" }, "parenRightCharacterDescriptor": { - "message": "Right parenthesis", + "message": "Prawy nawias okrągły", "description": "Represents the ) key in screen reader content as a readable word" }, "hyphenCharacterDescriptor": { - "message": "Underscore", + "message": "Znak podkreślenia", "description": "Represents the _ key in screen reader content as a readable word" }, "underscoreCharacterDescriptor": { - "message": "Hyphen", + "message": "Myślnik", "description": "Represents the - key in screen reader content as a readable word" }, "plusCharacterDescriptor": { @@ -4828,27 +4828,27 @@ "description": "Represents the + key in screen reader content as a readable word" }, "equalsCharacterDescriptor": { - "message": "Equals", + "message": "Znak równości", "description": "Represents the = key in screen reader content as a readable word" }, "braceLeftCharacterDescriptor": { - "message": "Left brace", + "message": "Lewy nawias klamrowy", "description": "Represents the { key in screen reader content as a readable word" }, "braceRightCharacterDescriptor": { - "message": "Right brace", + "message": "Prawy nawias klamrowy", "description": "Represents the } key in screen reader content as a readable word" }, "bracketLeftCharacterDescriptor": { - "message": "Left bracket", + "message": "Lewy nawias kwadratowy", "description": "Represents the [ key in screen reader content as a readable word" }, "bracketRightCharacterDescriptor": { - "message": "Right bracket", + "message": "Prawy nawias kwadratowy", "description": "Represents the ] key in screen reader content as a readable word" }, "pipeCharacterDescriptor": { - "message": "Pipe", + "message": "Pionowa kreska", "description": "Represents the | key in screen reader content as a readable word" }, "backSlashCharacterDescriptor": { @@ -4856,39 +4856,39 @@ "description": "Represents the back slash key in screen reader content as a readable word" }, "colonCharacterDescriptor": { - "message": "Colon", + "message": "Dwukropek", "description": "Represents the : key in screen reader content as a readable word" }, "semicolonCharacterDescriptor": { - "message": "Semicolon", + "message": "Średnik", "description": "Represents the ; key in screen reader content as a readable word" }, "doubleQuoteCharacterDescriptor": { - "message": "Double quote", + "message": "Cudzysłów", "description": "Represents the double quote key in screen reader content as a readable word" }, "singleQuoteCharacterDescriptor": { - "message": "Single quote", + "message": "Apostrof", "description": "Represents the ' key in screen reader content as a readable word" }, "lessThanCharacterDescriptor": { - "message": "Less than", + "message": "Mniejszy niż", "description": "Represents the < key in screen reader content as a readable word" }, "greaterThanCharacterDescriptor": { - "message": "Greater than", + "message": "Większy niż", "description": "Represents the > key in screen reader content as a readable word" }, "commaCharacterDescriptor": { - "message": "Comma", + "message": "Przecinek", "description": "Represents the , key in screen reader content as a readable word" }, "periodCharacterDescriptor": { - "message": "Period", + "message": "Kropka", "description": "Represents the . key in screen reader content as a readable word" }, "questionCharacterDescriptor": { - "message": "Question mark", + "message": "Znak zapytania", "description": "Represents the ? key in screen reader content as a readable word" }, "forwardSlashCharacterDescriptor": { @@ -4896,13 +4896,13 @@ "description": "Represents the / key in screen reader content as a readable word" }, "lowercaseAriaLabel": { - "message": "Lowercase" + "message": "Małe litery" }, "uppercaseAriaLabel": { - "message": "Uppercase" + "message": "Wielkie litery" }, "generatedPassword": { - "message": "Generated password" + "message": "Wygenerowane hasło" }, "compactMode": { "message": "Tryb kompaktowy" @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Szerokość rozszerzenia" }, diff --git a/apps/browser/src/_locales/pt_BR/messages.json b/apps/browser/src/_locales/pt_BR/messages.json index b346b8927e3..d7a8ae6ea4d 100644 --- a/apps/browser/src/_locales/pt_BR/messages.json +++ b/apps/browser/src/_locales/pt_BR/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Avaliar a Extensão" }, - "rateExtensionDesc": { - "message": "Por favor considere ajudar-nos com uma boa avaliação!" - }, "browserNotSupportClipboard": { "message": "O seu navegador web não suporta cópia para a área de transferência. Em alternativa, copie manualmente." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Mostrar o número de sugestões de preenchimento automático de login no ícone da extensão" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "Padrão do sistema" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/pt_PT/messages.json b/apps/browser/src/_locales/pt_PT/messages.json index 9b657e66b65..9bac24872e2 100644 --- a/apps/browser/src/_locales/pt_PT/messages.json +++ b/apps/browser/src/_locales/pt_PT/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Avaliar a extensão" }, - "rateExtensionDesc": { - "message": "Por favor, considere ajudar-nos com uma boa avaliação!" - }, "browserNotSupportClipboard": { "message": "O seu navegador Web não suporta a cópia fácil da área de transferência. Em vez disso, copie manualmente." }, @@ -3821,7 +3818,7 @@ "message": "Chave de acesso" }, "accessing": { - "message": "A aceder" + "message": "A aceder a" }, "loggedInExclamation": { "message": "Sessão iniciada!" @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Mostrar o número de sugestões de preenchimento automático de credenciais no ícone da extensão" }, + "showQuickCopyActions": { + "message": "Mostrar ações de cópia rápida no cofre" + }, "systemDefault": { "message": "Predefinição do sistema" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Aviso importante" + }, + "setupTwoStepLogin": { + "message": "Definir a verificação de dois passos" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "O Bitwarden enviará um código para o e-mail da sua conta para verificar as credenciais de novos dispositivos a partir de fevereiro de 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Pode configurar a verificação de dois passos como forma alternativa de proteger a sua conta ou alterar o seu e-mail para um a que possa aceder." + }, + "remindMeLater": { + "message": "Lembrar-me mais tarde" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Tem um acesso fiável ao seu e-mail, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Não, não tenho" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Sim, consigo aceder de forma fiável ao meu e-mail" + }, + "turnOnTwoStepLogin": { + "message": "Ativar a verificação de dois passos" + }, + "changeAcctEmail": { + "message": "Alterar o e-mail da conta" + }, "extensionWidth": { "message": "Largura da extensão" }, diff --git a/apps/browser/src/_locales/ro/messages.json b/apps/browser/src/_locales/ro/messages.json index a3e3ca308ed..cde1f233488 100644 --- a/apps/browser/src/_locales/ro/messages.json +++ b/apps/browser/src/_locales/ro/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Evaluare extensie" }, - "rateExtensionDesc": { - "message": "Vă rugăm să luați în considerare să ne ajutați cu o recenzie bună!" - }, "browserNotSupportClipboard": { "message": "Browserul dvs. nu acceptă copierea în clipboard. Transcrieți datele manual." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/ru/messages.json b/apps/browser/src/_locales/ru/messages.json index 145cd5d2d7f..a0279460363 100644 --- a/apps/browser/src/_locales/ru/messages.json +++ b/apps/browser/src/_locales/ru/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Оценить расширение" }, - "rateExtensionDesc": { - "message": "Пожалуйста, подумайте о том, чтобы помочь нам хорошим отзывом!" - }, "browserNotSupportClipboard": { "message": "Ваш браузер не поддерживает копирование данных в буфер обмена. Скопируйте вручную." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Показывать количество вариантов автозаполнения логина на значке расширения" }, + "showQuickCopyActions": { + "message": "Показать быстрые действия копирования в хранилище" + }, "systemDefault": { "message": "Системный" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Важное уведомление" + }, + "setupTwoStepLogin": { + "message": "Настроить двухэтапную аутентификацию" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Начиная с февраля 2025 года Bitwarden будет отправлять код на электронную почту вашего аккаунта для подтверждения авторизации с новых устройств." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "В качестве альтернативного способа защиты учетной записи вы можете настроить двухэтапную аутентификацию или сменить электронную почту на ту, к которой вы можете получить доступ." + }, + "remindMeLater": { + "message": "Напомнить позже" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Есть ли у вас надежный доступ к электронной почте $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Нет, не знаю" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Да, я имею надежный доступ к своей электронной почте" + }, + "turnOnTwoStepLogin": { + "message": "Включить двухэтапную аутентификацию" + }, + "changeAcctEmail": { + "message": "Изменить email аккаунта" + }, "extensionWidth": { "message": "Ширина расширения" }, diff --git a/apps/browser/src/_locales/si/messages.json b/apps/browser/src/_locales/si/messages.json index 81ece16334b..624e6f6f56e 100644 --- a/apps/browser/src/_locales/si/messages.json +++ b/apps/browser/src/_locales/si/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "දිගුව අනුපාතය" }, - "rateExtensionDesc": { - "message": "කරුණාකර හොඳ සමාලෝචනයකින් අපට උදව් කිරීම ගැන සලකා බලන්න!" - }, "browserNotSupportClipboard": { "message": "ඔබේ වෙබ් බ්රව්සරය පහසු පසුරු පුවරුවක් පිටපත් කිරීමට සහාය නොදක්වයි. ඒ වෙනුවට එය අතින් පිටපත් කරන්න." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/sk/messages.json b/apps/browser/src/_locales/sk/messages.json index 4bb0a99cca0..48c5007b54b 100644 --- a/apps/browser/src/_locales/sk/messages.json +++ b/apps/browser/src/_locales/sk/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Ohodnotiť rozšírenie" }, - "rateExtensionDesc": { - "message": "Prosíme, zvážte napísanie pozitívnej recenzie!" - }, "browserNotSupportClipboard": { "message": "Váš webový prehliadač nepodporuje automatické kopírovanie do schránky. Kopírujte manuálne." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Zobraziť počet odporúčaných prihlasovacích údajov na ikone rozšírenia" }, + "showQuickCopyActions": { + "message": "Zobraziť akcie rýchleho kopírovania v trezore" + }, "systemDefault": { "message": "Predvolené systémom" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Dôležité upozornenie" + }, + "setupTwoStepLogin": { + "message": "Nastavenie dvojstupňového prihlásenia" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden vám od februára 2025 pošle na e-mail vášho účtu kód na overenie prihlásenia z nových zariadení." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Ako alternatívny spôsob ochrany svojho účtu môžete nastaviť dvojstupňové prihlásenie alebo zmeniť e-mail na taký, ku ktorému máte prístup." + }, + "remindMeLater": { + "message": "Pripomenúť neskôr" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Máte spoľahlivý prístup k svojmu e-mailu, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Nie, nemám" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Áno, mám spoľahlivý prístup k svojmu e-mailu" + }, + "turnOnTwoStepLogin": { + "message": "Zapnúť dvojstupňové prihlásenie" + }, + "changeAcctEmail": { + "message": "Zmeniť e-mail účtu" + }, "extensionWidth": { "message": "Šírka rozšírenia" }, diff --git a/apps/browser/src/_locales/sl/messages.json b/apps/browser/src/_locales/sl/messages.json index 597155b775e..823b2ae0f18 100644 --- a/apps/browser/src/_locales/sl/messages.json +++ b/apps/browser/src/_locales/sl/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Ocenite to razširitev" }, - "rateExtensionDesc": { - "message": "Premislite, ali bi nam želeli pomagati z dobro oceno!" - }, "browserNotSupportClipboard": { "message": "Vaš brskalnik ne podpira enostavnega kopiranja na odložišče. Prosimo, kopirajte ročno." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/sr/messages.json b/apps/browser/src/_locales/sr/messages.json index df83b41a625..e940c908d6f 100644 --- a/apps/browser/src/_locales/sr/messages.json +++ b/apps/browser/src/_locales/sr/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Оцени овај додатак" }, - "rateExtensionDesc": { - "message": "Молимо вас да размотрите да нам помогнете уз добру оцену!" - }, "browserNotSupportClipboard": { "message": "Ваш прегледач не подржава једноставно копирање у клипборду. Уместо тога копирајте га ручно." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Прикажи број предлога за ауто-попуњавање пријаве на икони додатка" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "Системски подразумевано" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Бета" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Ширина додатка" }, diff --git a/apps/browser/src/_locales/sv/messages.json b/apps/browser/src/_locales/sv/messages.json index efdf8c10018..3cfb2feb37d 100644 --- a/apps/browser/src/_locales/sv/messages.json +++ b/apps/browser/src/_locales/sv/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Betygsätt tillägget" }, - "rateExtensionDesc": { - "message": "Överväg gärna att skriva en recension om oss!" - }, "browserNotSupportClipboard": { "message": "Din webbläsare har inte stöd för att enkelt kopiera till urklipp. Kopiera till urklipp manuellt istället." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "Systemstandard" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/te/messages.json b/apps/browser/src/_locales/te/messages.json index 779ff917578..8f7673803cb 100644 --- a/apps/browser/src/_locales/te/messages.json +++ b/apps/browser/src/_locales/te/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Rate the extension" }, - "rateExtensionDesc": { - "message": "Please consider helping us out with a good review!" - }, "browserNotSupportClipboard": { "message": "Your web browser does not support easy clipboard copying. Copy it manually instead." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/th/messages.json b/apps/browser/src/_locales/th/messages.json index b660dc785ba..2d65bb40f1a 100644 --- a/apps/browser/src/_locales/th/messages.json +++ b/apps/browser/src/_locales/th/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Rate the Extension" }, - "rateExtensionDesc": { - "message": "โปรดพิจารณา ช่วยเราด้วยการตรวจสอบที่ดี!" - }, "browserNotSupportClipboard": { "message": "เว็บเบราว์เซอร์ของคุณไม่รองรับการคัดลอกคลิปบอร์ดอย่างง่าย คัดลอกด้วยตนเองแทน" }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/tr/messages.json b/apps/browser/src/_locales/tr/messages.json index 8c8ffb0ffa7..4c87d5189fe 100644 --- a/apps/browser/src/_locales/tr/messages.json +++ b/apps/browser/src/_locales/tr/messages.json @@ -421,10 +421,10 @@ "message": "Son eşitleme:" }, "passGen": { - "message": "Parola üretici" + "message": "Parola üreteci" }, "generator": { - "message": "Oluşturucu", + "message": "Üreteç", "description": "Short for 'credential generator'." }, "passGenInfo": { @@ -648,9 +648,6 @@ "rateExtension": { "message": "Uzantıyı değerlendirin" }, - "rateExtensionDesc": { - "message": "İyi bir yorum yazarak bizi destekleyebilirsiniz." - }, "browserNotSupportClipboard": { "message": "Web tarayıcınız panoya kopyalamayı desteklemiyor. Parolayı elle kopyalayın." }, @@ -844,7 +841,7 @@ "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." }, "learnMoreAboutAuthenticators": { - "message": "Learn more about authenticators" + "message": "Kimlik doğrulayıcılar hakkında bilgi alın" }, "copyTOTP": { "message": "Kimlik doğrulama anahtarını kopyala (TOTP)" @@ -1327,10 +1324,10 @@ "message": "Kimlik doğrulama uygulamanızdaki 6 haneli doğrulama kodunu girin." }, "authenticationTimeout": { - "message": "Authentication timeout" + "message": "Kimlik doğrulama zaman aşımı" }, "authenticationSessionTimedOut": { - "message": "The authentication session timed out. Please restart the login process." + "message": "Kimlik doğrulama oturumu zaman aşımına uğradı. Lütfen giriş sürecini yeniden başlatın." }, "enterVerificationCodeEmail": { "message": "$EMAIL$ adresine e-postayla gönderdiğimiz 6 haneli doğrulama kodunu girin.", @@ -1435,7 +1432,7 @@ "message": "Specify the base URL of your on-premises hosted Bitwarden installation. Example: https://bitwarden.company.com" }, "selfHostedCustomEnvHeader": { - "message": "For advanced configuration, you can specify the base URL of each service independently." + "message": "İleri düzey yapılandırma için her hizmetin taban URL'sini bağımsız olarak belirleyebilirsiniz." }, "selfHostedEnvFormInvalid": { "message": "You must add either the base Server URL or at least one custom environment." @@ -3181,7 +3178,7 @@ "message": "Tüm giriş seçeneklerini gör" }, "viewAllLoginOptionsV1": { - "message": "View all log in options" + "message": "Tüm giriş seçeneklerini gör" }, "notificationSentDevice": { "message": "Cihazınıza bir bildirim gönderildi." @@ -4318,13 +4315,13 @@ "message": "Filtreler" }, "filterVault": { - "message": "Filter vault" + "message": "Kasayı filtrele" }, "filterApplied": { - "message": "One filter applied" + "message": "1 filtre uygulandı" }, "filterAppliedPlural": { - "message": "$COUNT$ filters applied", + "message": "$COUNT$ filtre uygulandı", "placeholders": { "count": { "content": "$1", @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Otomatik öneri sayısını uzantı simgesinde göster" }, + "showQuickCopyActions": { + "message": "Kasada hızlı kopyalama komutlarını göster" + }, "systemDefault": { "message": "Sistem varsayılanı" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Önemli uyarı" + }, + "setupTwoStepLogin": { + "message": "İki adımlı girişi ayarla" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Şubat 2025 itibarıyla Bitwarden, yeni cihazlardan yeni girişleri doğrulamanız için e-posta adresinize bir kod gönderecektir." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Hesabınızı korumanın alternatif bir yolu olarak iki adımlı girişi etkinleştirebilirsiniz. Aksi halde e-posta adresinizin doğru olduğundan emin olmalısınız." + }, + "remindMeLater": { + "message": "Daha sonra hatırlat" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "$EMAIL$ adresinize sağlıklı bir şekilde erişebiliyor musunuz?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Hayır, erişemiyorum" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Evet, e-postalarıma sağlıklı bir şekilde erişebiliyorum" + }, + "turnOnTwoStepLogin": { + "message": "İki adımlı girişi etkinleştir" + }, + "changeAcctEmail": { + "message": "Hesap e-postasını değiştir" + }, "extensionWidth": { "message": "Uzantı genişliği" }, diff --git a/apps/browser/src/_locales/uk/messages.json b/apps/browser/src/_locales/uk/messages.json index dc569fe0818..af8f6668111 100644 --- a/apps/browser/src/_locales/uk/messages.json +++ b/apps/browser/src/_locales/uk/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Оцінити розширення" }, - "rateExtensionDesc": { - "message": "Розкажіть іншим про свої враження, залишивши хороший відгук!" - }, "browserNotSupportClipboard": { "message": "Ваш браузер не підтримує копіювання даних в буфер обміну. Скопіюйте вручну." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Показувати кількість пропозицій автозаповнення на піктограмі розширення" }, + "showQuickCopyActions": { + "message": "Показати дії швидкого копіювання у сховищі" + }, "systemDefault": { "message": "Типово (система)" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Бета" }, + "importantNotice": { + "message": "Важлива інформація" + }, + "setupTwoStepLogin": { + "message": "Налаштувати двоетапну перевірку" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden надсилатиме код підтвердження на електронну пошту вашого облікового запису під час входу з нових пристроїв, починаючи з лютого 2025 року." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Ви можете налаштувати двоетапну перевірку як альтернативний спосіб захисту свого облікового запису, або змінити електронну пошту на таку, до якої ви маєте доступ." + }, + "remindMeLater": { + "message": "Нагадати пізніше" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Ви маєте постійний доступ до своєї електронної пошти $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Ні, не маю" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Так, я маю постійний доступ до своєї електронної пошти" + }, + "turnOnTwoStepLogin": { + "message": "Увімкнути двоетапну перевірку" + }, + "changeAcctEmail": { + "message": "Змінити адресу е-пошти" + }, "extensionWidth": { "message": "Ширина вікна розширення" }, diff --git a/apps/browser/src/_locales/vi/messages.json b/apps/browser/src/_locales/vi/messages.json index 591cb013968..db877e318ef 100644 --- a/apps/browser/src/_locales/vi/messages.json +++ b/apps/browser/src/_locales/vi/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "Đánh giá tiện ích mở rộng" }, - "rateExtensionDesc": { - "message": "Xin hãy nhìn nhận và đánh giá tốt cho chúng tôi!" - }, "browserNotSupportClipboard": { "message": "Trình duyệt web của bạn không hỗ trợ dễ dàng sao chép bộ nhớ tạm. Bạn có thể sao chép nó theo cách thủ công để thay thế." }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/zh_CN/messages.json b/apps/browser/src/_locales/zh_CN/messages.json index bdc4902b27d..a738ea4bb70 100644 --- a/apps/browser/src/_locales/zh_CN/messages.json +++ b/apps/browser/src/_locales/zh_CN/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "为本扩展打分" }, - "rateExtensionDesc": { - "message": "请给我们好评!" - }, "browserNotSupportClipboard": { "message": "您的浏览器不支持剪贴板简单复制,请手动复制。" }, @@ -2371,7 +2368,7 @@ "message": "限制查看" }, "limitSendViewsHint": { - "message": "在达到限额后,任何人无法查看此 Send。", + "message": "达到限额后,任何人无法查看此 Send。", "description": "Displayed under the limit views field on Send" }, "limitSendViewsCount": { @@ -3557,7 +3554,7 @@ "message": "切换侧边导航" }, "skipToContent": { - "message": "跳转到正文" + "message": "跳转到内容" }, "bitwardenOverlayButton": { "message": "Bitwarden 自动填充菜单按钮", @@ -4501,7 +4498,7 @@ "message": "对于如密码之类的敏感数据,请使用隐藏型字段" }, "checkBoxHelpText": { - "message": "如果您想自动勾选表单复选框(例如记住电子邮件地址),请使用复选框" + "message": "如果您想自动勾选表单复选框(例如记住电子邮件地址),请使用复选框型" }, "linkedHelpText": { "message": "当您处理特定网站的自动填充问题时,请使用链接型字段。" @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "在扩展图标上显示自动填充建议的登录的数量" }, + "showQuickCopyActions": { + "message": "在密码库上显示快速复制操作" + }, "systemDefault": { "message": "跟随系统" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta 版" }, + "importantNotice": { + "message": "重要通知" + }, + "setupTwoStepLogin": { + "message": "设置两步登录" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "从 2025 年 02 月开始,Bitwarden 将向您的账户电子邮件地址发送一个代码,以验证从新设备上的登录。" + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "您可以设置两步登录作为保护账户的替代方法,或将您的电子邮件地址更改为您可以访问的电子邮件地址。" + }, + "remindMeLater": { + "message": "稍后提醒我" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "您可以可靠地访问您的电子邮件地址 $EMAIL$ 吗?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "不,我不能" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "是的,我可以可靠地访问我的电子邮件地址" + }, + "turnOnTwoStepLogin": { + "message": "开启两步登录" + }, + "changeAcctEmail": { + "message": "更改账户电子邮件" + }, "extensionWidth": { "message": "扩展宽度" }, diff --git a/apps/browser/src/_locales/zh_TW/messages.json b/apps/browser/src/_locales/zh_TW/messages.json index cd0c1888034..2f1e1ef34f0 100644 --- a/apps/browser/src/_locales/zh_TW/messages.json +++ b/apps/browser/src/_locales/zh_TW/messages.json @@ -648,9 +648,6 @@ "rateExtension": { "message": "為本套件評分" }, - "rateExtensionDesc": { - "message": "請給予我們好評!" - }, "browserNotSupportClipboard": { "message": "您的瀏覽器不支援剪貼簿簡單複製,請手動複製。" }, @@ -4679,6 +4676,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, From 8c1f1a2e49e34266b800734ccf4b7f265145bd85 Mon Sep 17 00:00:00 2001 From: "bw-ghapp[bot]" <178206702+bw-ghapp[bot]@users.noreply.github.com> Date: Fri, 20 Dec 2024 13:31:45 +0100 Subject: [PATCH 07/75] Autosync the updated translations (#12495) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/web/src/locales/af/messages.json | 60 ++- apps/web/src/locales/ar/messages.json | 60 ++- apps/web/src/locales/az/messages.json | 62 ++- apps/web/src/locales/be/messages.json | 60 ++- apps/web/src/locales/bg/messages.json | 60 ++- apps/web/src/locales/bn/messages.json | 60 ++- apps/web/src/locales/bs/messages.json | 60 ++- apps/web/src/locales/ca/messages.json | 60 ++- apps/web/src/locales/cs/messages.json | 60 ++- apps/web/src/locales/cy/messages.json | 60 ++- apps/web/src/locales/da/messages.json | 60 ++- apps/web/src/locales/de/messages.json | 66 ++- apps/web/src/locales/el/messages.json | 60 ++- apps/web/src/locales/en_GB/messages.json | 60 ++- apps/web/src/locales/en_IN/messages.json | 60 ++- apps/web/src/locales/eo/messages.json | 60 ++- apps/web/src/locales/es/messages.json | 60 ++- apps/web/src/locales/et/messages.json | 60 ++- apps/web/src/locales/eu/messages.json | 60 ++- apps/web/src/locales/fa/messages.json | 60 ++- apps/web/src/locales/fi/messages.json | 60 ++- apps/web/src/locales/fil/messages.json | 60 ++- apps/web/src/locales/fr/messages.json | 98 ++-- apps/web/src/locales/gl/messages.json | 60 ++- apps/web/src/locales/he/messages.json | 60 ++- apps/web/src/locales/hi/messages.json | 60 ++- apps/web/src/locales/hr/messages.json | 582 ++++++++++++----------- apps/web/src/locales/hu/messages.json | 60 ++- apps/web/src/locales/id/messages.json | 60 ++- apps/web/src/locales/it/messages.json | 60 ++- apps/web/src/locales/ja/messages.json | 60 ++- apps/web/src/locales/ka/messages.json | 60 ++- apps/web/src/locales/km/messages.json | 60 ++- apps/web/src/locales/kn/messages.json | 60 ++- apps/web/src/locales/ko/messages.json | 60 ++- apps/web/src/locales/lv/messages.json | 60 ++- apps/web/src/locales/ml/messages.json | 60 ++- apps/web/src/locales/mr/messages.json | 60 ++- apps/web/src/locales/my/messages.json | 60 ++- apps/web/src/locales/nb/messages.json | 60 ++- apps/web/src/locales/ne/messages.json | 60 ++- apps/web/src/locales/nl/messages.json | 60 ++- apps/web/src/locales/nn/messages.json | 60 ++- apps/web/src/locales/or/messages.json | 60 ++- apps/web/src/locales/pl/messages.json | 60 ++- apps/web/src/locales/pt_BR/messages.json | 60 ++- apps/web/src/locales/pt_PT/messages.json | 62 ++- apps/web/src/locales/ro/messages.json | 60 ++- apps/web/src/locales/ru/messages.json | 68 ++- apps/web/src/locales/si/messages.json | 60 ++- apps/web/src/locales/sk/messages.json | 60 ++- apps/web/src/locales/sl/messages.json | 60 ++- apps/web/src/locales/sr/messages.json | 60 ++- apps/web/src/locales/sr_CS/messages.json | 60 ++- apps/web/src/locales/sv/messages.json | 60 ++- apps/web/src/locales/te/messages.json | 60 ++- apps/web/src/locales/th/messages.json | 60 ++- apps/web/src/locales/tr/messages.json | 60 ++- apps/web/src/locales/uk/messages.json | 60 ++- apps/web/src/locales/vi/messages.json | 60 ++- apps/web/src/locales/zh_CN/messages.json | 62 ++- apps/web/src/locales/zh_TW/messages.json | 60 ++- 62 files changed, 2894 insertions(+), 1406 deletions(-) diff --git a/apps/web/src/locales/af/messages.json b/apps/web/src/locales/af/messages.json index 783ee04ba6e..2e8c1fdea10 100644 --- a/apps/web/src/locales/af/messages.json +++ b/apps/web/src/locales/af/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Werk Blaaier By" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "U gebruik ’n onondersteunde webblaaier. Die webkluis werk dalk nie soos normaal nie." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/ar/messages.json b/apps/web/src/locales/ar/messages.json index a42eff8ee6a..91a223bc83f 100644 --- a/apps/web/src/locales/ar/messages.json +++ b/apps/web/src/locales/ar/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Update browser" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "You are using an unsupported web browser. The web vault may not function properly." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/az/messages.json b/apps/web/src/locales/az/messages.json index 68bb3664afa..1b3685420ce 100644 --- a/apps/web/src/locales/az/messages.json +++ b/apps/web/src/locales/az/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Üzvləri bərpa et" }, - "revokeMembersWarning": { - "message": "İddia edilən və edilməyən hesablara aid üzvlər, ləğv edildikdə fərqli nəticələrlə üzləşəcəklər:" - }, - "claimedAccountRevoke": { - "message": "İddia edilən hesab: Bitwarden hesabına müraciəti ləğv et" - }, - "unclaimedAccountRevoke": { - "message": "İddia edilməyən hesab: Təşkilat datasına müraciəti ləğv et" - }, - "claimedAccount": { - "message": "İddia edilən hesab" - }, - "unclaimedAccount": { - "message": "İddia edilməyən hesab" - }, - "restoreMembersInstructions": { - "message": "Bir üzvün hesabını bərpa etmək üçün Ləğv edildi vərəqinə gedin. Prosesin tamamlanması bir neçə saniyə çəkə bilər və yarımçıq kəsilə və ya ləğv edilə bilməz." - }, "cannotRestoreAccessError": { "message": "Təşkilat müraciəti bərpa edilə bilmir" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Brauzeri güncəllə" }, + "generatingRiskInsights": { + "message": "Risk təhlilləriniz yaradılır..." + }, "updateBrowserDesc": { "message": "Dəstəklənməyən bir veb brauzer istifadə edirsiniz. Veb seyf düzgün işləməyə bilər." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Açıqlayıcı kod" }, + "importantNotice": { + "message": "Vacib bildiriş" + }, + "setupTwoStepLogin": { + "message": "İki addımlı girişi qur" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden, 2025-ci ilin Fevral ayından etibarən yeni cihazlardan gələn girişləri doğrulamaq üçün hesabınızın e-poçtuna bir kod göndərəcək." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Hesabınızı qorumaq üçün alternativ bir yol kimi iki addımlı girişi qura və ya e-poçtunuzu müraciət edə biləcəyiniz e-poçtla dəyişdirə bilərsiniz." + }, + "remindMeLater": { + "message": "Daha sonra xatırlat" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "$EMAIL$ e-poçtunuza güvənli şəkildə müraciət edə bilirsiniz?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Xeyr, edə bilmirəm" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Bəli, e-poçtuma güvənli şəkildə müraciət edə bilirəm" + }, + "turnOnTwoStepLogin": { + "message": "İki addımlı girişi işə sal" + }, + "changeAcctEmail": { + "message": "Hesabın e-poçtunu dəyişdir" + }, "removeMembers": { "message": "Üzvləri çıxart" }, @@ -9982,6 +10003,9 @@ } }, "domainClaimed": { - "message": "Domain claimed" + "message": "Domen götürüldü" + }, + "organizationNameMaxLength": { + "message": "Təşkilat adı 50 xarakterdən çox ola bilməz." } } diff --git a/apps/web/src/locales/be/messages.json b/apps/web/src/locales/be/messages.json index 990e2c4b5b9..335dcbbe650 100644 --- a/apps/web/src/locales/be/messages.json +++ b/apps/web/src/locales/be/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Абнавіць браўзер" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "Ваш браўзер не падтрымліваецца. Вэб-сховішча можа працаваць няправільна." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/bg/messages.json b/apps/web/src/locales/bg/messages.json index ca7b3d147af..88634c13b98 100644 --- a/apps/web/src/locales/bg/messages.json +++ b/apps/web/src/locales/bg/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Възстановяване на достъпа на членове" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Обновяване на браузъра" }, + "generatingRiskInsights": { + "message": "Създаване на Вашата информация относно рисковете…" + }, "updateBrowserDesc": { "message": "Ползвате неподдържан браузър. Трезорът по уеб може да не сработи правилно." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Код от описанието" }, + "importantNotice": { + "message": "Важно съобщение" + }, + "setupTwoStepLogin": { + "message": "Настройте двустепенно удостоверяване" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Битуорден ще изпрати код до е-пощата Ви, за потвърждаване на вписването от нови устройства. Това ще започне от февруари 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Можете да настроите двустепенно удостоверяване, като различен метод на защита, или ако е необходимо да промените е-пощата си с такава, до която имате достъп." + }, + "remindMeLater": { + "message": "Напомнете ми по-късно" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Имате ли сигурен достъп до е-пощата си – $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Не, нямам" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Да, имам достъп до е-пощата си" + }, + "turnOnTwoStepLogin": { + "message": "Включване на двустепенното удостоверяване" + }, + "changeAcctEmail": { + "message": "Промяна на е-пощата" + }, "removeMembers": { "message": "Премахване на членовете" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Домейнът е присвоен" + }, + "organizationNameMaxLength": { + "message": "Името на организацията не може да бъде по-дълго от 50 знака." } } diff --git a/apps/web/src/locales/bn/messages.json b/apps/web/src/locales/bn/messages.json index 453507d441c..3860a66b925 100644 --- a/apps/web/src/locales/bn/messages.json +++ b/apps/web/src/locales/bn/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Update browser" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "You are using an unsupported web browser. The web vault may not function properly." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/bs/messages.json b/apps/web/src/locales/bs/messages.json index 6d489d73535..1604eb20677 100644 --- a/apps/web/src/locales/bs/messages.json +++ b/apps/web/src/locales/bs/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Update browser" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "You are using an unsupported web browser. The web vault may not function properly." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/ca/messages.json b/apps/web/src/locales/ca/messages.json index 84ca1d39d3a..38684e8aefa 100644 --- a/apps/web/src/locales/ca/messages.json +++ b/apps/web/src/locales/ca/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Actualitza el navegador" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "Esteu utilitzant un navegador web no compatible. La caixa forta web pot no funcionar correctament." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/cs/messages.json b/apps/web/src/locales/cs/messages.json index 4f7e53666bc..0fdac8e60f3 100644 --- a/apps/web/src/locales/cs/messages.json +++ b/apps/web/src/locales/cs/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Obnovit členy" }, - "revokeMembersWarning": { - "message": "Členové s nárokovanými a nenárokovanými účty budou mít při odvolání různé výsledky:" - }, - "claimedAccountRevoke": { - "message": "Nárokovaný účet: Zrušit přístup k účtu Bitwarden" - }, - "unclaimedAccountRevoke": { - "message": "Nenárokovaný účet: Odvolat přístup k datům organizace" - }, - "claimedAccount": { - "message": "Nárokovaný účet" - }, - "unclaimedAccount": { - "message": "Nenárokovaný účet" - }, - "restoreMembersInstructions": { - "message": "Chcete-li obnovit účet člena, přejděte na kartu Odvolané. Proces může trvat několik sekund a nelze jej přerušit ani zrušit." - }, "cannotRestoreAccessError": { "message": "Nelze obnovit přístup organizace" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Aktualizovat prohlížeč" }, + "generatingRiskInsights": { + "message": "Generování poznatků o rizicích..." + }, "updateBrowserDesc": { "message": "Používáte nepodporovaný webový prohlížeč. Webový trezor nemusí pracovat správně." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Kód z popisu" }, + "importantNotice": { + "message": "Důležité upozornění" + }, + "setupTwoStepLogin": { + "message": "Nastavit dvoufázové přihlášení" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden odešle kód na e-mail Vašeho účtu pro ověření přihlášení z nových zařízení počínaje únorem 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Dvoufázové přihlášení můžete nastavit jako alternativní způsob ochrany Vašeho účtu nebo změnit svůj e-mail na ten, k němuž můžete přistupovat." + }, + "remindMeLater": { + "message": "Připomenout později" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Máte spolehlivý přístup ke svému e-mailu $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Ne, nemám" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Ano, ke svému e-mailu mám přístup" + }, + "turnOnTwoStepLogin": { + "message": "Zapnout dvoufázové přihlášení" + }, + "changeAcctEmail": { + "message": "Změnit e-mail účtu" + }, "removeMembers": { "message": "Odebrat členy" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Doména uplatněna" + }, + "organizationNameMaxLength": { + "message": "Název organizace nesmí přesáhnout 50 znaků." } } diff --git a/apps/web/src/locales/cy/messages.json b/apps/web/src/locales/cy/messages.json index 10c89bf3c8b..c774497eae2 100644 --- a/apps/web/src/locales/cy/messages.json +++ b/apps/web/src/locales/cy/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Update browser" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "You are using an unsupported web browser. The web vault may not function properly." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/da/messages.json b/apps/web/src/locales/da/messages.json index 90a9271358a..86bb1145217 100644 --- a/apps/web/src/locales/da/messages.json +++ b/apps/web/src/locales/da/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Gendan medlemmer" }, - "revokeMembersWarning": { - "message": "Medlemmer med hævdede og uhævdede konti vil have forskellige resultater ved ophævning:" - }, - "claimedAccountRevoke": { - "message": "Hævdet konto: Ophæv adgang til Bitwarden-konto" - }, - "unclaimedAccountRevoke": { - "message": "Uhævdet konto: Ophæv adgang til organisationsdata" - }, - "claimedAccount": { - "message": "Hævdet konto" - }, - "unclaimedAccount": { - "message": "Uhævdet konto" - }, - "restoreMembersInstructions": { - "message": "For at gendanne et medlems konto, gå til fanen Ophævet. Processen kan tage et par sekunder at fuldføre og kan ikke afbrydes eller annulleres." - }, "cannotRestoreAccessError": { "message": "Kan ikke gendanne organisationsadgang" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Opdatér browser" }, + "generatingRiskInsights": { + "message": "Genererer risikoindsigter..." + }, "updateBrowserDesc": { "message": "Du bruger en ikke-understøttet webbrowser. Web-boksen fungerer muligvis ikke korrekt." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Beskrivelseskode" }, + "importantNotice": { + "message": "Vigtig notits" + }, + "setupTwoStepLogin": { + "message": "Opsæt totrins-login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Startende i februar 2025, sender Bitwarden en kode til kontoe-mailadressen for at bekræfte logins fra nye enheder." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Man kan opsætte totrins-login som en alternativ måde at beskytte sin konto på eller ændre sin e-mail til en, man kan tilgå." + }, + "remindMeLater": { + "message": "Påmind senere" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Er der pålidelig adgang til e-mailadressen, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Nej, muligvis ikke" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Ja, e-mailadressen kan pålideligt tilgås" + }, + "turnOnTwoStepLogin": { + "message": "Slå totrins-login til" + }, + "changeAcctEmail": { + "message": "Skift kontoe-mailadresse" + }, "removeMembers": { "message": "Fjern medlemmer" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domæne registreret" + }, + "organizationNameMaxLength": { + "message": "Organisationsnavn må ikke overstige 50 tegn." } } diff --git a/apps/web/src/locales/de/messages.json b/apps/web/src/locales/de/messages.json index 70694bb878f..b90d7fc2474 100644 --- a/apps/web/src/locales/de/messages.json +++ b/apps/web/src/locales/de/messages.json @@ -6,7 +6,7 @@ "message": "Kritische Anwendungen" }, "accessIntelligence": { - "message": "Access Intelligence" + "message": "Zugriff auf Informationen" }, "riskInsights": { "message": "Risiko-Überblick" @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Mitglieder wiederherstellen" }, - "revokeMembersWarning": { - "message": "Mitglieder mit beanspruchten und unbeanspruchten Konten werden andere Ergebnisse haben, wenn sie widerrufen werden:" - }, - "claimedAccountRevoke": { - "message": "Beanspruchtes Konto: Zugriff auf Bitwarden-Konto widerrufen" - }, - "unclaimedAccountRevoke": { - "message": "Unbeanspruchtes Konto: Zugriff auf Organisationsdaten widerrufen" - }, - "claimedAccount": { - "message": "Beanspruchtes Konto" - }, - "unclaimedAccount": { - "message": "Unbeanspruchtes Konto" - }, - "restoreMembersInstructions": { - "message": "Um das Konto eines Mitglieds wiederherzustellen, wechsel zum Wiederrufen-Tab. Der Vorgang kann einige Sekunden dauern und kann nicht unterbrochen oder abgebrochen werden." - }, "cannotRestoreAccessError": { "message": "Organisationszugriff kann nicht wiederhergestellt werden" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Browser aktualisieren" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "Du verwendest einen nicht unterstützten Webbrowser. Der Web-Tresor funktioniert möglicherweise nicht richtig." }, @@ -6835,7 +6820,7 @@ "description": "the text, 'SCIM', is an acronym and should not be translated." }, "scimIntegrationDescription": { - "message": "Automatische Bereitstellung von Benutzern und Gruppen mit deinem bevorzugten Identitätsanbieter über SCIM-Bereitstellung. Suche nach unterstützte Integrationen", + "message": "Automatische Bereitstellung von Benutzern und Gruppen mit deinem bevorzugten Identitätsanbieter über SCIM-Bereitstellung. Suche nach unterstützten Integrationen", "description": "the text, 'SCIM', is an acronym and should not be translated." }, "scimEnabledCheckboxDesc": { @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Beschreibungscode" }, + "importantNotice": { + "message": "Wichtiger Hinweis" + }, + "setupTwoStepLogin": { + "message": "Richte die zweistufige Anmeldung ein" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden wird einen Code an deine Konto-E-Mail-Adresse senden, um Anmeldungen von neuen Geräten ab Februar 2025 zu überprüfen." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Du kannst die zweistufige Anmeldung als eine alternative Methode einrichten, um deinen Account zu schützen, oder ändere deine E-Mail-Adresse zu einer, auf die du zugreifen kannst." + }, + "remindMeLater": { + "message": "Erinnere mich später" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Hast du zuverlässigen Zugriff auf deine E-Mail-Adresse $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Nein, habe ich nicht" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Ja, ich kann zuverlässig auf meine E-Mails zugreifen" + }, + "turnOnTwoStepLogin": { + "message": "Aktiviere die zweistufige Anmeldung" + }, + "changeAcctEmail": { + "message": "Ändere die E-Mail-Adresse des Kontos" + }, "removeMembers": { "message": "Mitglieder entfernen" }, @@ -9982,6 +10003,9 @@ } }, "domainClaimed": { - "message": "Domain claimed" + "message": "Beanspruchte Domain" + }, + "organizationNameMaxLength": { + "message": "Der Name der Organisation darf 50 Zeichen nicht überschreiten." } } diff --git a/apps/web/src/locales/el/messages.json b/apps/web/src/locales/el/messages.json index 782db881c7e..50a327b1fa1 100644 --- a/apps/web/src/locales/el/messages.json +++ b/apps/web/src/locales/el/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Ενημερώστε τον Browser" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "Χρησιμοποιείτε ένα μη υποστηριζόμενο browser. Το web vault ενδέχεται να μην λειτουργεί σωστά." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/en_GB/messages.json b/apps/web/src/locales/en_GB/messages.json index 975b02ff639..3da937ec266 100644 --- a/apps/web/src/locales/en_GB/messages.json +++ b/apps/web/src/locales/en_GB/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organisation data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or cancelled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organisation access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Update browser" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "You are using an unsupported web browser. The web vault may not function properly." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organisation name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/en_IN/messages.json b/apps/web/src/locales/en_IN/messages.json index d0b6023f532..dd507896e56 100644 --- a/apps/web/src/locales/en_IN/messages.json +++ b/apps/web/src/locales/en_IN/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organisation data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or cancelled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organisation access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Update browser" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "You are using an unsupported web browser. The web vault may not function properly." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organisation name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/eo/messages.json b/apps/web/src/locales/eo/messages.json index ae366fec51a..2c1aa36046e 100644 --- a/apps/web/src/locales/eo/messages.json +++ b/apps/web/src/locales/eo/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Ĝisdatigi retumilon" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "Vi uzas nesubtenatan tTT-legilon. La ttt-volbo eble ne funkcias ĝuste." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/es/messages.json b/apps/web/src/locales/es/messages.json index 08ad137c4dc..f2548036c2a 100644 --- a/apps/web/src/locales/es/messages.json +++ b/apps/web/src/locales/es/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restaurar miembros" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Actualizar navegador" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "Está utilizando un navegador web no compatible. Es posible que la caja fuerte web no funcione correctamente." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/et/messages.json b/apps/web/src/locales/et/messages.json index ff11a82830d..21096b3f710 100644 --- a/apps/web/src/locales/et/messages.json +++ b/apps/web/src/locales/et/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Uuenda brauserit" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "Kasutad brauserit, mida ei toetata. Veebihoidla ei pruugi hästi töötada." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/eu/messages.json b/apps/web/src/locales/eu/messages.json index a7261c0f615..19de255284b 100644 --- a/apps/web/src/locales/eu/messages.json +++ b/apps/web/src/locales/eu/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Nabigatzailea eguneratu" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "Euskarririk gabeko web nabigatzailea erabiltzen ari zara. Baliteke webguneko kutxa gotorrak behar bezala ez funtzionatzea." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/fa/messages.json b/apps/web/src/locales/fa/messages.json index 4656c997d30..bb422bf70ae 100644 --- a/apps/web/src/locales/fa/messages.json +++ b/apps/web/src/locales/fa/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "به‌روزرسانی مرورگر" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "شما از یک مرورگر وب پشتیبانی نشده استفاده می‌کنید. گاوصندوق وب ممکن است به درستی کار نکند." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/fi/messages.json b/apps/web/src/locales/fi/messages.json index f13e2b73a61..90a9078ceb9 100644 --- a/apps/web/src/locales/fi/messages.json +++ b/apps/web/src/locales/fi/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Palauta jäsenet" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Päivitä selain" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "Käytät selainta, jota ei tueta. Verkkoholvi ei välttämättä toimi oikein." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Poista jäsenet" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/fil/messages.json b/apps/web/src/locales/fil/messages.json index ef36872425c..6257f2b5eaf 100644 --- a/apps/web/src/locales/fil/messages.json +++ b/apps/web/src/locales/fil/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Update sa browser" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "Gumagamit ka ng isang hindi suportado na web browser. Ang web vault ay maaaring hindi gumana nang maayos." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/fr/messages.json b/apps/web/src/locales/fr/messages.json index 21566e83200..c9f87b19267 100644 --- a/apps/web/src/locales/fr/messages.json +++ b/apps/web/src/locales/fr/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restaurer des membres" }, - "revokeMembersWarning": { - "message": "Les membres avec des comptes réclamés ou non réclamés auront des résultats différents lorsqu'ils seront révoqués :" - }, - "claimedAccountRevoke": { - "message": "Compte réclamé : Révoquer l'accès au compte Bitwarden" - }, - "unclaimedAccountRevoke": { - "message": "Compte non réclamé : Révoquer l'accès aux données de l'organisation" - }, - "claimedAccount": { - "message": "Compte réclamé" - }, - "unclaimedAccount": { - "message": "Compte non réclamé" - }, - "restoreMembersInstructions": { - "message": "Pour restaurer le compte d'un membre, allez dans l'onglet Révoqué. Le processus peut prendre quelques secondes à compléter et ne peut pas être interrompu ou annulé." - }, "cannotRestoreAccessError": { "message": "Impossible de restaurer l'accès à l'organisation" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Mettre à jour le navigateur" }, + "generatingRiskInsights": { + "message": "Génération de vos connaissances en matière de risque..." + }, "updateBrowserDesc": { "message": "Vous utilisez un navigateur non supporté. Le coffre web pourrait ne pas fonctionner correctement." }, @@ -4740,10 +4725,10 @@ "message": "Connectez-vous en utilisant le portail de connexion unique de votre organisation. Veuillez entrer l'identifiant de votre organisation pour commencer." }, "singleSignOnEnterOrgIdentifier": { - "message": "Enter your organization's SSO identifier to begin" + "message": "Entrez l'identifiant SSO de votre organisation pour commencer" }, "singleSignOnEnterOrgIdentifierText": { - "message": "To log in with your SSO provider, enter your organization's SSO identifier to begin. You may need to enter this SSO identifier when you log in from a new device." + "message": "Pour vous connecter avec votre fournisseur de SSO, entrez l'identifiant SSO de votre organisation pour commencer. Vous devrez peut-être entrer cet identifiant SSO lorsque vous vous connecterez à partir d'un nouvel appareil." }, "enterpriseSingleSignOn": { "message": "Portail de connexion unique d'entreprise (Single Sign-On)" @@ -9662,10 +9647,10 @@ "message": "RSA 2048 bits" }, "sshKeyAlgorithmRSA3072": { - "message": "RSA 3072-Bit" + "message": "RSA 3072 bits" }, "sshKeyAlgorithmRSA4096": { - "message": "RSA 4096-Bit" + "message": "RSA 4096 bits" }, "premiumAccounts": { "message": "6 comptes premium" @@ -9791,23 +9776,23 @@ "message": "Êtes-vous sûr de vouloir supprimer définitivement cette pièce jointe ?" }, "manageSubscriptionFromThe": { - "message": "Manage subscription from the", + "message": "Gérer l'abonnement à partir du", "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." }, "toHostBitwardenOnYourOwnServer": { - "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + "message": "Pour héberger Bitwarden sur votre propre serveur, vous devrez téléverser votre fichier de licence. Pour prendre en charge les abonnements gratuits à Bitwarden Familles et les fonctionnalités de facturation avancées pour votre organisation auto-hébergée, vous devrez configurer la synchronisation automatique dans votre organisation auto-hébergée." }, "selfHostingTitleProper": { - "message": "Self-Hosting" + "message": "Auto-Hébergement" }, "claim-domain-single-org-warning": { "message": "Réclamer un domaine activera la politique d'organisation unique." }, "single-org-revoked-user-warning": { - "message": "Non-compliant members will be revoked. Administrators can restore members once they leave all other organizations." + "message": "Les membres non conformes seront révoqués. Les administrateurs peuvent restaurer les membres une fois qu'ils quittent toutes les autres organisations." }, "deleteOrganizationUser": { - "message": "Delete $NAME$", + "message": "Supprimer $NAME$", "placeholders": { "name": { "content": "$1", @@ -9831,7 +9816,7 @@ "description": "Warning description for the bulk delete organization users dialog" }, "organizationUserDeleted": { - "message": "Deleted $NAME$", + "message": "$NAME$ supprimé", "placeholders": { "name": { "content": "$1", @@ -9840,10 +9825,10 @@ } }, "organizationUserDeletedDesc": { - "message": "The user was removed from the organization and all associated user data has been deleted." + "message": "L'utilisateur a été retiré de l'organisation et toutes ses données utilisateur associées ont été supprimées." }, "deletedUserId": { - "message": "Deleted user $ID$ - an owner / admin deleted the user account", + "message": "Utilisateur supprimé $ID$ - un propriétaire / administrateur a supprimé le compte utilisateur", "placeholders": { "id": { "content": "$1", @@ -9852,7 +9837,7 @@ } }, "userLeftOrganization": { - "message": "User $ID$ left organization", + "message": "L'utilisateur $ID$ a quitté l'organisation", "placeholders": { "id": { "content": "$1", @@ -9861,7 +9846,7 @@ } }, "suspendedOrganizationTitle": { - "message": "The $ORGANIZATION$ is suspended", + "message": "$ORGANIZATION$ est suspendue", "placeholders": { "organization": { "content": "$1", @@ -9870,10 +9855,10 @@ } }, "suspendedUserOrgMessage": { - "message": "Contact your organization owner for assistance." + "message": "Contactez le propriétaire de votre organisation pour obtenir de l'aide." }, "suspendedOwnerOrgMessage": { - "message": "To regain access to your organization, add a payment method." + "message": "Pour regagner l'accès à votre organisation, ajoutez un mode de paiement." }, "deleteMembers": { "message": "Supprimer les membres" @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Code descripteur" }, + "importantNotice": { + "message": "Avis important" + }, + "setupTwoStepLogin": { + "message": "Configurer l'identification à deux facteurs" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden enverra un code au courriel de votre compte pour vérifier les connexions depuis de nouveaux appareils à partir de février 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Vous pouvez configurer l'identification à deux facteurs comme un moyen alternatif de protéger votre compte ou de changer votre adresse courriel à une autre à laquelle vous pouvez accéder." + }, + "remindMeLater": { + "message": "Me le rappeler plus tard" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Avez-vous un accès fiable à votre courriel $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Non, je ne l'ai pas" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Oui, je peux accéder à mon courriel de manière fiable" + }, + "turnOnTwoStepLogin": { + "message": "Configurer l'identification à deux facteurs" + }, + "changeAcctEmail": { + "message": "Changer l'adresse courriel du compte" + }, "removeMembers": { "message": "Retirer des membres" }, @@ -9960,7 +9981,7 @@ } }, "updatedRevokeSponsorshipConfirmationForSentSponsorship": { - "message": "If you remove $EMAIL$, the sponsorship for this Family plan cannot be redeemed. Are you sure you want to continue?", + "message": "Si vous supprimez $EMAIL$, la commandite pour ce plan Familles ne peut pas être échangée. Êtes-vous sûr de vouloir continuer ?", "placeholders": { "email": { "content": "$1", @@ -9969,7 +9990,7 @@ } }, "updatedRevokeSponsorshipConfirmationForAcceptedSponsorship": { - "message": "If you remove $EMAIL$, the sponsorship for this Family plan will end and the saved payment method will be charged $40 + applicable tax on $DATE$. You will not be able to redeem a new sponsorship until $DATE$. Are you sure you want to continue?", + "message": "Si vous supprimez $EMAIL$, la commandite pour ce plan Familles prendra fin et la méthode de paiement enregistrée sera facturée $40 + taxe applicable le $DATE$. Vous ne pourrez pas réclamer un nouveau parrainage avant $DATE$. Êtes-vous sûr de vouloir continuer ?", "placeholders": { "email": { "content": "$1", @@ -9982,6 +10003,9 @@ } }, "domainClaimed": { - "message": "Domain claimed" + "message": "Domaine réclamé" + }, + "organizationNameMaxLength": { + "message": "Le nom de l'organisation ne doit pas dépasser 50 caractères." } } diff --git a/apps/web/src/locales/gl/messages.json b/apps/web/src/locales/gl/messages.json index a1a802bac79..7fd7be8395d 100644 --- a/apps/web/src/locales/gl/messages.json +++ b/apps/web/src/locales/gl/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Update browser" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "You are using an unsupported web browser. The web vault may not function properly." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/he/messages.json b/apps/web/src/locales/he/messages.json index 857d685e81e..32e51d6a037 100644 --- a/apps/web/src/locales/he/messages.json +++ b/apps/web/src/locales/he/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "עדכן דפדפן" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "אתה משתמש בדפדפן אינטרנט שאיננו נתמך. כספת הרשת עלולה שלא לפעול כראוי." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/hi/messages.json b/apps/web/src/locales/hi/messages.json index 69a00333299..3f2bd5897e6 100644 --- a/apps/web/src/locales/hi/messages.json +++ b/apps/web/src/locales/hi/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Update browser" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "You are using an unsupported web browser. The web vault may not function properly." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/hr/messages.json b/apps/web/src/locales/hr/messages.json index 1621b1af818..9a127140786 100644 --- a/apps/web/src/locales/hr/messages.json +++ b/apps/web/src/locales/hr/messages.json @@ -1,24 +1,24 @@ { "allApplications": { - "message": "All applications" + "message": "Sve aplikacije" }, "criticalApplications": { - "message": "Critical applications" + "message": "Kritične aplikacije" }, "accessIntelligence": { - "message": "Access Intelligence" + "message": "Pristup inteligenciji" }, "riskInsights": { - "message": "Risk Insights" + "message": "Uvid u rizik" }, "passwordRisk": { - "message": "Password Risk" + "message": "Rizik lozinke" }, "reviewAtRiskPasswords": { - "message": "Review at-risk passwords (weak, exposed, or reused) across applications. Select your most critical applications to prioritize security actions for your users to address at-risk passwords." + "message": "Pregledaj rizične lozinke (slabe, izložene ili ponovno korištene) u svim aplikacijama. Odaberi svoje najkritičnije aplikacije za davanje prioriteta sigurnosnim radnjama da tvoji korisnici riješe rizične lozinke." }, "dataLastUpdated": { - "message": "Data last updated: $DATE$", + "message": "Zadnje ažuriranje: $DATE$", "placeholders": { "date": { "content": "$1", @@ -27,37 +27,19 @@ } }, "notifiedMembers": { - "message": "Notified members" + "message": "Obaviješteni članovi" }, "revokeMembers": { - "message": "Revoke members" + "message": "Opozovi članove" }, "restoreMembers": { - "message": "Restore members" - }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." + "message": "Vrati članove" }, "cannotRestoreAccessError": { - "message": "Cannot restore organization access" + "message": "Nije moguće vratiti pristup organizaciji" }, "allApplicationsWithCount": { - "message": "All applications ($COUNT$)", + "message": "Sve aplikacije ($COUNT$)", "placeholders": { "count": { "content": "$1", @@ -66,10 +48,10 @@ } }, "createNewLoginItem": { - "message": "Create new login item" + "message": "Stvori novu stavku prijave" }, "criticalApplicationsWithCount": { - "message": "Critical applications ($COUNT$)", + "message": "Kritične aplikacije ($COUNT$)", "placeholders": { "count": { "content": "$1", @@ -78,7 +60,7 @@ } }, "notifiedMembersWithCount": { - "message": "Notified members ($COUNT$)", + "message": "Obaviješteni članovi ($COUNT$)", "placeholders": { "count": { "content": "$1", @@ -87,7 +69,7 @@ } }, "noAppsInOrgTitle": { - "message": "No applications found in $ORG NAME$", + "message": "Nisu nađene aplikacije u $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -96,49 +78,49 @@ } }, "noAppsInOrgDescription": { - "message": "As users save logins, applications appear here, showing any at-risk passwords. Mark critical apps and notify users to update passwords." + "message": "Kako korisnici spremaju podatke za prijavu, ovdje se pojavljuju aplikacije koje prikazuju sve rizične lozinke. Označi kritične aplikacije i obavijesti korisnike da ažuriraju lozinke." }, "noCriticalAppsTitle": { - "message": "You haven't marked any applications as a Critical" + "message": "Niti jedna aplikacija nije označena kao kritična" }, "noCriticalAppsDescription": { - "message": "Select your most critical applications to discover at-risk passwords, and notify users to change those passwords." + "message": "Odaberi svoje najkritičnije aplikacije za otkrivanje rizičnih lozinki i obavijesti korisnike da promijene te lozinke." }, "markCriticalApps": { - "message": "Mark critical apps" + "message": "Označi kritične aplikacije" }, "markAppAsCritical": { - "message": "Mark app as critical" + "message": "Označi aplikacije kao kritične" }, "appsMarkedAsCritical": { - "message": "Apps marked as critical" + "message": "Aplikacije označene kao kritične" }, "application": { - "message": "Application" + "message": "Aplikacija" }, "atRiskPasswords": { - "message": "At-risk passwords" + "message": "Rizične lozinke" }, "requestPasswordChange": { - "message": "Request password change" + "message": "Zatraži promjenu lozinke" }, "totalPasswords": { - "message": "Total passwords" + "message": "Ukupno lozinki" }, "searchApps": { - "message": "Search applications" + "message": "Pretraži aplikacije" }, "atRiskMembers": { - "message": "At-risk members" + "message": "Rizični korisnici" }, "totalMembers": { - "message": "Total members" + "message": "Ukupno korisnika" }, "atRiskApplications": { - "message": "At-risk applications" + "message": "Rizične aplikacije" }, "totalApplications": { - "message": "Total applications" + "message": "Ukupno aplikacija" }, "whatTypeOfItem": { "message": "Koja je ovo vrsta stavke?" @@ -411,17 +393,17 @@ "message": "Boolean" }, "cfTypeCheckbox": { - "message": "Checkbox" + "message": "Potvrdni okvir" }, "cfTypeLinked": { "message": "Povezano", "description": "This describes a field that is 'linked' (related) to another field." }, "fieldType": { - "message": "Field type" + "message": "Vrsta polja" }, "fieldLabel": { - "message": "Field label" + "message": "Oznaka polja" }, "remove": { "message": "Ukloni" @@ -487,7 +469,7 @@ "message": "Generiraj lozinku" }, "generatePassphrase": { - "message": "Generate passphrase" + "message": "Generiraj fraznu lozinku" }, "checkPassword": { "message": "Provjeri je li lozinka bila ukradena." @@ -590,7 +572,7 @@ "message": "Sigurna bilješka" }, "typeSshKey": { - "message": "SSH key" + "message": "SSH ključ" }, "typeLoginPlural": { "message": "Prijave" @@ -668,7 +650,7 @@ "message": "Prikaz stavke" }, "newItemHeader": { - "message": "New $TYPE$", + "message": "Novi $TYPE$", "placeholders": { "type": { "content": "$1", @@ -677,7 +659,7 @@ } }, "editItemHeader": { - "message": "Edit $TYPE$", + "message": "Uredi $TYPE$", "placeholders": { "type": { "content": "$1", @@ -751,11 +733,11 @@ "description": "Copy password to clipboard" }, "copyPassphrase": { - "message": "Copy passphrase", + "message": "Kopiraj fraznu lozinku", "description": "Copy passphrase to clipboard" }, "passwordCopied": { - "message": "Password copied" + "message": "Lozinka kopirana" }, "copyUsername": { "message": "Kopiraj korisničko ime", @@ -964,7 +946,7 @@ "message": "Razina pristupa" }, "accessing": { - "message": "Pristupanje" + "message": "Poslužitelj:" }, "loggedOut": { "message": "Odjavljen/a" @@ -1012,7 +994,7 @@ "message": "Prijava uređajem mora biti namještena u postavka Bitwarden mobilne aplikacije. Trebaš drugu opciju?" }, "needAnotherOptionV1": { - "message": "Need another option?" + "message": "Trebaš drugu opciju?" }, "loginWithMasterPassword": { "message": "Prijava glavnom lozinkom" @@ -1027,13 +1009,13 @@ "message": "Koristi drugi način prijave" }, "logInWithPasskey": { - "message": "Log in with passkey" + "message": "Prijava pristupnim ključem" }, "useSingleSignOn": { - "message": "Use single sign-on" + "message": "Jedinstvena prijava (SSO)" }, "welcomeBack": { - "message": "Welcome back" + "message": "Dobro došli natrag" }, "invalidPasskeyPleaseTryAgain": { "message": "Nevažeći pristupni ključ. Pokušaj ponovno." @@ -1117,7 +1099,7 @@ "message": "Stvori račun" }, "newToBitwarden": { - "message": "New to Bitwarden?" + "message": "Novi u Bitwardenu?" }, "setAStrongPassword": { "message": "Postavi jaku lozinku" @@ -1135,13 +1117,13 @@ "message": "Prijavi se" }, "logInToBitwarden": { - "message": "Log in to Bitwarden" + "message": "Prijavi se u Bitwarden" }, "authenticationTimeout": { - "message": "Authentication timeout" + "message": "Istek vremena za autentifikaciju" }, "authenticationSessionTimedOut": { - "message": "The authentication session timed out. Please restart the login process." + "message": "Sesija za autentifikaciju je istekla. Ponovi proces prijave." }, "verifyIdentity": { "message": "Potvrdi svoj identitet" @@ -1275,7 +1257,7 @@ "message": "Trezor je zaključan" }, "yourAccountIsLocked": { - "message": "Your account is locked" + "message": "Tvoj račun je zaključan" }, "uuid": { "message": "UUID" @@ -1312,7 +1294,7 @@ "message": "Nemaš prava vidjeti sve stavke u ovoj zbirci." }, "youDoNotHavePermissions": { - "message": "You do not have permissions to this collection" + "message": "Nemaš prava za ovu kolekciju" }, "noCollectionsInList": { "message": "Nema zbirki za prikaz." @@ -1339,10 +1321,10 @@ "message": "Obavijest je poslana na tvoj uređaj." }, "aNotificationWasSentToYourDevice": { - "message": "A notification was sent to your device" + "message": "Obavijest je poslana na tvoj uređaj" }, "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { - "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + "message": "Provjeri je li trezor otključan i slaže li se jedinstvena fraza s drugim uređajem" }, "versionNumber": { "message": "Verzija $VERSION_NUMBER$", @@ -1354,10 +1336,10 @@ } }, "enterVerificationCodeApp": { - "message": "Unesi 6-znamenkasti kontrolni kôd iz autentifikatorske aplikacije." + "message": "Unesi 6-znamenkasti kôd za provjeru iz autentifikatorske aplikacije." }, "enterVerificationCodeEmail": { - "message": "Unesi 6-znamenkasti kontrolni kôd poslan e-poštom na $EMAIL$.", + "message": "Unesi 6-znamenkasti kôd za provjeru poslan e-poštom na $EMAIL$.", "placeholders": { "email": { "content": "$1", @@ -1489,7 +1471,7 @@ "message": "Sigurno želiš nastaviti?" }, "moveSelectedItemsDesc": { - "message": "Odaberi mapu u koju želiš premjestiti odabranih $COUNT$ stavke/i.", + "message": "Odaberi mapu u koju želiš dodati odabranih $COUNT$ stavke/i.", "placeholders": { "count": { "content": "$1", @@ -1628,7 +1610,7 @@ "description": "deprecated. Use avoidAmbiguous instead." }, "avoidAmbiguous": { - "message": "Avoid ambiguous characters", + "message": "Izbjegavaj dvosmislene znakove", "description": "Label for the avoid ambiguous characters checkbox." }, "regeneratePassword": { @@ -1653,7 +1635,7 @@ "description": "deprecated. Use numbersLabel instead." }, "specialCharacters": { - "message": "Posebni znakovi (!@#$%^&*)" + "message": "Posebni znakovi (! @ # $ % ^ & *)" }, "numWords": { "message": "Broj riječi" @@ -1669,32 +1651,32 @@ "message": "Uključi broj" }, "generatorPolicyInEffect": { - "message": "Enterprise policy requirements have been applied to your generator options.", + "message": "Pravila tvrtke primjenjena su na generator.", "description": "Indicates that a policy limits the credential generator screen." }, "passwordHistory": { "message": "Povijest" }, "generatorHistory": { - "message": "Generator history" + "message": "Povijest generatora" }, "clearGeneratorHistoryTitle": { - "message": "Clear generator history" + "message": "Očisti povijest generatora" }, "cleargGeneratorHistoryDescription": { - "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + "message": "Cijela povijest generatora biti će trajno izbirsana. Sigurno želiš nastaviti?" }, "noPasswordsInList": { "message": "Nema lozinki na popisu." }, "clearHistory": { - "message": "Clear history" + "message": "Očisti povijest" }, "nothingToShow": { - "message": "Nothing to show" + "message": "Ništa za prikazati" }, "nothingGeneratedRecently": { - "message": "You haven't generated anything recently" + "message": "Ništa nije generirano" }, "clear": { "message": "Očisti", @@ -1804,7 +1786,7 @@ "message": "Pažljivo, ove akcije su konačne i ne mogu se poništiti!" }, "dangerZoneDescSingular": { - "message": "Careful, this action is not reversible!" + "message": "Pažljivo, ova radnja se ne može poništiti!" }, "deauthorizeSessions": { "message": "Deautoriziraj sesije" @@ -1819,7 +1801,7 @@ "message": "Sve sesije deautorizirane" }, "accountIsOwnedMessage": { - "message": "This account is owned by $ORGANIZATIONNAME$", + "message": "Ovaj račun je vlasništvo $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -2267,7 +2249,7 @@ "message": "Unesi e-poštu na koju želiš primati verifikacijske kodove" }, "twoFactorEmailEnterCode": { - "message": "Unesi 6-znamenkasti verifikacijski kôd primljen e-poštom" + "message": "Unesi 6-znamenkasti kôd za provjeru primljen e-poštom" }, "sendEmail": { "message": "Pošalji poruku e-pošte" @@ -2423,7 +2405,7 @@ "message": "Provjeri izložene lozinke" }, "timesExposed": { - "message": "Times exposed" + "message": "Broj izloženosti" }, "exposedXTimes": { "message": "Izložene $COUNT$ put(a)", @@ -2460,7 +2442,7 @@ "message": "Niti jedna stavka u tvom trezoru nema slabu lozinku." }, "weakness": { - "message": "Weakness" + "message": "Slabost" }, "reusedPasswordsReport": { "message": "Izvještaj o istim lozinkama" @@ -2488,7 +2470,7 @@ "message": "Niti jedna prijava u tvom trezoru ne koristi iste lozinke." }, "timesReused": { - "message": "Times reused" + "message": "Broj ponovnih korištenja" }, "reusedXTimes": { "message": "Korišteno $COUNT$ puta", @@ -2646,7 +2628,7 @@ } }, "bitwardenFamiliesPlan": { - "message": "Plan Bitwarden Obitelji." + "message": "Paket Bitwarden Families." }, "addons": { "message": "Dodaci" @@ -2788,7 +2770,7 @@ "message": "Preuzmi licencu" }, "viewBillingToken": { - "message": "View Billing Token" + "message": "Pogledaj token za plaćanje" }, "updateLicense": { "message": "Ažuriraj licencu" @@ -2837,10 +2819,10 @@ "message": "Fakture" }, "noUnpaidInvoices": { - "message": "No unpaid invoices." + "message": "Nema neplaćenih računa." }, "noPaidInvoices": { - "message": "No paid invoices." + "message": "Nema plaćenih računa." }, "paid": { "message": "Plaćeno", @@ -3186,13 +3168,13 @@ "message": "Ljudi" }, "policies": { - "message": "Smjernice" + "message": "Pravila" }, "singleSignOn": { "message": "Jedinstvena prijava (SSO)" }, "editPolicy": { - "message": "Uredi smjernice" + "message": "Uređivanje pravila" }, "groups": { "message": "Grupe" @@ -3448,7 +3430,7 @@ } }, "viewAllLogInOptions": { - "message": "View all log in options" + "message": "Pogledaj sve mogućnosti prijave" }, "viewAllLoginOptions": { "message": "Pogledaj sve mogućnosti prijave" @@ -3583,7 +3565,7 @@ } }, "editedPolicyId": { - "message": "Uređene smjernice $ID$.", + "message": "Uređeno pravilo $ID$.", "placeholders": { "id": { "content": "$1", @@ -3900,11 +3882,14 @@ "updateBrowser": { "message": "Ažuriraj preglednik" }, + "generatingRiskInsights": { + "message": "Stvaranje tvojih uvida u rizik..." + }, "updateBrowserDesc": { "message": "Koristiš nepodržani preglednik. Web trezor možda neće ispravno raditi." }, "freeTrialEndPromptCount": { - "message": "Your free trial ends in $COUNT$ days.", + "message": "Besplatno probno razdoblje završava za $COUNT$ dan/a.", "placeholders": { "count": { "content": "$1", @@ -3913,7 +3898,7 @@ } }, "freeTrialEndPromptMultipleDays": { - "message": "$ORGANIZATION$, your free trial ends in $COUNT$ days.", + "message": "$ORGANIZATION$, besplatno probno razdoblje završava za $COUNT$ dan/a.", "placeholders": { "count": { "content": "$2", @@ -3926,7 +3911,7 @@ } }, "freeTrialEndPromptTomorrow": { - "message": "$ORGANIZATION$, your free trial ends tomorrow.", + "message": "$ORGANIZATION$, tvoje besplatno probno razdoblje završava sutra.", "placeholders": { "organization": { "content": "$1", @@ -3935,10 +3920,10 @@ } }, "freeTrialEndPromptTomorrowNoOrgName": { - "message": "Your free trial ends tomorrow." + "message": "Tvoje besplatno probno razdoblje završava sutra." }, "freeTrialEndPromptToday": { - "message": "$ORGANIZATION$, your free trial ends today.", + "message": "$ORGANIZATION$, tvoje besplatno probno razdoblje završava danas.", "placeholders": { "organization": { "content": "$1", @@ -3947,16 +3932,16 @@ } }, "freeTrialEndingTodayWithoutOrgName": { - "message": "Your free trial ends today." + "message": "Tvoje besplatno probno razdoblje završava danas." }, "clickHereToAddPaymentMethod": { - "message": "Click here to add a payment method." + "message": "Klikni ovdje za dodavanje načina plaćanja." }, "joinOrganization": { "message": "Pridruži se organizaciji" }, "joinOrganizationName": { - "message": "Join $ORGANIZATIONNAME$", + "message": "Pridruži se $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -4238,7 +4223,7 @@ } }, "subscriptionSponsoredFamiliesPlan": { - "message": "Tvoja pretplata dozvoljava najviše $COUNT$ korisnika. Tvoj plan je sponzoriran i naplaćuje se vanjskoj organizaciji.", + "message": "Tvoja pretplata dozvoljava najviše $COUNT$ korisnika. Tvoj paket je sponzoriran i naplaćuje se vanjskoj organizaciji.", "placeholders": { "count": { "content": "$1", @@ -4507,7 +4492,7 @@ "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." }, "youWillBeNotifiedOnceTheRequestIsApproved": { - "message": "You will be notified once the request is approved" + "message": "Dobiti ćeš obavijest kada je tvoj zahtjev odobren" }, "free": { "message": "Besplatno", @@ -4557,7 +4542,7 @@ "message": "Pravila glavne lozinke" }, "masterPassPolicyDesc": { - "message": "Postavi smjernice sigurnosti koju glavna lozinka mora zadovoljiti." + "message": "Postavi pravila sigurnosti koja mora zadovoljiti glavna lozinka." }, "twoStepLoginPolicyTitle": { "message": "Zahtijevaj prijavu dvostrukom autentifikacijom" @@ -4572,13 +4557,13 @@ "message": "Član si organizacije koja zahtijeva uključenu prijavu u dva koraka na tvojem računu. Ako onemogućiš sve pružatelje prijave u dva koraka, automatski ćeš biti uklonjen/a iz organizacije." }, "passwordGeneratorPolicyDesc": { - "message": "Postavi smjernice sigurnosti koju generirana lozinka mora zadovoljiti." + "message": "Postavi pravila sigurnosti koje mora zadovoljiti generator lozinki." }, "passwordGeneratorPolicyInEffect": { - "message": "Jedna ili više organizacijskih smjernica utječe na postavke generatora." + "message": "Jedna ili više organizacijskih pravila utječe na postavke generatora." }, "masterPasswordPolicyInEffect": { - "message": "Jedna ili više organizacijskih smjernica zahtijeva da tvoja glavna lozinka ispunjava sljedeće uvjete:" + "message": "Jedna ili više organizacijskih pravila zahtijeva da tvoja glavna lozinka ispunjava sljedeće uvjete:" }, "policyInEffectMinComplexity": { "message": "Minimalna ocjena složenosti od $SCORE$", @@ -4740,10 +4725,10 @@ "message": "Prijavi se koristeći SSO portal tvoje organizacije. Za nastavak unesi identifikator organizacije." }, "singleSignOnEnterOrgIdentifier": { - "message": "Enter your organization's SSO identifier to begin" + "message": "Za početak unesi SSO identifikator" }, "singleSignOnEnterOrgIdentifierText": { - "message": "To log in with your SSO provider, enter your organization's SSO identifier to begin. You may need to enter this SSO identifier when you log in from a new device." + "message": "Za prijavu sa tvojim SSO pružateljem usluga, unesi SSO identifikator svoje organizacije. Možda ćeš morati unijeti ovaj SSO identifikator kada se prijavljuješ s novog uređaja." }, "enterpriseSingleSignOn": { "message": "Jedinstvena prijava na razini tvrtke (SSO)" @@ -4782,7 +4767,7 @@ "message": "SSO autentifikacija putem SAML2.0 i OpenID Connect" }, "includeEnterprisePolicies": { - "message": "Smjernice za tvrtke" + "message": "Pravila za tvrtke" }, "ssoValidationFailed": { "message": "SSO provjera nije uspjela" @@ -4813,7 +4798,7 @@ "message": "Onemogući korisnicima da se pridruže drugim organizacijama." }, "singleOrgPolicyDesc": { - "message": "Restrict members from joining other organizations. This policy is required for organizations that have enabled domain verification." + "message": "Ograniči članove da se pridruže drugim organizacijama. Ovo je pravilo potrebno za organizacije koje imaju omogućenu provjeru domene." }, "singleOrgBlockCreateMessage": { "message": "Tvoja organizacija ima pravilo koje ti ne dozvoljava pridruživanje drugim organizacijama. Molimo kontaktiraj administratora svoje organizacije ili se prijavi s privatnim Bitwarden računom." @@ -4822,7 +4807,7 @@ "message": "Članovi organizacije koji nisu Vlasnici ili Administratori, a već su članovi neke druge organizacije, biti će uklonjeni iz tvoje organizacije." }, "singleOrgPolicyMemberWarning": { - "message": "Non-compliant members will be placed in revoked status until they leave all other organizations. Administrators are exempt and can restore members once compliance is met." + "message": "Nesukladni članovi bit će opozvani dok ne napuste sve druge organizacije. Administratori su izuzeti i mogu vratiti članove nakon što se ispuni usklađenost." }, "requireSso": { "message": "Zahtijevaj SSO autentifikaciju" @@ -4834,13 +4819,13 @@ "message": "Preduvjet" }, "requireSsoPolicyReq": { - "message": "Pravilo Isključive organizacije mora biti uključeno prije aktivacije ovog pravila." + "message": "Prije aktivacije ovog pravila mora se uključiti pravilo Isključive organizacije." }, "requireSsoPolicyReqError": { - "message": "Pravilo Isključive organizacije nije omogućeno." + "message": "Nije postavljeno pravilo Isključive organizacije." }, "requireSsoExemption": { - "message": "Vlasnici i Administratori organizacije nisu obuhvaćeni za provedbu ovog pravila." + "message": "Pravilo neće biti primjenjeno na Vlasnike i Administratore." }, "sendTypeFile": { "message": "Datoteka" @@ -5135,10 +5120,10 @@ "message": "Ukloni osobni trezor" }, "personalOwnershipPolicyDesc": { - "message": "Zahtijevaj korisnike spremanje stavki u trezor organizacije uklanjanjem opcije osobnog trezora." + "message": "Zahtijevaj da korisnici spremaju stavke u trezor organizacije uklanjanjem opcije osobnog trezora." }, "personalOwnershipExemption": { - "message": "Vlasnici i Administratori organizacije nisu obuhvaćeni za provedbu ovog pravila." + "message": "Vlasnici i Administratori organizacije izuzeti su od provedbe ovog pravila." }, "personalOwnershipSubmitError": { "message": "Pravila tvrtke onemogućuju spremanje stavki u osobni trezor. Promijeni vlasništvo stavke na tvrtku i odaberi dostupnu Zbirku." @@ -5147,7 +5132,7 @@ "message": "Onemogući Send" }, "disableSendPolicyDesc": { - "message": "Ne dozvoli korisnicima stvaranje ili uređivanje Sendova. Brisanje postojećeg Senda je dozvoljeno.", + "message": "Nemoj dozvoliti korisnicima stvaranje ili uređivanje Sendova. Brisanje postojećeg Senda je dozvoljeno.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "disableSendExemption": { @@ -5177,7 +5162,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendOptionsPolicyInEffect": { - "message": "Organizacijske smjernice trenutno na snazi:" + "message": "Organizacijska pravila trenutno na snazi:" }, "sendDisableHideEmailInEffect": { "message": "Korisnicima nije dopušteno skrivati adresu e-pošte od primatelja kod stvaranja ili uređivanja Senda.", @@ -5542,7 +5527,7 @@ "message": "ovaj korisnik" }, "resetPasswordMasterPasswordPolicyInEffect": { - "message": "Jedna ili više organizacijskih smjernica zahtijeva da glavna lozinka ispunjava sljedeće uvjete:" + "message": "Jedno ili više organizacijskih pravila zahtijeva da glavna lozinka ispunjava sljedeće uvjete:" }, "resetPasswordSuccess": { "message": "Uspješno ponovno postalvjena lozinka!" @@ -5560,7 +5545,7 @@ "message": "Postojeći računi s glavnim lozinkama zahtijevat će od članova da se sami učlane prije nego što im administratori mogu oporaviti račune. Automatsko učlanjenje uključit će oporavak računa i za nove članove." }, "accountRecoverySingleOrgRequirementDesc": { - "message": "Pravilo Isključive organizacije mora biti uključeno prije aktivacije ovog pravila." + "message": "Prije aktivacije ovog pravila mora se uključiti pravilo Isključive organizacije." }, "resetPasswordPolicyAutoEnroll": { "message": "Automatsko učlanjenje" @@ -5650,10 +5635,10 @@ "message": "Isključeno, nije primjenjivo na ovu radnju" }, "nonCompliantMembersTitle": { - "message": "Non-compliant members" + "message": "Nesukladni članovi" }, "nonCompliantMembersError": { - "message": "Members that are non-compliant with the Single organization or Two-step login policy cannot be restored until they adhere to the policy requirements" + "message": "Članovi koji nisu u skladu s Isključivom organizacijom ili politikom prijave s dvostrukom autentifikacijom ne mogu se vratiti dok god se ne pridržavaju pravila" }, "fingerprint": { "message": "Otisak prsta" @@ -5914,7 +5899,7 @@ "message": "Onemogući izvoz osobnog trezora" }, "disablePersonalVaultExportDescription": { - "message": "Onemogućuje korisnike da izvezu svoj osobni trezor." + "message": "Onemogućuje korisnicima izvoz osobnog trezora." }, "vaultExportDisabled": { "message": "Izvoz trezora onemogućen" @@ -6055,16 +6040,16 @@ "message": "Konfiguracija za jedinstvenu prijavu (SSO) je spremljena." }, "sponsoredFamilies": { - "message": "Besplatan obiteljski Bitwarden" + "message": "Besplatan Bitwarden Families" }, "sponsoredFamiliesEligible": { - "message": "Ti i tvoja obitelj ispunjavate uvjete za besplatni obiteljski Bitwarden. Iskoristiti ponudu svojom e-poštom kako bi zaštitio svoje podatke čak i kada nisi na poslu." + "message": "Ti i tvoja obitelj ispunjavate uvjete za besplatni Bitwarden Families. Iskoristiti ponudu svojom e-poštom kako bi zaštitio svoje podatke čak i kada nisi na poslu." }, "sponsoredFamiliesEligibleCard": { - "message": "Iskoristi svoju ponudu za besplatni obiteljski Bitwarden već danas kako bi tvoji podaci bili sigurni čak i kada nisi na poslu." + "message": "Iskoristi svoju ponudu za besplatni Bitwarden Families paket već danas kako bi tvoji podaci bili sigurni čak i kada nisi na poslu." }, "sponsoredFamiliesInclude": { - "message": "Bitwarden obiteljski plan uključuje" + "message": "Bitwarden Families paket uključuje" }, "sponsoredFamiliesPremiumAccess": { "message": "Premium pristup do 6 korisnika" @@ -6085,19 +6070,19 @@ "message": "Odaberi organizaciju koju želiš sponzorirati" }, "familiesSponsoringOrgSelect": { - "message": "Koju ponudu besplatnog obiteljskog plana želiš iskoristiti?" + "message": "Koju ponudu besplatnog Families paketa želiš iskoristiti?" }, "sponsoredFamiliesEmail": { - "message": "Unesi svoju osobnu e-poštu za korištenje obiteljskog Bitwardena" + "message": "Unesi svoju privatnu e-poštu za korištenje Bitwarden Families" }, "sponsoredFamiliesLeaveCopy": { - "message": "Ako ukloniš ponudu ili budeš uklonjen/a iz sponzorske organizacije, tvoje će sponzorstvo Obiteljskog plana isteći na sljedeći datum obnove." + "message": "Ako ukloniš ponudu ili budeš uklonjen/a iz sponzorske organizacije, tvoje će sponzorstvo Families paketa isteći na sljedeći datum obnove." }, "acceptBitwardenFamiliesHelp": { - "message": "Prihavati ponudu postojeće organizacije ili stvori novu obiteljsku organizaciju." + "message": "Prihavati ponudu postojeće organizacije ili stvori novu Families organizaciju." }, "setupSponsoredFamiliesLoginDesc": { - "message": "Ponuđen ti je besplatni obiteljski Bitwarden plan. Za nastavak, prijavi se korisničkim računom koji je dobio ponudu." + "message": "Ponuđen ti je besplatni Bitwarden Families paket. Za nastavak, prijavi se korisničkim računom koji je dobio ponudu." }, "sponsoredFamiliesAcceptFailed": { "message": "Nije moguće prihvatiti ponudu. Ponovo pošalji e-poštu ponude sa svog poslovnog računa i pokušaj ponovno." @@ -6112,10 +6097,10 @@ } }, "sponsoredFamiliesOffer": { - "message": "Prihvati besplatan obiteljski Bitwarden" + "message": "Prihvati besplatan Bitwarden Families" }, "sponsoredFamiliesOfferRedeemed": { - "message": "Besplatna ponuda obiteljski Bitwarden je uspješno iskorištena" + "message": "Besplatna ponuda Bitwarden Families je uspješno iskorištena" }, "redeemed": { "message": "Kôd iskorišten" @@ -6142,7 +6127,7 @@ } }, "freeFamiliesPlan": { - "message": "Besplatan obiteljski plan" + "message": "Besplatni Families paket" }, "redeemNow": { "message": "Iskoristi kôd sada" @@ -6245,7 +6230,7 @@ "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Connect login with SSO to your self-hosted decryption key server. Using this option, members won’t need to use their master passwords to decrypt vault data. The require SSO authentication and single organization policies are required to set up Key Connector decryption. Contact Bitwarden Support for set up assistance.'" }, "memberDecryptionKeyConnectorDescLink": { - "message": "zahtijevaj SSO autentifikaciju i pravila isključive organizacije", + "message": "zahtijevaj SSO autentifikaciju i pravila jedne organizacije", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Connect login with SSO to your self-hosted decryption key server. Using this option, members won’t need to use their master passwords to decrypt vault data. The require SSO authentication and single organization policies are required to set up Key Connector decryption. Contact Bitwarden Support for set up assistance.'" }, "memberDecryptionKeyConnectorDescEnd": { @@ -6280,7 +6265,7 @@ "message": "Sponzorirana ponuda je istekla. Kako na kraju 7 dnevnog probnog razdoblja ne bi došlo do terećenja, moguće je izbrisati stvorenu organizaciju. Zadržavanjem organizacije preuzimate obvezu plaćanja." }, "newFamiliesOrganization": { - "message": "Nova obiteljska organizacija" + "message": "Nova Families organizacija" }, "acceptOffer": { "message": "Prihvati ponudu" @@ -6307,7 +6292,7 @@ "message": "Pogledaj token za sinkronizaciju naplate" }, "generateBillingToken": { - "message": "Generate billing token" + "message": "Generiraj token za plaćanje" }, "copyPasteBillingSync": { "message": "Kopiraj i zalijepi ovaj token u postavke sinkronizacije naplate tvoje organizacije s vlastitim poslužiteljem." @@ -6316,7 +6301,7 @@ "message": "Tvoj token za sinkronizaciju naplate može pristupiti i uređivati postavke pretplate ove organizacije." }, "manageBillingTokenSync": { - "message": "Manage Billing Token" + "message": "Upravljaj tokenom za plaćanje" }, "setUpBillingSync": { "message": "Podesi sinkronizaciju naplate" @@ -6370,7 +6355,7 @@ "message": "Vlastiti poslužitelj" }, "selfHostingEnterpriseOrganizationSectionCopy": { - "message": "Za postavljanje svoje organizacije na vlastitom poslužitelju, morat ćeš prenijeti datoteku licence. Kako bi podržali planove Free Families i napredne mogućnosti naplate za svoju samostalnu organizaciju, morat ćeš postaviti sinkronizaciju naplate." + "message": "Za postavljanje svoje organizacije na vlastitom poslužitelju, morat ćeš prenijeti datoteku licence. Kako bi podržali besplatne Families pakete i napredne mogućnosti naplate za svoju samostalnu organizaciju, morat ćeš postaviti sinkronizaciju naplate." }, "billingSyncApiKeyRotated": { "message": "Token rotiran" @@ -6382,7 +6367,7 @@ "message": "Token za sinkronizaciju naplate" }, "automaticBillingSyncDesc": { - "message": "Automatic sync unlocks Families sponsorships and allows you to sync your license without uploading a file. After making updates in the Bitwarden cloud server, select Sync License to apply changes." + "message": "Automatska sinkronizacija otključava Families sponzorstva i omogućuje sinkronizaciju licence bez učitavanja datoteke. Nakon ažuriranja Bitwarden poslužitelja u oblaku, odaberi Sinkronizacija Licence za primjenu promjena." }, "active": { "message": "Aktivno" @@ -6452,7 +6437,7 @@ "message": "Obavezan Enditiy ID nije URL." }, "offerNoLongerValid": { - "message": "This offer is no longer valid. Contact your organization administrators for more information." + "message": "Ova ponuda više ne vrijedi. Obrati se administratorima svoje organizacije za više informacija." }, "openIdOptionalCustomizations": { "message": "Izborne prilagodbe" @@ -6544,10 +6529,10 @@ "message": "Generiraj korisničko ime" }, "generateEmail": { - "message": "Generate email" + "message": "Generiraj e-poštu" }, "spinboxBoundariesHint": { - "message": "Value must be between $MIN$ and $MAX$.", + "message": "Vrijednost mora biti u rasponu $MIN$ - $MAX$.", "description": "Explains spin box minimum and maximum values to the user", "placeholders": { "min": { @@ -6561,7 +6546,7 @@ } }, "passwordLengthRecommendationHint": { - "message": " Use $RECOMMENDED$ characters or more to generate a strong password.", + "message": " Koristi $RECOMMENDED$ i više znakova za generiranje jake lozinke.", "description": "Appended to `spinboxBoundariesHint` to recommend a length to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -6571,7 +6556,7 @@ } }, "passphraseNumWordsRecommendationHint": { - "message": " Use $RECOMMENDED$ words or more to generate a strong passphrase.", + "message": " Koristi $RECOMMENDED$ i više riječi za generiranje jake frazne lozinke.", "description": "Appended to `spinboxBoundariesHint` to recommend a number of words to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -6686,11 +6671,11 @@ "message": "Generiraj pseudonim e-pošte s vanjskom uslugom prosljeđivanja." }, "forwarderDomainName": { - "message": "Email domain", + "message": "Domena e-pošte", "description": "Labels the domain name email forwarder service option" }, "forwarderDomainNameHint": { - "message": "Choose a domain that is supported by the selected service", + "message": "Odaberi domenu koju podržava odabrani servis", "description": "Guidance provided for email forwarding services that support multiple email domains." }, "forwarderError": { @@ -6815,7 +6800,7 @@ "message": "Provjera uređaja ažurirana" }, "areYouSureYouWantToEnableDeviceVerificationTheVerificationCodeEmailsWillArriveAtX": { - "message": "Sigurno želiš uključiti provjeru uređaja? e-pošta s kontrolnim kôdom stići će na: $EMAIL$", + "message": "Sigurno želiš uključiti provjeru uređaja? e-pošta s kôdom za provjeru stići će na: $EMAIL$", "placeholders": { "email": { "content": "$1", @@ -6835,7 +6820,7 @@ "description": "the text, 'SCIM', is an acronym and should not be translated." }, "scimIntegrationDescription": { - "message": "Automatically provision users and groups with your preferred identity provider via SCIM provisioning. Find supported integrations", + "message": "Automatski korisnicima i grupama dodijeli željenog pružatelja identiteta putem SCIM dodjeljivanja. Pronađi podržane integracije", "description": "the text, 'SCIM', is an acronym and should not be translated." }, "scimEnabledCheckboxDesc": { @@ -6957,10 +6942,10 @@ } }, "singleFieldNeedsAttention": { - "message": "1 field needs your attention." + "message": "1 polje treba tvoju pažnju." }, "multipleFieldsNeedAttention": { - "message": "$COUNT$ fields need your attention.", + "message": "$COUNT$ polja treba(ju) tvoju pažnju.", "placeholders": { "count": { "content": "$1", @@ -7817,7 +7802,7 @@ "message": "Prenesi datoteku" }, "upload": { - "message": "Upload" + "message": "Prijenos" }, "acceptedFormats": { "message": "Prihvaćeni formati:" @@ -7829,13 +7814,13 @@ "message": "ili" }, "unlockWithBiometrics": { - "message": "Unlock with biometrics" + "message": "Otključaj biometrijom" }, "unlockWithPin": { - "message": "Unlock with PIN" + "message": "Otključaj PIN-om" }, "unlockWithMasterPassword": { - "message": "Unlock with master password" + "message": "Otključaj glavnom lozinkom" }, "licenseAndBillingManagement": { "message": "Upravljanje licencama i naplatom" @@ -7847,7 +7832,7 @@ "message": "Ručni prijenos" }, "manualBillingTokenUploadDesc": { - "message": "If you do not want to opt into billing sync, manually upload your license here. This will not automatically unlock Families sponsorships." + "message": "Ako ne želiš uključiti sinkronizaciju naplate, ovdje ručno prenesi svoju licencu. Time se neće automatski otključati Families sponzorstva." }, "syncLicense": { "message": "Sinkroniziraj licencu" @@ -8116,16 +8101,16 @@ "message": "Prijava pokrenuta" }, "rememberThisDeviceToMakeFutureLoginsSeamless": { - "message": "Remember this device to make future logins seamless" + "message": "Zapamti ovaj uređaj kako bi buduće prijave bile brže" }, "deviceApprovalRequired": { "message": "Potrebno je odobriti uređaj. Odaberi metodu odobravanja:" }, "deviceApprovalRequiredV2": { - "message": "Device approval required" + "message": "Potrebno odobrenje uređaja" }, "selectAnApprovalOptionBelow": { - "message": "Select an approval option below" + "message": "Odaberi opciju odobrenja" }, "rememberThisDevice": { "message": "Zapamti ovaj uređaj" @@ -8149,27 +8134,27 @@ "message": "Pouzdani uređaji" }, "memberDecryptionOptionTdeDescriptionPartOne": { - "message": "Jednom autentificirani, korisnici će moći dešifrirati podatke u trezoru koristeći svoju glavnu lozinku", + "message": "Jednom autentificirani, korisnici će moći dešifrirati podatke u trezoru koristeći ključ spremljen na njihovom uređaju. ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy, SSO Required policy, and account recovery administration policy with automatic enrollment will turn on when this option is used.'" }, "memberDecryptionOptionTdeDescriptionLinkOne": { - "message": "isključivo-organizacijska", + "message": "pravilo Isključive organizacije", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy, SSO required policy, and account recovery administration policy with automatic enrollment will turn on when this option is used.'" }, "memberDecryptionOptionTdeDescriptionPartTwo": { - "message": "politika,", + "message": ",", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy, SSO required policy, and account recovery administration policy with automatic enrollment will turn on when this option is used.'" }, "memberDecryptionOptionTdeDescriptionLinkTwo": { - "message": "obavezna SSO", + "message": "pravilo obavezne SSO autentifikacije", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy, SSO required policy, and account recovery administration policy with automatic enrollment will turn on when this option is used.'" }, "memberDecryptionOptionTdeDescriptionPartThree": { - "message": "politika i ", + "message": "pravilo i ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy, SSO required policy, and account recovery administration policy with automatic enrollment will turn on when this option is used.'" }, "memberDecryptionOptionTdeDescriptionLinkThree": { - "message": "politika administracije povrata računa", + "message": "pravilo administracije povrata računa", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The single organization policy, SSO required policy, and account recovery administration policy with automatic enrollment will turn on when this option is used.'" }, "memberDecryptionOptionTdeDescriptionPartFour": { @@ -8185,7 +8170,7 @@ "description": "Used as a card title description on the set password page to explain why the user is there" }, "cardMetrics": { - "message": "out of $TOTAL$", + "message": "od $TOTAL$", "placeholders": { "total": { "content": "$1", @@ -8357,7 +8342,7 @@ "message": "Nedostaje e-pošta korisnika" }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "Nije pronađena e-pošta aktivnog korisnika. Odjava u tijeku..." }, "deviceTrusted": { "message": "Uređaj pouzdan" @@ -8455,10 +8440,10 @@ "message": "Upravljaj ponašanjem zbirki za organizaciju" }, "limitCollectionCreationDesc": { - "message": "Limit collection creation to owners and admins" + "message": "Omogući kreiranje zbirki samo vlasnicima i adminima" }, "limitCollectionDeletionDesc": { - "message": "Limit collection deletion to owners and admins" + "message": "Omogući brisanje zbirki samo vlasnicima i adminima" }, "allowAdminAccessToAllCollectionItemsDesc": { "message": "Vlasnici i admini mogu upravljati svim zbirkama i stavkama" @@ -8506,7 +8491,7 @@ "message": "URL poslužitelja" }, "selfHostBaseUrl": { - "message": "Self-host server URL", + "message": "URL vlastitog poslužitelja", "description": "Label for field requesting a self-hosted integration service URL" }, "aliasDomain": { @@ -8718,7 +8703,7 @@ "message": "Dodaj polje" }, "editField": { - "message": "Edit field" + "message": "Uredi polje" }, "items": { "message": "Stavke" @@ -9033,47 +9018,47 @@ "message": "Koristi Bitwarden Secrets Manager SDK u sljedećim programskim jezicima za izradu vlastitih aplikacija." }, "ssoDescStart": { - "message": "Configure", + "message": "Podesi", "description": "This represents the beginning of a sentence, broken up to include links. The full sentence will be 'Configure single sign-on for Bitwarden using the implementation guide for your Identity Provider." }, "ssoDescEnd": { - "message": "for Bitwarden using the implementation guide for your Identity Provider.", + "message": "za Bitwarden koristeći vodič za implementaciju za tvojeg davatelja identiteta.", "description": "This represents the end of a sentence, broken up to include links. The full sentence will be 'Configure single sign-on for Bitwarden using the implementation guide for your Identity Provider." }, "userProvisioning": { - "message": "User provisioning" + "message": "Korisničko dodijeljivanje" }, "scimIntegration": { "message": "SCIM" }, "scimIntegrationDescStart": { - "message": "Configure ", + "message": "Podesi ", "description": "This represents the beginning of a sentence, broken up to include links. The full sentence will be 'Configure SCIM (System for Cross-domain Identity Management) to automatically provision users and groups to Bitwarden using the implementation guide for your Identity Provider" }, "scimIntegrationDescEnd": { - "message": "(System for Cross-domain Identity Management) to automatically provision users and groups to Bitwarden using the implementation guide for your Identity Provider.", + "message": "(Sustav za upravljanje identitetom između domena) za automatsko dodjeljivanje korisnika i grupa Bitwardenu korištenjem vodiča za implementaciju za vašeg pružatelja identiteta.", "description": "This represents the end of a sentence, broken up to include links. The full sentence will be 'Configure SCIM (System for Cross-domain Identity Management) to automatically provision users and groups to Bitwarden using the implementation guide for your Identity Provider" }, "bwdc": { "message": "Bitwarden Directory Connector" }, "bwdcDesc": { - "message": "Configure Bitwarden Directory Connector to automatically provision users and groups using the implementation guide for your Identity Provider." + "message": "Konfiguriraj Bitwarden Directory Connector za automatsko dodjeljivanje korisnika i grupa pomoću vodiča za implementaciju za vašeg pružatelja identiteta." }, "eventManagement": { - "message": "Event management" + "message": "Upravljanje događajima" }, "eventManagementDesc": { - "message": "Integrate Bitwarden event logs with your SIEM (system information and event management) system by using the implementation guide for your platform." + "message": "Integrirajte Bitwarden zapisnike događaja sa svojim SIEM (system information and event management) sustavom pomoću vodiča za implementaciju za vašu platformu." }, "deviceManagement": { - "message": "Device management" + "message": "Upravljanje uređajima" }, "deviceManagementDesc": { - "message": "Configure device management for Bitwarden using the implementation guide for your platform." + "message": "Konfiguriraj upravljanje uređajima za Bitwarden pomoću vodiča za implementaciju za svoju platformu." }, "integrationCardTooltip": { - "message": "Launch $INTEGRATION$ implementation guide.", + "message": "Pokreni vodič za implementaciju $INTEGRATION$.", "placeholders": { "integration": { "content": "$1", @@ -9082,7 +9067,7 @@ } }, "smIntegrationTooltip": { - "message": "Set up $INTEGRATION$.", + "message": "Postavi $INTEGRATION$.", "placeholders": { "integration": { "content": "$1", @@ -9091,7 +9076,7 @@ } }, "smSdkTooltip": { - "message": "View $SDK$ repository", + "message": "Pogledaj $SDK$ repozitorij", "placeholders": { "sdk": { "content": "$1", @@ -9100,7 +9085,7 @@ } }, "integrationCardAriaLabel": { - "message": "open $INTEGRATION$ implementation guide in a new tab.", + "message": "otvori vodič za implementaciju $INTEGRATION$ u novoj kartici.", "placeholders": { "integration": { "content": "$1", @@ -9109,7 +9094,7 @@ } }, "smSdkAriaLabel": { - "message": "view $SDK$ repository in a new tab.", + "message": "pogledaj $SDK$ repozitorij u novoj kartici.", "placeholders": { "sdk": { "content": "$1", @@ -9118,7 +9103,7 @@ } }, "smIntegrationCardAriaLabel": { - "message": "set up $INTEGRATION$ implementation guide in a new tab.", + "message": "postavi vodič za implementaciju $INTEGRATION$ u novoj kartici.", "placeholders": { "integration": { "content": "$1", @@ -9166,19 +9151,19 @@ "message": "Upravljanje naplatom s Portala pružatelja usluga" }, "continueSettingUpFreeTrial": { - "message": "Continue setting up your free trial of Bitwarden" + "message": "Nastavi s postavljanjem besplatne probe Bitwardena" }, "continueSettingUpFreeTrialPasswordManager": { - "message": "Continue setting up your free trial of Bitwarden Password Manager" + "message": "Nastavi s postavljanjem besplatne probe Bitwarden Upravitelja Lozinki" }, "continueSettingUpFreeTrialSecretsManager": { - "message": "Continue setting up your free trial of Bitwarden Secrets Manager" + "message": "Nastavi s postavljanjem besplatne probe Btwarden Secrets Managera" }, "enterTeamsOrgInfo": { "message": "Unesi podatke za organizaciju Timovi" }, "enterFamiliesOrgInfo": { - "message": "Unesi podatke za organizaciju Obitelji" + "message": "Unesi podatke za Families organizaciju" }, "enterEnterpriseOrgInfo": { "message": "Unesi podatke za organizaciju Tvrtke" @@ -9236,10 +9221,10 @@ "message": "Upravljani pružatelj usluga" }, "managedServiceProvider": { - "message": "Managed service provider" + "message": "Upravljani pružatelj usluga" }, "multiOrganizationEnterprise": { - "message": "Multi-organization enterprise" + "message": "Tvrtka s više organizacija" }, "orgSeats": { "message": "Organizacijska mjesta" @@ -9491,64 +9476,64 @@ "message": "kupljenih mjesta uklonjeno" }, "environmentVariables": { - "message": "Environment variables" + "message": "Varijable okruženja" }, "organizationId": { - "message": "Organization ID" + "message": "ID organizacije" }, "projectIds": { - "message": "Project IDs" + "message": "Projekt ID" }, "projectId": { - "message": "Project ID" + "message": "Projekt ID" }, "projectsAccessedByMachineAccount": { - "message": "The following projects can be accessed by this machine account." + "message": "Sljedećim projektima može se pristupiti putem ovog strojnog računa." }, "config": { - "message": "Config" + "message": "Konfiguracija" }, "learnMoreAboutEmergencyAccess": { - "message": "Learn more about emergency access" + "message": "Saznaj više o pristupu u nuždi" }, "learnMoreAboutMatchDetection": { - "message": "Learn more about match detection" + "message": "Saznaj više o otkrivanju podudaranja" }, "learnMoreAboutMasterPasswordReprompt": { - "message": "Learn more about master password re-prompt" + "message": "Saznaj više o ponovnom upitu za glavnu lozinku" }, "learnMoreAboutSearchingYourVault": { - "message": "Learn more about searching your vault" + "message": "Saznaj više o pretraživanju svojeg trezora" }, "learnMoreAboutYourAccountFingerprintPhrase": { - "message": "Learn about your account fingerprint phrase" + "message": "Saznaj više o jedinstvenoj frazi tvog računa" }, "impactOfRotatingYourEncryptionKey": { - "message": "Impact of rotating your encryption key" + "message": "Utjecaj rotiranja tvojeg ključa za šifriranje" }, "learnMoreAboutEncryptionAlgorithms": { - "message": "Learn more about encryption algorithms" + "message": "Saznaj više o algoritmima šifriranja" }, "learnMoreAboutKDFIterations": { - "message": "Learn more about KDF iterations" + "message": "Saznaj više o KDF iteracijama" }, "learnMoreAboutLocalization": { - "message": "Learn more about localization" + "message": "Saznaj više o prevođenju" }, "learnMoreAboutWebsiteIcons": { - "message": "Learn more about using website icons" + "message": "Saznaj više o korištenju ikona web stranica" }, "learnMoreAboutUserAccess": { - "message": "Learn more about user access" + "message": "Saznaj više o pristupu korisnika" }, "learnMoreAboutMemberRoles": { - "message": "Learn more about member roles and permissions" + "message": "Saznaj više o članskim ulogama i pravima" }, "whatIsACvvNumber": { - "message": "What is a CVV number?" + "message": "Što je CVV broj?" }, "learnMoreAboutApi": { - "message": "Learn more about Bitwarden's API" + "message": "Saznaj više o Bitwarden API" }, "fileSends": { "message": "Send datoteke" @@ -9590,7 +9575,7 @@ "message": "SSO autentifikacija" }, "familiesPlanInvLimitReachedManageBilling": { - "message": "Organizacije Obitelji mogu imati ovoliko članova: $SEATCOUNT$. Nadogradi na plaćeni plan za povećanje broja članova.", + "message": "Families organizacije mogu imati ovoliko članova: $SEATCOUNT$. Nadogradi na plaćeni plan za povećanje broja članova.", "placeholders": { "seatcount": { "content": "$1", @@ -9599,7 +9584,7 @@ } }, "familiesPlanInvLimitReachedNoManageBilling": { - "message": "Organizacije Obitelji mogu imati ovoliko članova: $SEATCOUNT$. Kontaktiraj vlasnika organizacije za nadogradnju.", + "message": "Families organizacije mogu imati ovoliko članova: $SEATCOUNT$. Kontaktiraj vlasnika organizacije za nadogradnju.", "placeholders": { "seatcount": { "content": "$1", @@ -9644,16 +9629,16 @@ "message": "GB dodatnog prostora" }, "sshKeyAlgorithm": { - "message": "Key algorithm" + "message": "Algoritam ključa" }, "sshKeyFingerprint": { - "message": "Fingerprint" + "message": "Otisak prsta" }, "sshKeyPrivateKey": { - "message": "Private key" + "message": "Privatni ključ" }, "sshKeyPublicKey": { - "message": "Public key" + "message": "Javni ključ" }, "sshKeyAlgorithmED25519": { "message": "ED25519" @@ -9710,7 +9695,7 @@ "message": "Trenutno" }, "secretsManagerSubscriptionInfo": { - "message": "Your Secrets Manager subscription will upgrade based on the plan selected" + "message": "Tvoja pretplata na Secrets Manager će se nadograditi na temelju odabranog plana" }, "bitwardenPasswordManager": { "message": "Bitwarden upravitelj lozinki" @@ -9735,22 +9720,22 @@ "message": "Uredi pristup" }, "textHelpText": { - "message": "Use text fields for data like security questions" + "message": "Koristi tekstualna polja za podatke poput sigurnosnih pitanja" }, "hiddenHelpText": { - "message": "Use hidden fields for sensitive data like a password" + "message": "Koristi skrivena polja za osjetljive podatke poput lozinke" }, "checkBoxHelpText": { - "message": "Use checkboxes if you'd like to autofill a form's checkbox, like a remember email" + "message": "Koristi potvrdne okvire ako ih želiš auto-ispuniti u obrascu, npr. zapamti adresu e-pošte" }, "linkedHelpText": { - "message": "Use a linked field when you are experiencing autofill issues for a specific website." + "message": "Koristi povezano polje kada imaš problema s auto-ispunom za određenu web stranicu." }, "linkedLabelHelpText": { - "message": "Enter the the field's html id, name, aria-label, or placeholder." + "message": "Unesi html id polja, naziv, aria-label ili rezervirano mjesto." }, "uppercaseDescription": { - "message": "Include uppercase characters", + "message": "Uključi velika slova", "description": "Tooltip for the password generator uppercase character checkbox" }, "uppercaseLabel": { @@ -9758,7 +9743,7 @@ "description": "Label for the password generator uppercase character checkbox" }, "lowercaseDescription": { - "message": "Include lowercase characters", + "message": "Uključi mala slova", "description": "Full description for the password generator lowercase character checkbox" }, "lowercaseLabel": { @@ -9766,7 +9751,7 @@ "description": "Label for the password generator lowercase character checkbox" }, "numbersDescription": { - "message": "Include numbers", + "message": "Uključi brojeve", "description": "Full description for the password generator numbers checkbox" }, "numbersLabel": { @@ -9774,40 +9759,40 @@ "description": "Label for the password generator numbers checkbox" }, "specialCharactersDescription": { - "message": "Include special characters", + "message": "Uključi posebne znakove", "description": "Full description for the password generator special characters checkbox" }, "specialCharactersLabel": { - "message": "!@#$%^&*", + "message": "! @ # $ % ^ & *", "description": "Label for the password generator special characters checkbox" }, "addAttachment": { - "message": "Add attachment" + "message": "Dodaj privitak" }, "maxFileSizeSansPunctuation": { - "message": "Maximum file size is 500 MB" + "message": "Najveća veličina datoteke je 500 MB" }, "permanentlyDeleteAttachmentConfirmation": { - "message": "Are you sure you want to permanently delete this attachment?" + "message": "Sigurno želiš trajno izbrisati ovaj privitak?" }, "manageSubscriptionFromThe": { - "message": "Manage subscription from the", + "message": "Upravljaj pretplatom iz datoteke", "description": "This represents the beginning of a sentence. The full sentence will be 'Manage subscription from the Provider Portal', but 'Provider Portal' will be a link and thus cannot be included in the translation file." }, "toHostBitwardenOnYourOwnServer": { - "message": "To host Bitwarden on your own server, you will need to upload your license file. To support Free Families plans and advanced billing capabilities for your self-hosted organization, you will need to set up automatic sync in your self-hosted organization." + "message": "Za smještanje Bitwardena na vlastiti poslužitelj, morat ćeš prenijeti datoteku licence. Kako bi podržali besplatne Families pakete i napredne mogućnosti naplate za svoj poslužitelj, morat ćeš postaviti automatsku sinkronizaciju." }, "selfHostingTitleProper": { - "message": "Self-Hosting" + "message": "Vlastiti poslužitelj" }, "claim-domain-single-org-warning": { - "message": "Claiming a domain will turn on the single organization policy." + "message": "Polaganje prava na domenu uključit će pravilo Isključive organizacije." }, "single-org-revoked-user-warning": { - "message": "Non-compliant members will be revoked. Administrators can restore members once they leave all other organizations." + "message": "Nesukladni članovi bit će opozvani. Administratori mogu vratiti članove nakon što napuste sve druge organizacije." }, "deleteOrganizationUser": { - "message": "Delete $NAME$", + "message": "Izbriši $NAME$", "placeholders": { "name": { "content": "$1", @@ -9817,7 +9802,7 @@ } }, "deleteOrganizationUserWarningDesc": { - "message": "This will permanently delete all items owned by $NAME$. Collection items are not impacted.", + "message": "Ovo će trajno obrisati sve stavke čiji vlasnik je $NAME$. Ne odnosi se na zbirke.", "description": "Warning description for the delete organization user dialog", "placeholders": { "name": { @@ -9827,11 +9812,11 @@ } }, "deleteManyOrganizationUsersWarningDesc": { - "message": "This will permanently delete all items owned by the following members. Collection items are not impacted.", + "message": "Ovo će trajno obrisati sve stavke sljedećih vlasnika. Ne odnosi se na zbirke.", "description": "Warning description for the bulk delete organization users dialog" }, "organizationUserDeleted": { - "message": "Deleted $NAME$", + "message": "$NAME$ izbrisano", "placeholders": { "name": { "content": "$1", @@ -9840,10 +9825,10 @@ } }, "organizationUserDeletedDesc": { - "message": "The user was removed from the organization and all associated user data has been deleted." + "message": "Korisnik je uklonjen iz organizacije i svi povezani korisnički podaci su izbrisani." }, "deletedUserId": { - "message": "Deleted user $ID$ - an owner / admin deleted the user account", + "message": "Izbrisan korisnik $ID$ - vlasnik/admin je izbrisao korisnički račun", "placeholders": { "id": { "content": "$1", @@ -9852,7 +9837,7 @@ } }, "userLeftOrganization": { - "message": "User $ID$ left organization", + "message": "Korisnik $ID$ je napustio organizaciju", "placeholders": { "id": { "content": "$1", @@ -9861,7 +9846,7 @@ } }, "suspendedOrganizationTitle": { - "message": "The $ORGANIZATION$ is suspended", + "message": "$ORGANIZATION$ je suspendirana", "placeholders": { "organization": { "content": "$1", @@ -9870,58 +9855,94 @@ } }, "suspendedUserOrgMessage": { - "message": "Contact your organization owner for assistance." + "message": "Za pomoć se obrati vlasniku organizacije." }, "suspendedOwnerOrgMessage": { - "message": "To regain access to your organization, add a payment method." + "message": "Za ponovni pristup svojoj organizaciji, dodaj način plaćanja." }, "deleteMembers": { - "message": "Delete members" + "message": "Obriši članove" }, "noSelectedMembersApplicable": { - "message": "This action is not applicable to any of the selected members." + "message": "Ova radnja nije primjenjiva niti na jednog odabranog korisnika." }, "deletedSuccessfully": { - "message": "Deleted successfully" + "message": "Uspješno izbrisano" }, "freeFamiliesSponsorship": { - "message": "Remove Free Bitwarden Families sponsorship" + "message": "Ukloni besplatno sponzorstvo Bitwarden Families" }, "freeFamiliesSponsorshipPolicyDesc": { - "message": "Do not allow members to redeem a Families plan through this organization." + "message": "Nemoj dopustiti članovima da koriste Families paket putem ove organizacije." }, "verifyBankAccountWithStatementDescriptorWarning": { - "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 organization's billing page to verify the bank account. Failure to verify the bank account will result in a missed payment and your subscription being suspended." + "message": "Plaćanje putem bankovnog računa dostupno je samo kupcima u SAD. Trebat ćeš potvrditi svoj bankovni račun. Unutar 1 - 2 radna dana izvršit ćemo mikro-uplatu. Unesi šifru u opisu ove uplate na stranici za naplatu organizacije za potvrdu bankovni račun. Nepotvrda bankovnog računa rezultirat će propuštenim plaćanjem i obustavom pretplate." }, "verifyBankAccountWithStatementDescriptorInstructions": { - "message": "We have made a micro-deposit to your bank account (this may take 1-2 business days). Enter the six-digit code starting with 'SM' found on the deposit description. Failure to verify the bank account will result in a missed payment and your subscription being suspended." + "message": "Izvršili smo mikro-uplatu na bankovni račun (ovo može potrajati 1 - 2 radna dana). Unesi šesteroznamenkasti kôd koji počinje sa 'SM' i nalazi se u opisu uplate. Nepotvrda bankovnog računa rezultirat će propuštenim plaćanjem i obustavom pretplate." }, "descriptorCode": { - "message": "Descriptor code" + "message": "Šifra uplate" + }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" }, "removeMembers": { - "message": "Remove members" + "message": "Ukloni članove" }, "claimedDomains": { - "message": "Claimed domains" + "message": "Potvrđene domene" }, "claimDomain": { - "message": "Claim domain" + "message": "Potvrdi domenu" }, "reclaimDomain": { - "message": "Reclaim domain" + "message": "Ponovno potvrdi domenu" }, "claimDomainNameInputHint": { - "message": "Example: mydomain.com. Subdomains require separate entries to be claimed." + "message": "Primjer: mydomain.com. Poddomene je potrebno zasebno potvrditi." }, "automaticClaimedDomains": { - "message": "Automatic Claimed Domains" + "message": "Automatski potvrđene domene" }, "automaticDomainClaimProcess": { - "message": "Bitwarden will attempt to claim the domain 3 times during the first 72 hours. If the domain can’t be claimed, check the DNS record in your host and manually claim. The domain will be removed from your organization in 7 days if it is not claimed." + "message": "Bitwarden će pokušati potvrditi domenu 3 puta tijekom prva 72 sata. Ako se domena ne može potvrditi, provjeri DNS zapis na svom poslužitelju i ručno potvrdi. Domena će, ako se ne potvrdi, biti uklonjena iz vaše organizacije nakon 7 dana." }, "domainNotClaimed": { - "message": "$DOMAIN$ not claimed. Check your DNS records.", + "message": "$DOMAIN$ nije potvrđena. Provjeri DNS zapise.", "placeholders": { "DOMAIN": { "content": "$1", @@ -9930,19 +9951,19 @@ } }, "domainStatusClaimed": { - "message": "Claimed" + "message": "Potvrđena" }, "domainStatusUnderVerification": { - "message": "Under verification" + "message": "Provjera u tijeku" }, "claimedDomainsDesc": { - "message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts." + "message": "Potvrdi domenu za vlasništvo nad svim računima članova čija adresa e-pošte odgovara domeni. Članovi će moći preskočiti SSO identifikator prilikom prijave. Administratori će također moći brisati članske račune." }, "invalidDomainNameClaimMessage": { - "message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed." + "message": "Unos nije važeći. Format: mojadomena.hr Poddomene zahtijevaju zasebne unose za provjeru." }, "domainClaimedEvent": { - "message": "$DOMAIN$ claimed", + "message": "$DOMAIN$ potvrđena", "placeholders": { "DOMAIN": { "content": "$1", @@ -9951,7 +9972,7 @@ } }, "domainNotClaimedEvent": { - "message": "$DOMAIN$ not claimed", + "message": "$DOMAIN$ nije potvrđena", "placeholders": { "DOMAIN": { "content": "$1", @@ -9960,7 +9981,7 @@ } }, "updatedRevokeSponsorshipConfirmationForSentSponsorship": { - "message": "If you remove $EMAIL$, the sponsorship for this Family plan cannot be redeemed. Are you sure you want to continue?", + "message": "Ako ukloniš $EMAIL$, sponzorstvo za ovaj Families paket se neće moći iskoristiti. Sigurno želiš nastaviti?", "placeholders": { "email": { "content": "$1", @@ -9969,7 +9990,7 @@ } }, "updatedRevokeSponsorshipConfirmationForAcceptedSponsorship": { - "message": "If you remove $EMAIL$, the sponsorship for this Family plan will end and the saved payment method will be charged $40 + applicable tax on $DATE$. You will not be able to redeem a new sponsorship until $DATE$. Are you sure you want to continue?", + "message": "Ako ukloniš $EMAIL$, sponzorstvo za ovaj Families paket če završiti, a na spremljeni način plaćanja će ovaj dartuM: $DATE$ biti naplaćeno 40 USD + primjenjivi porez. Nećeš moći iskoristiti novo sponzorstvo do $DATE$. Sigurno želiš nastaviti?", "placeholders": { "email": { "content": "$1", @@ -9982,6 +10003,9 @@ } }, "domainClaimed": { - "message": "Domain claimed" + "message": "Domena potvrđena" + }, + "organizationNameMaxLength": { + "message": "Naziv organizacije ne može biti duži od 50 znakova." } } diff --git a/apps/web/src/locales/hu/messages.json b/apps/web/src/locales/hu/messages.json index 71f96aabb5e..682f080fb87 100644 --- a/apps/web/src/locales/hu/messages.json +++ b/apps/web/src/locales/hu/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Tagok visszaállítása" }, - "revokeMembersWarning": { - "message": "Az igényelt és nem igényelt fiókokkal rendelkező tagok visszavonása esetén eltérő eredményeket érnek el:" - }, - "claimedAccountRevoke": { - "message": "Igényelt fiók: A Bitwarden fiókhoz hozzáférés visszavonása" - }, - "unclaimedAccountRevoke": { - "message": "Nem igényelt fiók: A szervezeti adatokhoz hozzáférés visszavonása" - }, - "claimedAccount": { - "message": "Igényelt fiók" - }, - "unclaimedAccount": { - "message": "Nem-igényelt fiók" - }, - "restoreMembersInstructions": { - "message": "Egy tag fiókjának visszaállításához lépjünk a Visszavont fülre. A folyamat néhány másodpercig tarthat és nem lehet megszakítani vagy törölni." - }, "cannotRestoreAccessError": { "message": "Nem lehet visszaállítani a szervezeti hozzáférést." }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Böngésző frissítése" }, + "generatingRiskInsights": { + "message": "A kockázati betekintések generálása..." + }, "updateBrowserDesc": { "message": "Nem támogatott böngészőt használunk. Előfordulhat, hogy a webes széf nem működik megfelelően." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Leíró kód" }, + "importantNotice": { + "message": "Fontos megjegyzés" + }, + "setupTwoStepLogin": { + "message": "Kétlépéses bejelentkezés szükséges" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "A Bitwarden 2025 februárjától kódot küld a fiókhoz tartozó email-címre, amellyel ellenőrizhetők az új eszközökről történő bejelentkezések." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "A fiók védelmének alternatív módjaként beállíthatunk kétlépcsős bejelentkezést vagy módosíthatjuk az email címet egy elérhetőre." + }, + "remindMeLater": { + "message": "Emlékeztetés később" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Megbízható a hozzáférés $EMAIL$ email címhez?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Nem, nem érem el" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Igen, megbízhatóan hozzáférek az emailjeimhez" + }, + "turnOnTwoStepLogin": { + "message": "Kétlépéses bejelentkezés bekapcsolása" + }, + "changeAcctEmail": { + "message": "Fiók email cím megváltoztatása" + }, "removeMembers": { "message": "Tagok eltávolítása" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "A tartomány követelésre került." + }, + "organizationNameMaxLength": { + "message": "A szervezet neve nem haladhatja meg az 50 karaktert." } } diff --git a/apps/web/src/locales/id/messages.json b/apps/web/src/locales/id/messages.json index ac113158285..877a6b21261 100644 --- a/apps/web/src/locales/id/messages.json +++ b/apps/web/src/locales/id/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Perbarui Browser" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "Anda menggunakan browser web yang tidak didukung. Kubah web mungkin tidak berfungsi dengan baik." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/it/messages.json b/apps/web/src/locales/it/messages.json index 174d29a6f16..36bcc07a023 100644 --- a/apps/web/src/locales/it/messages.json +++ b/apps/web/src/locales/it/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Aggiorna browser" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "Stai utilizzando un browser non supportato. La cassaforte web potrebbe non funzionare correttamente." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/ja/messages.json b/apps/web/src/locales/ja/messages.json index 050bb4ffb2a..343818e3ad4 100644 --- a/apps/web/src/locales/ja/messages.json +++ b/apps/web/src/locales/ja/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "ブラウザを更新" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "サポートされていないブラウザを使用しています。ウェブ保管庫が正しく動作しないかもしれません。" }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "メンバーを削除" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/ka/messages.json b/apps/web/src/locales/ka/messages.json index 14be03e0bd5..2a61cd89766 100644 --- a/apps/web/src/locales/ka/messages.json +++ b/apps/web/src/locales/ka/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Update browser" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "You are using an unsupported web browser. The web vault may not function properly." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/km/messages.json b/apps/web/src/locales/km/messages.json index 88355860d4f..c5a77826c9e 100644 --- a/apps/web/src/locales/km/messages.json +++ b/apps/web/src/locales/km/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Update browser" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "You are using an unsupported web browser. The web vault may not function properly." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/kn/messages.json b/apps/web/src/locales/kn/messages.json index 5b0f73c44fe..ec7241be790 100644 --- a/apps/web/src/locales/kn/messages.json +++ b/apps/web/src/locales/kn/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "ಬ್ರೌಸರ್ ನವೀಕರಿಸಿ" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "ನೀವು ಬೆಂಬಲಿಸದ ವೆಬ್ ಬ್ರೌಸರ್ ಅನ್ನು ಬಳಸುತ್ತಿರುವಿರಿ. ವೆಬ್ ವಾಲ್ಟ್ ಸರಿಯಾಗಿ ಕಾರ್ಯನಿರ್ವಹಿಸದೆ ಇರಬಹುದು." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/ko/messages.json b/apps/web/src/locales/ko/messages.json index 5f3f2692fc4..59c0e15ee33 100644 --- a/apps/web/src/locales/ko/messages.json +++ b/apps/web/src/locales/ko/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "브라우저 업데이트" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "지원하지 않는 웹 브라우저를 사용하고 있습니다. 웹 보관함 기능이 제대로 동작하지 않을 수 있습니다." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/lv/messages.json b/apps/web/src/locales/lv/messages.json index f8425f78b08..f60e5691572 100644 --- a/apps/web/src/locales/lv/messages.json +++ b/apps/web/src/locales/lv/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Atjaunot dalībniekus" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "Lai atjaunotu dalībnieka kontu, jādodas uz cilni \"Atsaukts\". Darbība var aizņemt dažas sekundes, un to nevar pārtraukt vai atcelt." - }, "cannotRestoreAccessError": { "message": "Nevar atjaunot apvienības piekļuvi" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Atjaunināt pārlūku" }, + "generatingRiskInsights": { + "message": "Tiek veidots ieskats par riskiem..." + }, "updateBrowserDesc": { "message": "Tiek izmantots neatbalstīts tīmekļa pārlūks. Tīmekļa glabātava var nedarboties pareizi." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Apraksta kods" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Noņemt dalībniekus" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domēns pieteikts" + }, + "organizationNameMaxLength": { + "message": "Apvienības nosaukums nevar pārsniegt 50 rakstzīmes." } } diff --git a/apps/web/src/locales/ml/messages.json b/apps/web/src/locales/ml/messages.json index d970aa004e8..bca6a172b62 100644 --- a/apps/web/src/locales/ml/messages.json +++ b/apps/web/src/locales/ml/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "ബ്രൌസർ അപ്‌ഡേറ്റുചെയ്യുക" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "You are using an unsupported web browser. The web vault may not function properly." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/mr/messages.json b/apps/web/src/locales/mr/messages.json index 88355860d4f..c5a77826c9e 100644 --- a/apps/web/src/locales/mr/messages.json +++ b/apps/web/src/locales/mr/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Update browser" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "You are using an unsupported web browser. The web vault may not function properly." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/my/messages.json b/apps/web/src/locales/my/messages.json index 88355860d4f..c5a77826c9e 100644 --- a/apps/web/src/locales/my/messages.json +++ b/apps/web/src/locales/my/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Update browser" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "You are using an unsupported web browser. The web vault may not function properly." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/nb/messages.json b/apps/web/src/locales/nb/messages.json index ea624075f31..caf22428259 100644 --- a/apps/web/src/locales/nb/messages.json +++ b/apps/web/src/locales/nb/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Oppdater nettleseren" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "Du bruker en ustøttet nettleser. Netthvelvet vil kanskje ikke fungere ordentlig." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/ne/messages.json b/apps/web/src/locales/ne/messages.json index 3ba281d33f4..345b8945460 100644 --- a/apps/web/src/locales/ne/messages.json +++ b/apps/web/src/locales/ne/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Update browser" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "You are using an unsupported web browser. The web vault may not function properly." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/nl/messages.json b/apps/web/src/locales/nl/messages.json index 0dc70affb8e..4e6e29704b6 100644 --- a/apps/web/src/locales/nl/messages.json +++ b/apps/web/src/locales/nl/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Leden herstellen" }, - "revokeMembersWarning": { - "message": "Leden met geclaimde en niet-geclailmde accounts krijgen verschillende resultaten bij intrekken:" - }, - "claimedAccountRevoke": { - "message": "Geclaimd account: Toegang tot Bitwarden-account intrekken" - }, - "unclaimedAccountRevoke": { - "message": "Niet-geclaild account: Toegang tot organisatiegegevens intrekken" - }, - "claimedAccount": { - "message": "Geclaimd account" - }, - "unclaimedAccount": { - "message": "Niet-geclaimd account" - }, - "restoreMembersInstructions": { - "message": "Om het account van een lid te herstellen, ga je naar het tabblad Ingetrokken. Het proces duurt een paar seconden om te voltooien en kan niet worden onderbroken of geannuleerd." - }, "cannotRestoreAccessError": { "message": "Kan organisatietoegang niet herstellen" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Webbrowser bijwerken" }, + "generatingRiskInsights": { + "message": "Je risico-inzichten genereren..." + }, "updateBrowserDesc": { "message": "Je maakt gebruik van webbrowser die we niet ondersteunen. De webkluis werkt mogelijk niet goed." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Code bankafschrift" }, + "importantNotice": { + "message": "Belangrijke mededeling" + }, + "setupTwoStepLogin": { + "message": "Tweestapsaanmelding instellen" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Vanaf februari 2025 stuurt Bitwarden een code naar het e-mailadres van je account om inloggen op nieuwe apparaten te verifiëren." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Je kunt tweestapsaanmelding instellen als een alternatieve manier om je account te beschermen of je e-mailadres te veranderen naar een waar je toegang toe hebt." + }, + "remindMeLater": { + "message": "Herinner me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Heb je betrouwbare toegang tot je e-mail, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Nee, dat heb ik niet" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Ja, ik heb betrouwbare toegang tot mijn e-mail" + }, + "turnOnTwoStepLogin": { + "message": "Tweestapsaanmelding inschakelen" + }, + "changeAcctEmail": { + "message": "E-mailadres van het account veranderen" + }, "removeMembers": { "message": "Leden verwijderen" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domein geverifieerd" + }, + "organizationNameMaxLength": { + "message": "Organisatienaam mag niet langer zijn dan 50 tekens." } } diff --git a/apps/web/src/locales/nn/messages.json b/apps/web/src/locales/nn/messages.json index 343b49a791b..7951e875fe8 100644 --- a/apps/web/src/locales/nn/messages.json +++ b/apps/web/src/locales/nn/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Update browser" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "You are using an unsupported web browser. The web vault may not function properly." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/or/messages.json b/apps/web/src/locales/or/messages.json index 88355860d4f..c5a77826c9e 100644 --- a/apps/web/src/locales/or/messages.json +++ b/apps/web/src/locales/or/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Update browser" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "You are using an unsupported web browser. The web vault may not function properly." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/pl/messages.json b/apps/web/src/locales/pl/messages.json index c35fc6e34ab..67d63ce9b33 100644 --- a/apps/web/src/locales/pl/messages.json +++ b/apps/web/src/locales/pl/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Aktualizuj przeglądarkę" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "Używasz nieobsługiwanej przeglądarki. Sejf internetowy może działać niewłaściwie." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/pt_BR/messages.json b/apps/web/src/locales/pt_BR/messages.json index 3883fbfe173..5461370c663 100644 --- a/apps/web/src/locales/pt_BR/messages.json +++ b/apps/web/src/locales/pt_BR/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restaurar membro" }, - "revokeMembersWarning": { - "message": "Membros com contas reivindicadas e não reivindicadas terão resultados diferentes quando forem removidos:" - }, - "claimedAccountRevoke": { - "message": "Conta criada: Revogar acesso à conta Bitwarden" - }, - "unclaimedAccountRevoke": { - "message": "Conta não reivindicada: Revogar acesso aos dados da organização" - }, - "claimedAccount": { - "message": "Conta reivindicada" - }, - "unclaimedAccount": { - "message": "Conta não reivindicada" - }, - "restoreMembersInstructions": { - "message": "Para restaurar a conta de um membro, vá para aba Revogado. O processo pode levar alguns segundos para ser concluído e não pode ser interrompido ou cancelado." - }, "cannotRestoreAccessError": { "message": "Não é possível restaurar acesso à organização" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Atualizar Navegador" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "Você está usando um navegador da Web não suportado. O cofre web pode não funcionar corretamente." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Código do descritor" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remover membro?" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/pt_PT/messages.json b/apps/web/src/locales/pt_PT/messages.json index a83ff3546e5..78206bc56a2 100644 --- a/apps/web/src/locales/pt_PT/messages.json +++ b/apps/web/src/locales/pt_PT/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restaurar membros" }, - "revokeMembersWarning": { - "message": "Os membros com contas reclamadas e não reclamadas terão resultados diferentes quando revogadas:" - }, - "claimedAccountRevoke": { - "message": "Conta reclamada: Revogar o acesso à conta do Bitwarden" - }, - "unclaimedAccountRevoke": { - "message": "Conta não reclamada: Revogar o acesso aos dados da organização" - }, - "claimedAccount": { - "message": "Conta reclamada" - }, - "unclaimedAccount": { - "message": "Conta não reclamada" - }, - "restoreMembersInstructions": { - "message": "Para restaurar a conta de um membro, aceda ao separador Revogado. O processo pode demorar alguns segundos a ser concluído e não pode ser interrompido ou cancelado." - }, "cannotRestoreAccessError": { "message": "Não é possível restaurar o acesso à organização" }, @@ -964,7 +946,7 @@ "message": "Nível de acesso" }, "accessing": { - "message": "A aceder" + "message": "A aceder a" }, "loggedOut": { "message": "Sessão terminada" @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Atualizar navegador" }, + "generatingRiskInsights": { + "message": "A gerar as suas perceções de riscos..." + }, "updateBrowserDesc": { "message": "Está a utilizar um navegador web não suportado. O cofre web pode não funcionar corretamente." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Código descritor" }, + "importantNotice": { + "message": "Aviso importante" + }, + "setupTwoStepLogin": { + "message": "Definir a verificação de dois passos" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "O Bitwarden enviará um código para o e-mail da sua conta para verificar as credenciais de novos dispositivos a partir de fevereiro de 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Pode configurar a verificação de dois passos como forma alternativa de proteger a sua conta ou alterar o seu e-mail para um a que possa aceder." + }, + "remindMeLater": { + "message": "Lembrar-me mais tarde" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Tem um acesso fiável ao seu e-mail, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Não, não tenho" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Sim, consigo aceder de forma fiável ao meu e-mail" + }, + "turnOnTwoStepLogin": { + "message": "Ativar a verificação de dois passos" + }, + "changeAcctEmail": { + "message": "Alterar o e-mail da conta" + }, "removeMembers": { "message": "Remover membros" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domínio reivindicado" + }, + "organizationNameMaxLength": { + "message": "O nome da organização não pode exceder 50 caracteres." } } diff --git a/apps/web/src/locales/ro/messages.json b/apps/web/src/locales/ro/messages.json index 9e646ae0f81..5fdb6ab5e68 100644 --- a/apps/web/src/locales/ro/messages.json +++ b/apps/web/src/locales/ro/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Actualizare browser" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "Utilizați un browser nesuportat. Seiful web ar putea să nu funcționeze corect." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/ru/messages.json b/apps/web/src/locales/ru/messages.json index c572a35876b..fd309722f46 100644 --- a/apps/web/src/locales/ru/messages.json +++ b/apps/web/src/locales/ru/messages.json @@ -35,26 +35,8 @@ "restoreMembers": { "message": "Восстановление пользователей" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { - "message": "Cannot restore organization access" + "message": "Невозможно восстановить доступ к организации" }, "allApplicationsWithCount": { "message": "Все приложения ($COUNT$)", @@ -1596,7 +1578,7 @@ "message": "Ограничено аккаунтом" }, "passwordProtected": { - "message": "Пароль защищен" + "message": "Защищено паролем" }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "\"Пароль к файлу\" и \"Подтверждение пароля к файлу\" не совпадают." @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Обновить браузер" }, + "generatingRiskInsights": { + "message": "Генерирование информации о рисках..." + }, "updateBrowserDesc": { "message": "Вы используете неподдерживаемый браузер. Веб-хранилище может работать некорректно." }, @@ -5650,10 +5635,10 @@ "message": "Исключено, не применимо для данного действия." }, "nonCompliantMembersTitle": { - "message": "Non-compliant members" + "message": "Участники, не отвечающие требованиям" }, "nonCompliantMembersError": { - "message": "Members that are non-compliant with the Single organization or Two-step login policy cannot be restored until they adhere to the policy requirements" + "message": "Участники, не соответствующие требованиям политики единой организации или двухэтапной аутентификации, не могут быть восстановлены, пока они не будут соответствовать требованиям политики" }, "fingerprint": { "message": "Отпечаток" @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Код дескриптора" }, + "importantNotice": { + "message": "Важное уведомление" + }, + "setupTwoStepLogin": { + "message": "Настроить двухэтапную аутентификацию" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Начиная с февраля 2025 года Bitwarden будет отправлять код на электронную почту вашего аккаунта для подтверждения авторизации с новых устройств." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "В качестве альтернативного способа защиты учетной записи вы можете настроить двухэтапную аутентификацию или сменить электронную почту на ту, к которой вы можете получить доступ." + }, + "remindMeLater": { + "message": "Напомнить позже" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Есть ли у вас надежный доступ к электронной почте $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Нет, не знаю" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Да, я имею надежный доступ к своей электронной почте" + }, + "turnOnTwoStepLogin": { + "message": "Включить двухэтапную аутентификацию" + }, + "changeAcctEmail": { + "message": "Изменить email аккаунта" + }, "removeMembers": { "message": "Удалить участников" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Домен зарегистрирован" + }, + "organizationNameMaxLength": { + "message": "Название организации не может превышать 50 символов." } } diff --git a/apps/web/src/locales/si/messages.json b/apps/web/src/locales/si/messages.json index f60e366e794..3dfac52754f 100644 --- a/apps/web/src/locales/si/messages.json +++ b/apps/web/src/locales/si/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Update browser" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "You are using an unsupported web browser. The web vault may not function properly." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/sk/messages.json b/apps/web/src/locales/sk/messages.json index 16a36a194fb..8533d50b304 100644 --- a/apps/web/src/locales/sk/messages.json +++ b/apps/web/src/locales/sk/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Obnoviť členov" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Nie je možné obnoviť prístup do organizácie" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Aktualizovať prehliadač" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "Používate nepodporovaný prehliadač. Webový trezor nemusí úplne fungovať." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Kód výpisu" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Odstrániť členov" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Doména privlastnená" + }, + "organizationNameMaxLength": { + "message": "Meno organizácie nemôže mať viac ako 50 znakov." } } diff --git a/apps/web/src/locales/sl/messages.json b/apps/web/src/locales/sl/messages.json index c155ab58ab1..55aceaecf2b 100644 --- a/apps/web/src/locales/sl/messages.json +++ b/apps/web/src/locales/sl/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Update browser" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "You are using an unsupported web browser. The web vault may not function properly." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/sr/messages.json b/apps/web/src/locales/sr/messages.json index ca1bae3c358..17e82485f8c 100644 --- a/apps/web/src/locales/sr/messages.json +++ b/apps/web/src/locales/sr/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Врати чланове" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Није могуће повратити приступ организацији" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Ажурирајте Претраживач" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "Користите неподржани веб прегледач. Веб сеф можда неће правилно функционисати." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/sr_CS/messages.json b/apps/web/src/locales/sr_CS/messages.json index eeaf87eef32..6dd5fed6736 100644 --- a/apps/web/src/locales/sr_CS/messages.json +++ b/apps/web/src/locales/sr_CS/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Update browser" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "You are using an unsupported web browser. The web vault may not function properly." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/sv/messages.json b/apps/web/src/locales/sv/messages.json index ff7f2f3a2df..b4d2fb7aa8d 100644 --- a/apps/web/src/locales/sv/messages.json +++ b/apps/web/src/locales/sv/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Uppdatera webbläsare" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "Du använder en webbläsare som inte stöds. Webbvalvet kanske inte fungerar som det ska." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/te/messages.json b/apps/web/src/locales/te/messages.json index 88355860d4f..c5a77826c9e 100644 --- a/apps/web/src/locales/te/messages.json +++ b/apps/web/src/locales/te/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Update browser" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "You are using an unsupported web browser. The web vault may not function properly." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/th/messages.json b/apps/web/src/locales/th/messages.json index 3a8bb79b20a..5d2da786d83 100644 --- a/apps/web/src/locales/th/messages.json +++ b/apps/web/src/locales/th/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Update browser" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "You are using an unsupported web browser. The web vault may not function properly." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/tr/messages.json b/apps/web/src/locales/tr/messages.json index c822b90ef77..fb821407a16 100644 --- a/apps/web/src/locales/tr/messages.json +++ b/apps/web/src/locales/tr/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Tarayıcıyı güncelle" }, + "generatingRiskInsights": { + "message": "Risk içgörüleriniz oluşturuluyor..." + }, "updateBrowserDesc": { "message": "Desteklenmeyen bir web tarayıcısı kullanıyorsunuz. Web kasası düzgün çalışmayabilir." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Önemli uyarı" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Daha sonra hatırlat" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/uk/messages.json b/apps/web/src/locales/uk/messages.json index 6bd151f8a41..89d538e1a2a 100644 --- a/apps/web/src/locales/uk/messages.json +++ b/apps/web/src/locales/uk/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Відновити учасників" }, - "revokeMembersWarning": { - "message": "Учасники із заявленими та не заявленими обліковими записами матимуть різні результати після відкликання:" - }, - "claimedAccountRevoke": { - "message": "Заявлений обліковий запис: відкликати доступ до облікового запису Bitwarden" - }, - "unclaimedAccountRevoke": { - "message": "Не заявлений обліковий запис: відкликати доступ до даних організації" - }, - "claimedAccount": { - "message": "Заявлений обліковий запис" - }, - "unclaimedAccount": { - "message": "Не заявлений обліковий запис" - }, - "restoreMembersInstructions": { - "message": "Щоб відновити обліковий запис учасника, перейдіть на вкладку \"Відкликані\". Процес може тривати декілька секунд, і його не можна перервати чи скасувати." - }, "cannotRestoreAccessError": { "message": "Не вдається відновити доступ до організації" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Оновити браузер" }, + "generatingRiskInsights": { + "message": "Генерується інформація щодо ризиків..." + }, "updateBrowserDesc": { "message": "Ви використовуєте непідтримуваний браузер. Вебсховище може працювати неправильно." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Код дескриптора" }, + "importantNotice": { + "message": "Важлива інформація" + }, + "setupTwoStepLogin": { + "message": "Налаштувати двоетапну перевірку" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden надсилатиме код підтвердження на електронну пошту вашого облікового запису під час входу з нових пристроїв, починаючи з лютого 2025 року." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Ви можете налаштувати двоетапну перевірку як альтернативний спосіб захисту свого облікового запису, або змінити електронну пошту на таку, до якої ви маєте доступ." + }, + "remindMeLater": { + "message": "Нагадати пізніше" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Ви маєте постійний доступ до своєї електронної пошти $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Ні, не маю" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Так, я маю постійний доступ до своєї електронної пошти" + }, + "turnOnTwoStepLogin": { + "message": "Увімкнути двоетапну перевірку" + }, + "changeAcctEmail": { + "message": "Змінити адресу е-пошти" + }, "removeMembers": { "message": "Вилучити учасників" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Домен заявлено" + }, + "organizationNameMaxLength": { + "message": "Назва організації не може перевищувати 50 символів." } } diff --git a/apps/web/src/locales/vi/messages.json b/apps/web/src/locales/vi/messages.json index 1f2e9ff4226..c8300de6fbf 100644 --- a/apps/web/src/locales/vi/messages.json +++ b/apps/web/src/locales/vi/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "Members with claimed and unclaimed accounts will have different results when revoked:" - }, - "claimedAccountRevoke": { - "message": "Claimed account: Revoke access to Bitwarden account" - }, - "unclaimedAccountRevoke": { - "message": "Unclaimed account: Revoke access to organization data" - }, - "claimedAccount": { - "message": "Claimed account" - }, - "unclaimedAccount": { - "message": "Unclaimed account" - }, - "restoreMembersInstructions": { - "message": "To restore a member's account, go to the Revoked tab. The process may take a few seconds to complete and cannot be interrupted or canceled." - }, "cannotRestoreAccessError": { "message": "Cannot restore organization access" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "Update browser" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "You are using an unsupported web browser. The web vault may not function properly." }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } diff --git a/apps/web/src/locales/zh_CN/messages.json b/apps/web/src/locales/zh_CN/messages.json index e9d8ff53e8b..0dad065f574 100644 --- a/apps/web/src/locales/zh_CN/messages.json +++ b/apps/web/src/locales/zh_CN/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "恢复成员" }, - "revokeMembersWarning": { - "message": "已声明和未声明账户的成员在被撤销时将有不同的结果:" - }, - "claimedAccountRevoke": { - "message": "已声明账户:撤销对 Bitwarden 账户的访问权限" - }, - "unclaimedAccountRevoke": { - "message": "未声明账户:撤销对组织数据的访问权限" - }, - "claimedAccount": { - "message": "已声明账户" - }, - "unclaimedAccount": { - "message": "未声明账户" - }, - "restoreMembersInstructions": { - "message": "要恢复成员账户,请转到「已撤销」标签页。该过程可能需要几秒钟才能完成,并且无法中断或取消。" - }, "cannotRestoreAccessError": { "message": "无法恢复组织访问权限" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "更新浏览器" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "您使用的是不受支持的 Web 浏览器。网页密码库可能无法正常运行。" }, @@ -9741,7 +9726,7 @@ "message": "对于如密码之类的敏感数据,请使用隐藏型字段" }, "checkBoxHelpText": { - "message": "如果您想自动勾选表单复选框(例如记住电子邮件地址),请使用复选框" + "message": "如果您想自动勾选表单复选框(例如记住电子邮件地址),请使用复选框型" }, "linkedHelpText": { "message": "当您处理特定网站的自动填充问题时,请使用链接型字段。" @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "描述符代码" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "移除成员" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "域名已声明" + }, + "organizationNameMaxLength": { + "message": "组织名称不能超过 50 个字符。" } } diff --git a/apps/web/src/locales/zh_TW/messages.json b/apps/web/src/locales/zh_TW/messages.json index cf3fa970434..bf15ea2a20a 100644 --- a/apps/web/src/locales/zh_TW/messages.json +++ b/apps/web/src/locales/zh_TW/messages.json @@ -35,24 +35,6 @@ "restoreMembers": { "message": "Restore members" }, - "revokeMembersWarning": { - "message": "已被索取及未被索取帳號之成員將會在帳號註銷後有不同結果:" - }, - "claimedAccountRevoke": { - "message": "已被索取之帳號:註銷 Bitwarden 帳號存取權" - }, - "unclaimedAccountRevoke": { - "message": "未被索取之帳號:註銷組織資料存取權" - }, - "claimedAccount": { - "message": "已被索取之帳號" - }, - "unclaimedAccount": { - "message": "未被索取之帳號" - }, - "restoreMembersInstructions": { - "message": "去註銷分頁以還原成員之帳號。這個過程可能會花上幾秒鐘且無法被中斷或取消。" - }, "cannotRestoreAccessError": { "message": "無法還原組織存取" }, @@ -3900,6 +3882,9 @@ "updateBrowser": { "message": "更新瀏覽器" }, + "generatingRiskInsights": { + "message": "Generating your risk insights..." + }, "updateBrowserDesc": { "message": "未支援您使用的瀏覽器。網頁版密碼庫可能無法正常運作。" }, @@ -9899,6 +9884,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, @@ -9983,5 +10004,8 @@ }, "domainClaimed": { "message": "Domain claimed" + }, + "organizationNameMaxLength": { + "message": "Organization name cannot exceed 50 characters." } } From 8caadacfbc79ac34fd14242c216d1a723c261890 Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Fri, 20 Dec 2024 13:54:52 +0100 Subject: [PATCH 08/75] [PM-16217] Remove wasm timeout (#12476) Remove the WASM timeout logic and supported$. --- .../browser/src/background/main.background.ts | 18 ------ .../sdk/browser-sdk-client-factory.ts | 31 +--------- apps/browser/src/popup/app.component.ts | 30 +--------- .../service-container/service-container.ts | 15 ----- apps/desktop/src/app/app.component.ts | 37 +----------- apps/web/src/app/app.component.ts | 30 +--------- .../src/services/jslib-services.module.ts | 1 - .../platform/abstractions/sdk/sdk.service.ts | 7 --- .../services/sdk/default-sdk.service.spec.ts | 4 -- .../services/sdk/default-sdk.service.ts | 56 +------------------ 10 files changed, 7 insertions(+), 222 deletions(-) diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 616e18601af..aaee59ee3fa 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -739,7 +739,6 @@ export default class MainBackground { this.accountService, this.kdfConfigService, this.keyService, - this.apiService, ); this.passwordStrengthService = new PasswordStrengthService(); @@ -1313,23 +1312,6 @@ export default class MainBackground { await this.initOverlayAndTabsBackground(); - if (flagEnabled("sdk")) { - // Warn if the SDK for some reason can't be initialized - let supported = false; - let error: Error; - try { - supported = await firstValueFrom(this.sdkService.supported$); - } catch (e) { - error = e; - } - - if (!supported) { - this.sdkService - .failedToInitialize("background", error) - .catch((e) => this.logService.error(e)); - } - } - return new Promise((resolve) => { setTimeout(async () => { await this.refreshBadge(); diff --git a/apps/browser/src/platform/services/sdk/browser-sdk-client-factory.ts b/apps/browser/src/platform/services/sdk/browser-sdk-client-factory.ts index c9d5d726a6a..a2f0c78cd52 100644 --- a/apps/browser/src/platform/services/sdk/browser-sdk-client-factory.ts +++ b/apps/browser/src/platform/services/sdk/browser-sdk-client-factory.ts @@ -2,7 +2,6 @@ // @ts-strict-ignore import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { SdkClientFactory } from "@bitwarden/common/platform/abstractions/sdk/sdk-client-factory"; -import { RecoverableSDKError } from "@bitwarden/common/platform/services/sdk/default-sdk.service"; import type { BitwardenClient } from "@bitwarden/sdk-internal"; import { BrowserApi } from "../../browser/browser-api"; @@ -72,42 +71,14 @@ export class BrowserSdkClientFactory implements SdkClientFactory { ...args: ConstructorParameters ): Promise { const startTime = performance.now(); - try { - await loadWithTimeout(); - } catch (error) { - throw new Error(`Failed to load: ${error.message}`); - } + await load(); const endTime = performance.now(); - const elapsed = Math.round((endTime - startTime) / 1000); const instance = (globalThis as any).init_sdk(...args); this.logService.info("WASM SDK loaded in", Math.round(endTime - startTime), "ms"); - // If it takes 3 seconds or more to load, we want to capture it. - if (elapsed >= 3) { - throw new RecoverableSDKError(instance, elapsed); - } - return instance; } } - -const loadWithTimeout = async () => { - return new Promise((resolve, reject) => { - const timer = setTimeout(() => { - reject(new Error("Operation timed out after 10 second")); - }, 10000); - - load() - .then(() => { - clearTimeout(timer); - resolve(); - }) - .catch((error) => { - clearTimeout(timer); - reject(error); - }); - }); -}; diff --git a/apps/browser/src/popup/app.component.ts b/apps/browser/src/popup/app.component.ts index cbdbed51db7..248050ed61c 100644 --- a/apps/browser/src/popup/app.component.ts +++ b/apps/browser/src/popup/app.component.ts @@ -1,7 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { ChangeDetectorRef, Component, NgZone, OnDestroy, OnInit, inject } from "@angular/core"; -import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { NavigationEnd, Router, RouterOutlet } from "@angular/router"; import { Subject, takeUntil, firstValueFrom, concatMap, filter, tap } from "rxjs"; @@ -11,9 +10,7 @@ import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { AnimationControlService } from "@bitwarden/common/platform/abstractions/animation-control.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 { SdkService } from "@bitwarden/common/platform/abstractions/sdk/sdk.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { MessageListener } from "@bitwarden/common/platform/messaging"; import { UserId } from "@bitwarden/common/types/guid"; @@ -25,7 +22,6 @@ import { ToastService, } from "@bitwarden/components"; -import { flagEnabled } from "../platform/flags"; import { PopupCompactModeService } from "../platform/popup/layout/popup-compact-mode.service"; import { PopupWidthService } from "../platform/popup/layout/popup-width.service"; import { PopupViewCacheService } from "../platform/popup/view-cache/popup-view-cache.service"; @@ -72,31 +68,7 @@ export class AppComponent implements OnInit, OnDestroy { private toastService: ToastService, private accountService: AccountService, private animationControlService: AnimationControlService, - private logService: LogService, - private sdkService: SdkService, - ) { - if (flagEnabled("sdk")) { - // Warn if the SDK for some reason can't be initialized - this.sdkService.supported$.pipe(takeUntilDestroyed()).subscribe({ - next: (supported) => { - if (!supported) { - this.logService.debug("SDK is not supported"); - this.sdkService - .failedToInitialize("popup", undefined) - .catch((e) => this.logService.error(e)); - } else { - this.logService.debug("SDK is supported"); - } - }, - error: (e: unknown) => { - this.sdkService - .failedToInitialize("popup", e as Error) - .catch((e) => this.logService.error(e)); - this.logService.error(e); - }, - }); - } - } + ) {} async ngOnInit() { initPopupClosedListener(); diff --git a/apps/cli/src/service-container/service-container.ts b/apps/cli/src/service-container/service-container.ts index 91b75a14ff6..2afbae0782f 100644 --- a/apps/cli/src/service-container/service-container.ts +++ b/apps/cli/src/service-container/service-container.ts @@ -550,7 +550,6 @@ export class ServiceContainer { this.accountService, this.kdfConfigService, this.keyService, - this.apiService, customUserAgent, ); @@ -864,19 +863,5 @@ export class ServiceContainer { } this.inited = true; - - if (flagEnabled("sdk")) { - // Warn if the SDK for some reason can't be initialized - let supported = false; - try { - supported = await firstValueFrom(this.sdkService.supported$); - } catch (e) { - // Do nothing. - } - - if (!supported) { - this.sdkService.failedToInitialize("cli").catch((e) => this.logService.error(e)); - } - } } } diff --git a/apps/desktop/src/app/app.component.ts b/apps/desktop/src/app/app.component.ts index 5443285da25..5fefbf9ddff 100644 --- a/apps/desktop/src/app/app.component.ts +++ b/apps/desktop/src/app/app.component.ts @@ -10,19 +10,8 @@ import { ViewChild, ViewContainerRef, } from "@angular/core"; -import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { Router } from "@angular/router"; -import { - catchError, - filter, - firstValueFrom, - map, - of, - Subject, - takeUntil, - timeout, - withLatestFrom, -} from "rxjs"; +import { filter, firstValueFrom, map, Subject, takeUntil, timeout, withLatestFrom } from "rxjs"; import { CollectionService } from "@bitwarden/admin-console/common"; import { ModalRef } from "@bitwarden/angular/components/modal/modal.ref"; @@ -52,7 +41,6 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { SdkService } from "@bitwarden/common/platform/abstractions/sdk/sdk.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { SystemService } from "@bitwarden/common/platform/abstractions/system.service"; import { clearCaches } from "@bitwarden/common/platform/misc/sequentialize"; @@ -70,7 +58,6 @@ import { KeyService, BiometricStateService } from "@bitwarden/key-management"; import { DeleteAccountComponent } from "../auth/delete-account.component"; import { MenuAccount, MenuUpdateRequest } from "../main/menu/menu.updater"; -import { flagEnabled } from "../platform/flags"; import { PremiumComponent } from "../vault/app/accounts/premium.component"; import { FolderAddEditComponent } from "../vault/app/vault/folder-add-edit.component"; @@ -167,28 +154,8 @@ export class AppComponent implements OnInit, OnDestroy { private biometricStateService: BiometricStateService, private stateEventRunnerService: StateEventRunnerService, private accountService: AccountService, - private sdkService: SdkService, private organizationService: OrganizationService, - ) { - if (flagEnabled("sdk")) { - // Warn if the SDK for some reason can't be initialized - this.sdkService.supported$ - .pipe( - takeUntilDestroyed(), - catchError(() => { - return of(false); - }), - ) - .subscribe((supported) => { - if (!supported) { - this.logService.debug("SDK is not supported"); - this.sdkService.failedToInitialize("desktop").catch((e) => this.logService.error(e)); - } else { - this.logService.debug("SDK is supported"); - } - }); - } - } + ) {} ngOnInit() { this.accountService.activeAccount$.pipe(takeUntil(this.destroy$)).subscribe((account) => { diff --git a/apps/web/src/app/app.component.ts b/apps/web/src/app/app.component.ts index 1075655af9e..16c783f3a5a 100644 --- a/apps/web/src/app/app.component.ts +++ b/apps/web/src/app/app.component.ts @@ -2,10 +2,9 @@ // @ts-strict-ignore import { DOCUMENT } from "@angular/common"; import { Component, Inject, NgZone, OnDestroy, OnInit } from "@angular/core"; -import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { NavigationEnd, Router } from "@angular/router"; import * as jq from "jquery"; -import { Subject, filter, firstValueFrom, map, takeUntil, timeout, catchError, of } from "rxjs"; +import { Subject, filter, firstValueFrom, map, takeUntil, timeout } from "rxjs"; import { CollectionService } from "@bitwarden/admin-console/common"; import { EventUploadService } from "@bitwarden/common/abstractions/event/event-upload.service"; @@ -22,9 +21,7 @@ import { ProcessReloadServiceAbstraction } from "@bitwarden/common/key-managemen import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { SdkService } from "@bitwarden/common/platform/abstractions/sdk/sdk.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { StateEventRunnerService } from "@bitwarden/common/platform/state"; import { SyncService } from "@bitwarden/common/platform/sync"; @@ -34,8 +31,6 @@ import { DialogService, ToastService } from "@bitwarden/components"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy"; import { KeyService, BiometricStateService } from "@bitwarden/key-management"; -import { flagEnabled } from "../utils/flags"; - import { PolicyListService } from "./admin-console/core/policy-list.service"; import { DisableSendPolicy, @@ -93,29 +88,8 @@ export class AppComponent implements OnDestroy, OnInit { private stateEventRunnerService: StateEventRunnerService, private organizationService: InternalOrganizationServiceAbstraction, private accountService: AccountService, - private logService: LogService, - private sdkService: SdkService, private processReloadService: ProcessReloadServiceAbstraction, - ) { - if (flagEnabled("sdk")) { - // Warn if the SDK for some reason can't be initialized - this.sdkService.supported$ - .pipe( - takeUntilDestroyed(), - catchError(() => { - return of(false); - }), - ) - .subscribe((supported) => { - if (!supported) { - this.logService.debug("SDK is not supported"); - this.sdkService.failedToInitialize("web").catch((e) => this.logService.error(e)); - } else { - this.logService.debug("SDK is supported"); - } - }); - } - } + ) {} ngOnInit() { this.i18nService.locale$.pipe(takeUntil(this.destroy$)).subscribe((locale) => { diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 0765fd8e4c6..688507099de 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -1384,7 +1384,6 @@ const safeProviders: SafeProvider[] = [ AccountServiceAbstraction, KdfConfigService, KeyServiceAbstraction, - ApiServiceAbstraction, ], }), safeProvider({ diff --git a/libs/common/src/platform/abstractions/sdk/sdk.service.ts b/libs/common/src/platform/abstractions/sdk/sdk.service.ts index ec606896408..d44d38c36ab 100644 --- a/libs/common/src/platform/abstractions/sdk/sdk.service.ts +++ b/libs/common/src/platform/abstractions/sdk/sdk.service.ts @@ -7,11 +7,6 @@ import { BitwardenClient } from "@bitwarden/sdk-internal"; import { UserId } from "../../../types/guid"; export abstract class SdkService { - /** - * Check if the SDK is supported in the current environment. - */ - supported$: Observable; - /** * Retrieve the version of the SDK. */ @@ -35,6 +30,4 @@ export abstract class SdkService { * @param userId */ abstract userClient$(userId: UserId): Observable; - - abstract failedToInitialize(category: string, error?: Error): Promise; } diff --git a/libs/common/src/platform/services/sdk/default-sdk.service.spec.ts b/libs/common/src/platform/services/sdk/default-sdk.service.spec.ts index de8b079621a..6593095b325 100644 --- a/libs/common/src/platform/services/sdk/default-sdk.service.spec.ts +++ b/libs/common/src/platform/services/sdk/default-sdk.service.spec.ts @@ -4,7 +4,6 @@ import { BehaviorSubject, firstValueFrom, of } from "rxjs"; import { KdfConfigService, KeyService, PBKDF2KdfConfig } from "@bitwarden/key-management"; import { BitwardenClient } from "@bitwarden/sdk-internal"; -import { ApiService } from "../../../abstractions/api.service"; import { AccountInfo, AccountService } from "../../../auth/abstractions/account.service"; import { UserId } from "../../../types/guid"; import { UserKey } from "../../../types/key"; @@ -24,7 +23,6 @@ describe("DefaultSdkService", () => { let accountService!: MockProxy; let kdfConfigService!: MockProxy; let keyService!: MockProxy; - let apiService!: MockProxy; let service!: DefaultSdkService; let mockClient!: MockProxy; @@ -36,7 +34,6 @@ describe("DefaultSdkService", () => { accountService = mock(); kdfConfigService = mock(); keyService = mock(); - apiService = mock(); // Can't use `of(mock())` for some reason environmentService.environment$ = new BehaviorSubject(mock()); @@ -48,7 +45,6 @@ describe("DefaultSdkService", () => { accountService, kdfConfigService, keyService, - apiService, ); mockClient = mock(); diff --git a/libs/common/src/platform/services/sdk/default-sdk.service.ts b/libs/common/src/platform/services/sdk/default-sdk.service.ts index be6f99ab040..bf593aecf7d 100644 --- a/libs/common/src/platform/services/sdk/default-sdk.service.ts +++ b/libs/common/src/platform/services/sdk/default-sdk.service.ts @@ -3,7 +3,6 @@ import { combineLatest, concatMap, - firstValueFrom, Observable, shareReplay, map, @@ -21,7 +20,6 @@ import { DeviceType as SdkDeviceType, } from "@bitwarden/sdk-internal"; -import { ApiService } from "../../../abstractions/api.service"; import { EncryptedOrganizationKeyData } from "../../../admin-console/models/data/encrypted-organization-key.data"; import { AccountInfo, AccountService } from "../../../auth/abstractions/account.service"; import { DeviceType } from "../../../enums/device-type.enum"; @@ -34,43 +32,17 @@ import { SdkService } from "../../abstractions/sdk/sdk.service"; import { compareValues } from "../../misc/compare-values"; import { EncryptedString } from "../../models/domain/enc-string"; -export class RecoverableSDKError extends Error { - sdk: BitwardenClient; - timeout: number; - - constructor(sdk: BitwardenClient, timeout: number) { - super(`SDK took ${timeout}s to initialize`); - - this.sdk = sdk; - this.timeout = timeout; - } -} - export class DefaultSdkService implements SdkService { private sdkClientCache = new Map>(); client$ = this.environmentService.environment$.pipe( concatMap(async (env) => { const settings = this.toSettings(env); - try { - return await this.sdkClientFactory.createSdkClient(settings, LogLevel.Info); - } catch (e) { - if (e instanceof RecoverableSDKError) { - await this.failedToInitialize("sdk", e); - return e.sdk; - } - throw e; - } + return await this.sdkClientFactory.createSdkClient(settings, LogLevel.Info); }), shareReplay({ refCount: true, bufferSize: 1 }), ); - supported$ = this.client$.pipe( - concatMap(async (client) => { - return client.echo("bitwarden wasm!") === "bitwarden wasm!"; - }), - ); - version$ = this.client$.pipe( map((client) => client.version()), catchError(() => "Unsupported"), @@ -83,7 +55,6 @@ export class DefaultSdkService implements SdkService { private accountService: AccountService, private kdfConfigService: KdfConfigService, private keyService: KeyService, - private apiService: ApiService, // Yes we shouldn't import ApiService, but it's temporary private userAgent: string = null, ) {} @@ -155,31 +126,6 @@ export class DefaultSdkService implements SdkService { return client$; } - async failedToInitialize(category: string, error?: Error): Promise { - // Only log on cloud instances - if ( - this.platformUtilsService.isDev() || - !(await firstValueFrom(this.environmentService.environment$)).isCloud - ) { - return; - } - - return this.apiService.send( - "POST", - "/wasm-debug", - { - category: category, - error: error?.message, - }, - false, - false, - null, - (headers) => { - headers.append("SDK-Version", "1.0.0"); - }, - ); - } - private async initializeClient( client: BitwardenClient, account: AccountInfo, From 2041799174ddf23948fa8aff1b010355fd5ddce7 Mon Sep 17 00:00:00 2001 From: Daniel James Smith <2670567+djsmith85@users.noreply.github.com> Date: Fri, 20 Dec 2024 15:18:35 +0100 Subject: [PATCH 09/75] Fix Crowdin push (#12500) Related to https://github.com/bitwarden/clients/pull/11961 Co-authored-by: Daniel James Smith --- .github/workflows/build-desktop.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml index bc9bdec396a..2a59dc28fc9 100644 --- a/.github/workflows/build-desktop.yml +++ b/.github/workflows/build-desktop.yml @@ -1433,7 +1433,7 @@ jobs: crowdin-push: name: Crowdin Push - if: github.ref == 'refs/heads/main' + if: github.event_name != 'pull_request_target' && github.ref == 'refs/heads/main' needs: - linux - windows From 1d335bb16400ce8b452593d519ccbae2788bf46b Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Fri, 20 Dec 2024 15:20:23 +0100 Subject: [PATCH 10/75] [PM-16262] Make `getEnvironment` observable and use it in SdkService (#12501) * feat: re-implement getEnvironment as an observable * feat: deprecate `getEnvironment` * fix: use correct environment function in SdkService * fix: test --- .../abstractions/environment.service.ts | 5 +++ .../default-environment.service.spec.ts | 14 ++++----- .../services/default-environment.service.ts | 31 ++++++++++++------- .../services/sdk/default-sdk.service.spec.ts | 3 ++ .../services/sdk/default-sdk.service.ts | 2 +- 5 files changed, 35 insertions(+), 20 deletions(-) diff --git a/libs/common/src/platform/abstractions/environment.service.ts b/libs/common/src/platform/abstractions/environment.service.ts index 0293d68903c..8d32fc4231d 100644 --- a/libs/common/src/platform/abstractions/environment.service.ts +++ b/libs/common/src/platform/abstractions/environment.service.ts @@ -128,5 +128,10 @@ export abstract class EnvironmentService { /** * Get the environment from state. Useful if you need to get the environment for another user. */ + abstract getEnvironment$(userId?: string): Observable; + + /** + * @deprecated Use {@link getEnvironment$} instead. + */ abstract getEnvironment(userId?: string): Promise; } diff --git a/libs/common/src/platform/services/default-environment.service.spec.ts b/libs/common/src/platform/services/default-environment.service.spec.ts index 7d266e93fc3..870f887c160 100644 --- a/libs/common/src/platform/services/default-environment.service.spec.ts +++ b/libs/common/src/platform/services/default-environment.service.spec.ts @@ -314,7 +314,7 @@ describe("EnvironmentService", () => { await switchUser(testUser); - const env = await sut.getEnvironment(); + const env = await firstValueFrom(sut.getEnvironment$()); expect(env.getHostname()).toBe(expectedHost); }); @@ -325,7 +325,7 @@ describe("EnvironmentService", () => { setGlobalData(region, new EnvironmentUrls()); setUserData(Region.US, new EnvironmentUrls()); - const env = await sut.getEnvironment(); + const env = await firstValueFrom(sut.getEnvironment$()); expect(env.getHostname()).toBe(expectedHost); }); @@ -338,7 +338,7 @@ describe("EnvironmentService", () => { setGlobalData(region, new EnvironmentUrls()); setUserData(Region.US, new EnvironmentUrls()); - const env = await sut.getEnvironment(testUser); + const env = await firstValueFrom(sut.getEnvironment$(testUser)); expect(env.getHostname()).toBe(expectedHost); }, ); @@ -355,7 +355,7 @@ describe("EnvironmentService", () => { await switchUser(testUser); - const env = await sut.getEnvironment(alternateTestUser); + const env = await firstValueFrom(sut.getEnvironment$(alternateTestUser)); expect(env.getHostname()).toBe(expectedHost); }, ); @@ -366,7 +366,7 @@ describe("EnvironmentService", () => { setGlobalData(Region.SelfHosted, globalSelfHostUrls); setUserData(Region.EU, new EnvironmentUrls()); - const env = await sut.getEnvironment(); + const env = await firstValueFrom(sut.getEnvironment$()); expect(env.getHostname()).toBe("base.example.com"); }); @@ -377,7 +377,7 @@ describe("EnvironmentService", () => { setGlobalData(Region.SelfHosted, globalSelfHostUrls); setUserData(Region.EU, new EnvironmentUrls()); - const env = await sut.getEnvironment(); + const env = await firstValueFrom(sut.getEnvironment$()); expect(env.getHostname()).toBe("vault.example.com"); }); @@ -391,7 +391,7 @@ describe("EnvironmentService", () => { await switchUser(testUser); - const env = await sut.getEnvironment(alternateTestUser); + const env = await firstValueFrom(sut.getEnvironment$(alternateTestUser)); expect(env.getHostname()).toBe("base.example.com"); }); }); diff --git a/libs/common/src/platform/services/default-environment.service.ts b/libs/common/src/platform/services/default-environment.service.ts index 69d990ce691..ac3e39b2bb3 100644 --- a/libs/common/src/platform/services/default-environment.service.ts +++ b/libs/common/src/platform/services/default-environment.service.ts @@ -271,23 +271,30 @@ export class DefaultEnvironmentService implements EnvironmentService { } } - async getEnvironment(userId?: UserId): Promise { + getEnvironment$(userId?: UserId): Observable { if (userId == null) { - return await firstValueFrom(this.environment$); + return this.environment$; } - const state = await this.getEnvironmentState(userId); - return this.buildEnvironment(state.region, state.urls); + return this.activeAccountId$.pipe( + switchMap((activeUserId) => { + // Previous rules dictated that we only get from user scoped state if there is an active user. + if (activeUserId == null) { + return this.globalState.state$; + } + return this.stateProvider.getUser(userId ?? activeUserId, USER_ENVIRONMENT_KEY).state$; + }), + map((state) => { + return this.buildEnvironment(state?.region, state?.urls); + }), + ); } - private async getEnvironmentState(userId: UserId | null) { - // Previous rules dictated that we only get from user scoped state if there is an active user. - const activeUserId = await firstValueFrom(this.activeAccountId$); - return activeUserId == null - ? await firstValueFrom(this.globalState.state$) - : await firstValueFrom( - this.stateProvider.getUser(userId ?? activeUserId, USER_ENVIRONMENT_KEY).state$, - ); + /** + * @deprecated Use getEnvironment$ instead. + */ + async getEnvironment(userId?: UserId): Promise { + return firstValueFrom(this.getEnvironment$(userId)); } async seedUserEnvironment(userId: UserId) { diff --git a/libs/common/src/platform/services/sdk/default-sdk.service.spec.ts b/libs/common/src/platform/services/sdk/default-sdk.service.spec.ts index 6593095b325..c5917e0230f 100644 --- a/libs/common/src/platform/services/sdk/default-sdk.service.spec.ts +++ b/libs/common/src/platform/services/sdk/default-sdk.service.spec.ts @@ -56,6 +56,9 @@ describe("DefaultSdkService", () => { const userId = "user-id" as UserId; beforeEach(() => { + environmentService.getEnvironment$ + .calledWith(userId) + .mockReturnValue(new BehaviorSubject(mock())); accountService.accounts$ = of({ [userId]: { email: "email", emailVerified: true, name: "name" } as AccountInfo, }); diff --git a/libs/common/src/platform/services/sdk/default-sdk.service.ts b/libs/common/src/platform/services/sdk/default-sdk.service.ts index bf593aecf7d..e9cecbb15dc 100644 --- a/libs/common/src/platform/services/sdk/default-sdk.service.ts +++ b/libs/common/src/platform/services/sdk/default-sdk.service.ts @@ -78,7 +78,7 @@ export class DefaultSdkService implements SdkService { ); const client$ = combineLatest([ - this.environmentService.environment$, + this.environmentService.getEnvironment$(userId), account$, kdfParams$, privateKey$, From 31387d9a2a1e150ce5a4bcb876ee3619a9d8ce01 Mon Sep 17 00:00:00 2001 From: "bw-ghapp[bot]" <178206702+bw-ghapp[bot]@users.noreply.github.com> Date: Fri, 20 Dec 2024 14:56:00 +0000 Subject: [PATCH 11/75] Autosync the updated translations (#12503) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/browser/src/_locales/lv/messages.json | 20 +++++++++---------- apps/browser/src/_locales/zh_TW/messages.json | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/browser/src/_locales/lv/messages.json b/apps/browser/src/_locales/lv/messages.json index 7d0f789280b..31bdcd59e45 100644 --- a/apps/browser/src/_locales/lv/messages.json +++ b/apps/browser/src/_locales/lv/messages.json @@ -4911,22 +4911,22 @@ "message": "Beta" }, "importantNotice": { - "message": "Important notice" + "message": "Svarīgs paziņojums" }, "setupTwoStepLogin": { - "message": "Set up two-step login" + "message": "Iestatīt divpakāpju pieteikšanos" }, "newDeviceVerificationNoticeContentPage1": { - "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + "message": "Bitwarden, sākot ar 2025. gada februāri, nosūtīs kodu uz konta e-pasta adresi, lai apliecinātu pieteikšanos no jaunām ierīcēm." }, "newDeviceVerificationNoticeContentPage2": { - "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + "message": "Var iestatīt divpakāpju pieteikšanos kā citu veidu, kā aizsargāt savu kontu, vai iestatīt savu e-pasta adresi uz tādu, kurai ir piekļuve." }, "remindMeLater": { - "message": "Remind me later" + "message": "Atgādināt man vēlāk" }, "newDeviceVerificationNoticePageOneFormContent": { - "message": "Do you have reliable access to your email, $EMAIL$?", + "message": "Vai ir uzticama piekļuve savai e-pasta adresei $EMAIL$?", "placeholders": { "email": { "content": "$1", @@ -4935,16 +4935,16 @@ } }, "newDeviceVerificationNoticePageOneEmailAccessNo": { - "message": "No, I do not" + "message": "Nē, nav" }, "newDeviceVerificationNoticePageOneEmailAccessYes": { - "message": "Yes, I can reliably access my email" + "message": "Jā, varu uzticami piekļūt savam e-pastam" }, "turnOnTwoStepLogin": { - "message": "Turn on two-step login" + "message": "Ieslēgt divpakāpju pieteikšanos" }, "changeAcctEmail": { - "message": "Change account email" + "message": "Mainīt konta e-pasta adresi" }, "extensionWidth": { "message": "Paplašinājuma platums" diff --git a/apps/browser/src/_locales/zh_TW/messages.json b/apps/browser/src/_locales/zh_TW/messages.json index 2f1e1ef34f0..ae903678521 100644 --- a/apps/browser/src/_locales/zh_TW/messages.json +++ b/apps/browser/src/_locales/zh_TW/messages.json @@ -248,7 +248,7 @@ "message": "請求密碼提示" }, "enterYourAccountEmailAddressAndYourPasswordHintWillBeSentToYou": { - "message": "輸入您帳戶的電子郵件,您的密碼提示會傳送給您" + "message": "輸入您帳號的電子郵件,您的密碼提示會傳送給您" }, "passwordHint": { "message": "密碼提示" From a728331ec1beba7a9ff34ce3d35636cb417ef6ce Mon Sep 17 00:00:00 2001 From: "bw-ghapp[bot]" <178206702+bw-ghapp[bot]@users.noreply.github.com> Date: Fri, 20 Dec 2024 14:56:42 +0000 Subject: [PATCH 12/75] Autosync the updated translations (#12504) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/web/src/locales/de/messages.json | 2 +- apps/web/src/locales/lv/messages.json | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/web/src/locales/de/messages.json b/apps/web/src/locales/de/messages.json index b90d7fc2474..57e5891a702 100644 --- a/apps/web/src/locales/de/messages.json +++ b/apps/web/src/locales/de/messages.json @@ -3883,7 +3883,7 @@ "message": "Browser aktualisieren" }, "generatingRiskInsights": { - "message": "Generating your risk insights..." + "message": "Deine Risikoübersicht wird generiert..." }, "updateBrowserDesc": { "message": "Du verwendest einen nicht unterstützten Webbrowser. Der Web-Tresor funktioniert möglicherweise nicht richtig." diff --git a/apps/web/src/locales/lv/messages.json b/apps/web/src/locales/lv/messages.json index f60e5691572..8f8ae16320c 100644 --- a/apps/web/src/locales/lv/messages.json +++ b/apps/web/src/locales/lv/messages.json @@ -9885,22 +9885,22 @@ "message": "Apraksta kods" }, "importantNotice": { - "message": "Important notice" + "message": "Svarīgs paziņojums" }, "setupTwoStepLogin": { - "message": "Set up two-step login" + "message": "Iestatīt divpakāpju pieteikšanos" }, "newDeviceVerificationNoticeContentPage1": { - "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + "message": "Bitwarden, sākot ar 2025. gada februāri, nosūtīs kodu uz konta e-pasta adresi, lai apliecinātu pieteikšanos no jaunām ierīcēm." }, "newDeviceVerificationNoticeContentPage2": { - "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + "message": "Var iestatīt divpakāpju pieteikšanos kā citu veidu, kā aizsargāt savu kontu, vai iestatīt savu e-pasta adresi uz tādu, kurai ir piekļuve." }, "remindMeLater": { - "message": "Remind me later" + "message": "Atgādināt man vēlāk" }, "newDeviceVerificationNoticePageOneFormContent": { - "message": "Do you have reliable access to your email, $EMAIL$?", + "message": "Vai ir uzticama piekļuve savai e-pasta adresei $EMAIL$?", "placeholders": { "email": { "content": "$1", @@ -9909,16 +9909,16 @@ } }, "newDeviceVerificationNoticePageOneEmailAccessNo": { - "message": "No, I do not" + "message": "Nē, nav" }, "newDeviceVerificationNoticePageOneEmailAccessYes": { - "message": "Yes, I can reliably access my email" + "message": "Jā, varu uzticami piekļūt savam e-pastam" }, "turnOnTwoStepLogin": { - "message": "Turn on two-step login" + "message": "Ieslēgt divpakāpju pieteikšanos" }, "changeAcctEmail": { - "message": "Change account email" + "message": "Mainīt konta e-pasta adresi" }, "removeMembers": { "message": "Noņemt dalībniekus" From acd3ab05f6b2335b5dd284b0da8f079fb9a3b87c Mon Sep 17 00:00:00 2001 From: Daniel James Smith <2670567+djsmith85@users.noreply.github.com> Date: Fri, 20 Dec 2024 16:39:37 +0100 Subject: [PATCH 13/75] [PM-7100] Remove conditional routing for extension refresh (#12485) * Remove conditional routing for new vault page (header/footer) Redirect tabs/current to tabs/vault (new home) * Remove unused TabsComponent --------- Co-authored-by: Daniel James Smith --- apps/browser/src/popup/app-routing.module.ts | 19 +++---- apps/browser/src/popup/app.module.ts | 2 - apps/browser/src/popup/tabs.component.html | 57 -------------------- apps/browser/src/popup/tabs.component.ts | 15 ------ 4 files changed, 7 insertions(+), 86 deletions(-) delete mode 100644 apps/browser/src/popup/tabs.component.html delete mode 100644 apps/browser/src/popup/tabs.component.ts diff --git a/apps/browser/src/popup/app-routing.module.ts b/apps/browser/src/popup/app-routing.module.ts index ad839bbd7ce..73147cace23 100644 --- a/apps/browser/src/popup/app-routing.module.ts +++ b/apps/browser/src/popup/app-routing.module.ts @@ -105,10 +105,8 @@ import { clearVaultStateGuard } from "../vault/guards/clear-vault-state.guard"; import { AddEditComponent } from "../vault/popup/components/vault/add-edit.component"; import { AttachmentsComponent } from "../vault/popup/components/vault/attachments.component"; import { CollectionsComponent } from "../vault/popup/components/vault/collections.component"; -import { CurrentTabComponent } from "../vault/popup/components/vault/current-tab.component"; import { PasswordHistoryComponent } from "../vault/popup/components/vault/password-history.component"; import { ShareComponent } from "../vault/popup/components/vault/share.component"; -import { VaultFilterComponent } from "../vault/popup/components/vault/vault-filter.component"; import { VaultItemsComponent } from "../vault/popup/components/vault/vault-items.component"; import { VaultV2Component } from "../vault/popup/components/vault/vault-v2.component"; import { ViewComponent } from "../vault/popup/components/vault/view.component"; @@ -130,7 +128,6 @@ import { VaultSettingsComponent } from "../vault/popup/settings/vault-settings.c import { RouteElevation } from "./app-routing.animations"; import { debounceNavigationGuard } from "./services/debounce-navigation.service"; import { TabsV2Component } from "./tabs-v2.component"; -import { TabsComponent } from "./tabs.component"; /** * Data properties acceptable for use in extension route objects @@ -748,8 +745,9 @@ const routes: Routes = [ }, ], }, - ...extensionRefreshSwap(TabsComponent, TabsV2Component, { + { path: "tabs", + component: TabsV2Component, data: { elevation: 0 } satisfies RouteDataProperties, children: [ { @@ -759,18 +757,15 @@ const routes: Routes = [ }, { path: "current", - component: CurrentTabComponent, - canActivate: [authGuard], - canMatch: [extensionRefreshRedirect("/tabs/vault")], - data: { elevation: 0 } satisfies RouteDataProperties, - runGuardsAndResolvers: "always", + redirectTo: "/tabs/vault", }, - ...extensionRefreshSwap(VaultFilterComponent, VaultV2Component, { + { path: "vault", + component: VaultV2Component, canActivate: [authGuard, NewDeviceVerificationNoticeGuard], canDeactivate: [clearVaultStateGuard], data: { elevation: 0 } satisfies RouteDataProperties, - }), + }, ...extensionRefreshSwap(GeneratorComponent, CredentialGeneratorComponent, { path: "generator", canActivate: [authGuard], @@ -788,7 +783,7 @@ const routes: Routes = [ data: { elevation: 0 } satisfies RouteDataProperties, }), ], - }), + }, { path: "account-switcher", component: AccountSwitcherComponent, diff --git a/apps/browser/src/popup/app.module.ts b/apps/browser/src/popup/app.module.ts index 76bd06565c7..4695f0820b2 100644 --- a/apps/browser/src/popup/app.module.ts +++ b/apps/browser/src/popup/app.module.ts @@ -88,7 +88,6 @@ import { AppComponent } from "./app.component"; import { UserVerificationComponent } from "./components/user-verification.component"; import { ServicesModule } from "./services/services.module"; import { TabsV2Component } from "./tabs-v2.component"; -import { TabsComponent } from "./tabs.component"; // Register the locales for the application import "../platform/popup/locales"; @@ -177,7 +176,6 @@ import "../platform/popup/locales"; ShareComponent, SsoComponentV1, SyncComponent, - TabsComponent, TabsV2Component, TwoFactorComponent, TwoFactorOptionsComponent, diff --git a/apps/browser/src/popup/tabs.component.html b/apps/browser/src/popup/tabs.component.html deleted file mode 100644 index fd04967b914..00000000000 --- a/apps/browser/src/popup/tabs.component.html +++ /dev/null @@ -1,57 +0,0 @@ -
- - -
diff --git a/apps/browser/src/popup/tabs.component.ts b/apps/browser/src/popup/tabs.component.ts deleted file mode 100644 index 7546c9ca13b..00000000000 --- a/apps/browser/src/popup/tabs.component.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Component, OnInit } from "@angular/core"; - -import BrowserPopupUtils from "../platform/popup/browser-popup-utils"; - -@Component({ - selector: "app-tabs", - templateUrl: "tabs.component.html", -}) -export class TabsComponent implements OnInit { - showCurrentTab = true; - - ngOnInit() { - this.showCurrentTab = !BrowserPopupUtils.inPopout(window); - } -} From b27a1a5337f765ee3fa9c07f51083c29c69a68da Mon Sep 17 00:00:00 2001 From: Nick Krantz <125900171+nick-livefront@users.noreply.github.com> Date: Fri, 20 Dec 2024 09:44:36 -0600 Subject: [PATCH 14/75] [PM-12998] View Cipher: Color Password (#12354) * show color password for visible passwords in vault view - The password input will be visually hidden - Adds tests for the login credentials component * formatting --- .../login-credentials-view.component.html | 23 +- .../login-credentials-view.component.spec.ts | 198 ++++++++++++++++++ 2 files changed, 218 insertions(+), 3 deletions(-) create mode 100644 libs/vault/src/cipher-view/login-credentials/login-credentials-view.component.spec.ts diff --git a/libs/vault/src/cipher-view/login-credentials/login-credentials-view.component.html b/libs/vault/src/cipher-view/login-credentials/login-credentials-view.component.html index 5b6b995d095..8503604bf7c 100644 --- a/libs/vault/src/cipher-view/login-credentials/login-credentials-view.component.html +++ b/libs/vault/src/cipher-view/login-credentials/login-credentials-view.component.html @@ -28,17 +28,34 @@

{{ "loginCredentials" | i18n }}

> - {{ "password" | i18n }} + + {{ "password" | i18n }} + + + + + + +
+ +
  • +
    + + +
    +
  • + + +`; + exports[`AutofillInlineMenuList initAutofillInlineMenuList the locked inline menu for an unauthenticated user creates the views for the locked inline menu 1`] = `
    { expect(autofillInlineMenuList["inlineMenuListContainer"]).toMatchSnapshot(); }); + it("renders correctly when there are multiple TOTP elements with username displayed", async () => { + const totpCipher1 = createAutofillOverlayCipherDataMock(1, { + type: CipherType.Login, + login: { + totp: "123456", + totpField: true, + username: "user1", + }, + }); + + const totpCipher2 = createAutofillOverlayCipherDataMock(2, { + type: CipherType.Login, + login: { + totp: "654321", + totpField: true, + username: "user2", + }, + }); + + postWindowMessage( + createInitAutofillInlineMenuListMessageMock({ + inlineMenuFillType: CipherType.Login, + ciphers: [totpCipher1, totpCipher2], + }), + ); + + await flushPromises(); + const checkSubtitleElement = (username: string) => { + const subtitleElement = autofillInlineMenuList["inlineMenuListContainer"].querySelector( + `span.cipher-subtitle[title="${username}"]`, + ); + expect(subtitleElement).not.toBeNull(); + expect(subtitleElement.textContent).toBe(username); + }; + + checkSubtitleElement("user1"); + checkSubtitleElement("user2"); + + expect(autofillInlineMenuList["inlineMenuListContainer"]).toMatchSnapshot(); + }); + it("creates the view for a totp field", () => { postWindowMessage( createInitAutofillInlineMenuListMessageMock({ diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts b/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts index 6cf390d0a29..b7837505d41 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts @@ -1163,7 +1163,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { } if (cipher.login?.totpField && cipher.login?.totp) { - return this.buildTotpElement(cipher.login?.totp); + return this.buildTotpElement(cipher.login?.totp, cipher.login?.username); } const subTitleText = this.getSubTitleText(cipher); const cipherSubtitleElement = this.buildCipherSubtitleElement(subTitleText); @@ -1174,13 +1174,24 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { return cipherDetailsElement; } + /** + * Checks if there is more than one TOTP element being displayed. + * + * @returns {boolean} - Returns true if more than one TOTP element is displayed, otherwise false. + */ + private multipleTotpElements(): boolean { + return ( + this.ciphers.filter((cipher) => cipher.login?.totpField && cipher.login?.totp).length > 1 + ); + } + /** * Builds a TOTP element for a given TOTP code. * * @param totp - The TOTP code to display. */ - private buildTotpElement(totpCode: string): HTMLDivElement | null { + private buildTotpElement(totpCode: string, username?: string): HTMLDivElement | null { if (!totpCode) { return null; } @@ -1196,12 +1207,17 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { containerElement.appendChild(totpHeading); - const subtitleElement = document.createElement("span"); - subtitleElement.classList.add("cipher-subtitle"); - subtitleElement.textContent = formattedTotpCode; - subtitleElement.setAttribute("aria-label", this.getTranslation("totpCodeAria")); - subtitleElement.setAttribute("data-testid", "totp-code"); - containerElement.appendChild(subtitleElement); + if (this.multipleTotpElements() && username) { + const usernameSubtitle = this.buildCipherSubtitleElement(username); + containerElement.appendChild(usernameSubtitle); + } + + const totpCodeSpan = document.createElement("span"); + totpCodeSpan.classList.add("cipher-subtitle"); + totpCodeSpan.textContent = formattedTotpCode; + totpCodeSpan.setAttribute("aria-label", this.getTranslation("totpCodeAria")); + totpCodeSpan.setAttribute("data-testid", "totp-code"); + containerElement.appendChild(totpCodeSpan); return containerElement; } From 2e6031eee90631274864f9b9ba65e935132ed32f Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Fri, 20 Dec 2024 17:36:04 +0100 Subject: [PATCH 17/75] Increase heap size for builds (#12483) * Increase heap size for builds * Add cross-env to cli * Add cross-env to desktop * Update apps/web/package.json Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com> --------- Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com> --- apps/browser/package.json | 10 +++++----- apps/cli/package.json | 4 ++-- apps/desktop/package.json | 2 +- apps/web/package.json | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/browser/package.json b/apps/browser/package.json index 2b3edd96b6e..7c839ecc3d9 100644 --- a/apps/browser/package.json +++ b/apps/browser/package.json @@ -14,11 +14,11 @@ "build:watch:firefox": "npm run build:firefox -- --watch", "build:watch:opera": "npm run build:opera -- --watch", "build:watch:safari": "npm run build:safari -- --watch", - "build:prod:chrome": "cross-env NODE_ENV=production NODE_OPTIONS=\"--max-old-space-size=4096\" npm run build:chrome", - "build:prod:edge": "cross-env NODE_ENV=production NODE_OPTIONS=\"--max-old-space-size=4096\" npm run build:edge", - "build:prod:firefox": "cross-env NODE_ENV=production NODE_OPTIONS=\"--max-old-space-size=4096\" npm run build:firefox", - "build:prod:opera": "cross-env NODE_ENV=production NODE_OPTIONS=\"--max-old-space-size=4096\" npm run build:opera", - "build:prod:safari": "cross-env NODE_ENV=production NODE_OPTIONS=\"--max-old-space-size=4096\" npm run build:safari", + "build:prod:chrome": "cross-env NODE_ENV=production NODE_OPTIONS=\"--max-old-space-size=8192\" npm run build:chrome", + "build:prod:edge": "cross-env NODE_ENV=production NODE_OPTIONS=\"--max-old-space-size=8192\" npm run build:edge", + "build:prod:firefox": "cross-env NODE_ENV=production NODE_OPTIONS=\"--max-old-space-size=8192\" npm run build:firefox", + "build:prod:opera": "cross-env NODE_ENV=production NODE_OPTIONS=\"--max-old-space-size=8192\" npm run build:opera", + "build:prod:safari": "cross-env NODE_ENV=production NODE_OPTIONS=\"--max-old-space-size=8192\" npm run build:safari", "dist:chrome": "npm run build:prod:chrome && mkdir -p dist && ./scripts/compress.ps1 dist-chrome.zip", "dist:edge": "npm run build:prod:edge && mkdir -p dist && ./scripts/compress.ps1 dist-edge.zip", "dist:firefox": "npm run build:prod:firefox && mkdir -p dist && ./scripts/compress.ps1 dist-firefox.zip", diff --git a/apps/cli/package.json b/apps/cli/package.json index b7d54e78e1d..b25f327c42a 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -18,14 +18,14 @@ "license": "SEE LICENSE IN LICENSE.txt", "scripts": { "clean": "rimraf dist", - "build:oss": "webpack", + "build:oss": "cross-env NODE_OPTIONS=\"--max-old-space-size=8192\" webpack", "build:oss:debug": "npm run build:oss && node --inspect ./build/bw.js", "build:oss:watch": "webpack --watch", "build:oss:prod": "cross-env NODE_ENV=production webpack", "build:oss:prod:watch": "cross-env NODE_ENV=production webpack --watch", "debug": "node --inspect ./build/bw.js", "publish:npm": "npm run build:oss:prod && npm publish --access public", - "build:bit": "webpack -c ../../bitwarden_license/bit-cli/webpack.config.js", + "build:bit": "cross-env NODE_OPTIONS=\"--max-old-space-size=8192\" webpack -c ../../bitwarden_license/bit-cli/webpack.config.js", "build:bit:debug": "npm run build:bit && node --inspect ./build/bw.js", "build:bit:watch": "webpack --watch -c ../../bitwarden_license/bit-cli/webpack.config.js", "build:bit:prod": "cross-env NODE_ENV=production npm run build:bit", diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 10d5ded3448..7c6d1f37692 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -19,7 +19,7 @@ "postinstall": "electron-rebuild", "start": "cross-env ELECTRON_IS_DEV=0 ELECTRON_NO_UPDATER=1 electron ./build", "build-native": "cd desktop_native && node build.js", - "build": "concurrently -n Main,Rend,Prel -c yellow,cyan \"npm run build:main\" \"npm run build:renderer\" \"npm run build:preload\"", + "build": "cross-env NODE_OPTIONS=\"--max-old-space-size=8192\" concurrently -n Main,Rend,Prel -c yellow,cyan \"npm run build:main\" \"npm run build:renderer\" \"npm run build:preload\"", "build:dev": "concurrently -n Main,Rend -c yellow,cyan \"npm run build:main:dev\" \"npm run build:renderer:dev\"", "build:preload": "cross-env NODE_ENV=production webpack --config webpack.preload.js", "build:preload:watch": "cross-env NODE_ENV=production webpack --config webpack.preload.js --watch", diff --git a/apps/web/package.json b/apps/web/package.json index dee8e7722c9..f2697e1b68d 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -2,8 +2,8 @@ "name": "@bitwarden/web-vault", "version": "2024.12.1", "scripts": { - "build:oss": "webpack", - "build:bit": "webpack -c ../../bitwarden_license/bit-web/webpack.config.js", + "build:oss": "cross-env NODE_OPTIONS=\"--max-old-space-size=8192\" webpack", + "build:bit": "cross-env NODE_OPTIONS=\"--max-old-space-size=8192\" webpack -c ../../bitwarden_license/bit-web/webpack.config.js", "build:oss:watch": "webpack serve", "build:bit:watch": "webpack serve -c ../../bitwarden_license/bit-web/webpack.config.js", "build:bit:dev": "cross-env ENV=development npm run build:bit", From d209da4c945b9fddbc480c779150989596bfd90f Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Fri, 20 Dec 2024 10:23:03 -0800 Subject: [PATCH 18/75] feat(auth): [PM-9674] Remove Deprecated LockComponents (#12453) This PR deletes the legacy lock components from the Angular clients and also removes feature flag control from the routing. The lock component will now be based entirely on the new, recently refreshed LockComponent in libs/auth/angular. --- .../src/auth/popup/lock.component.html | 100 ---- apps/browser/src/auth/popup/lock.component.ts | 185 ------- apps/browser/src/popup/app-routing.module.ts | 21 +- apps/browser/src/popup/app.module.ts | 2 - apps/desktop/src/app/app-routing.module.ts | 16 +- apps/desktop/src/app/app.module.ts | 2 - apps/desktop/src/auth/lock.component.html | 80 --- apps/desktop/src/auth/lock.component.spec.ts | 478 ------------------ apps/desktop/src/auth/lock.component.ts | 235 --------- apps/web/src/app/auth/lock.component.html | 27 - apps/web/src/app/auth/lock.component.ts | 51 -- apps/web/src/app/oss-routing.module.ts | 59 +-- .../src/auth/components/lock.component.ts | 398 --------------- libs/auth/src/angular/lock/lock.component.ts | 6 +- 14 files changed, 31 insertions(+), 1629 deletions(-) delete mode 100644 apps/browser/src/auth/popup/lock.component.html delete mode 100644 apps/browser/src/auth/popup/lock.component.ts delete mode 100644 apps/desktop/src/auth/lock.component.html delete mode 100644 apps/desktop/src/auth/lock.component.spec.ts delete mode 100644 apps/desktop/src/auth/lock.component.ts delete mode 100644 apps/web/src/app/auth/lock.component.html delete mode 100644 apps/web/src/app/auth/lock.component.ts delete mode 100644 libs/angular/src/auth/components/lock.component.ts diff --git a/apps/browser/src/auth/popup/lock.component.html b/apps/browser/src/auth/popup/lock.component.html deleted file mode 100644 index fb1b09de49c..00000000000 --- a/apps/browser/src/auth/popup/lock.component.html +++ /dev/null @@ -1,100 +0,0 @@ -
    - -
    -

    - {{ "verifyIdentity" | i18n }} -

    -
    - -
    -
    -
    - -
    -
    -
    -
    - - -
    -
    - - -
    -
    - -
    -
    -
    - -
    -
    - -
    -

    - -

    - {{ biometricError }} -

    - {{ "awaitDesktop" | i18n }} -

    - - -
    -
    -
    diff --git a/apps/browser/src/auth/popup/lock.component.ts b/apps/browser/src/auth/popup/lock.component.ts deleted file mode 100644 index 66de3fb89d2..00000000000 --- a/apps/browser/src/auth/popup/lock.component.ts +++ /dev/null @@ -1,185 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { Component, NgZone, OnInit } from "@angular/core"; -import { Router } from "@angular/router"; -import { firstValueFrom } from "rxjs"; - -import { LockComponent as BaseLockComponent } from "@bitwarden/angular/auth/components/lock.component"; -import { PinServiceAbstraction } from "@bitwarden/auth/common"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service"; -import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service"; -import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction"; -import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; -import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; -import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction"; -import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; -import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; -import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; -import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; -import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength"; -import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; -import { DialogService, ToastService } from "@bitwarden/components"; -import { - KdfConfigService, - KeyService, - BiometricsService, - BiometricStateService, -} from "@bitwarden/key-management"; - -import { BiometricErrors, BiometricErrorTypes } from "../../models/biometricErrors"; -import { BrowserRouterService } from "../../platform/popup/services/browser-router.service"; -import { fido2PopoutSessionData$ } from "../../vault/popup/utils/fido2-popout-session-data"; - -@Component({ - selector: "app-lock", - templateUrl: "lock.component.html", -}) -export class LockComponent extends BaseLockComponent implements OnInit { - private isInitialLockScreen: boolean; - - biometricError: string; - pendingBiometric = false; - fido2PopoutSessionData$ = fido2PopoutSessionData$(); - - constructor( - masterPasswordService: InternalMasterPasswordServiceAbstraction, - router: Router, - i18nService: I18nService, - platformUtilsService: PlatformUtilsService, - messagingService: MessagingService, - keyService: KeyService, - vaultTimeoutService: VaultTimeoutService, - vaultTimeoutSettingsService: VaultTimeoutSettingsService, - environmentService: EnvironmentService, - stateService: StateService, - apiService: ApiService, - logService: LogService, - ngZone: NgZone, - policyApiService: PolicyApiServiceAbstraction, - policyService: InternalPolicyService, - passwordStrengthService: PasswordStrengthServiceAbstraction, - authService: AuthService, - dialogService: DialogService, - deviceTrustService: DeviceTrustServiceAbstraction, - userVerificationService: UserVerificationService, - pinService: PinServiceAbstraction, - private routerService: BrowserRouterService, - biometricStateService: BiometricStateService, - biometricsService: BiometricsService, - accountService: AccountService, - kdfConfigService: KdfConfigService, - syncService: SyncService, - toastService: ToastService, - ) { - super( - masterPasswordService, - router, - i18nService, - platformUtilsService, - messagingService, - keyService, - vaultTimeoutService, - vaultTimeoutSettingsService, - environmentService, - stateService, - apiService, - logService, - ngZone, - policyApiService, - policyService, - passwordStrengthService, - dialogService, - deviceTrustService, - userVerificationService, - pinService, - biometricStateService, - biometricsService, - accountService, - authService, - kdfConfigService, - syncService, - toastService, - ); - this.successRoute = "/tabs/current"; - this.isInitialLockScreen = (window as any).previousPopupUrl == null; - - this.onSuccessfulSubmit = async () => { - const previousUrl = this.routerService.getPreviousUrl(); - if (previousUrl) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.router.navigateByUrl(previousUrl); - } else { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.router.navigate([this.successRoute]); - } - }; - } - - async ngOnInit() { - await super.ngOnInit(); - const autoBiometricsPrompt = await firstValueFrom( - this.biometricStateService.promptAutomatically$, - ); - - window.setTimeout(async () => { - document.getElementById(this.pinEnabled ? "pin" : "masterPassword")?.focus(); - if ( - this.biometricLock && - autoBiometricsPrompt && - this.isInitialLockScreen && - (await this.authService.getAuthStatus()) === AuthenticationStatus.Locked - ) { - await this.unlockBiometric(true); - } - }, 100); - } - - override async unlockBiometric(automaticPrompt: boolean = false): Promise { - if (!this.biometricLock) { - return; - } - - this.biometricError = null; - - let success; - try { - const available = await super.isBiometricUnlockAvailable(); - if (!available) { - if (!automaticPrompt) { - await this.dialogService.openSimpleDialog({ - type: "warning", - title: { key: "biometricsNotAvailableTitle" }, - content: { key: "biometricsNotAvailableDesc" }, - acceptButtonText: { key: "ok" }, - cancelButtonText: null, - }); - } - } else { - this.pendingBiometric = true; - success = await super.unlockBiometric(); - } - } catch (e) { - const error = BiometricErrors[e?.message as BiometricErrorTypes]; - - if (error == null) { - this.logService.error("Unknown error: " + e); - return false; - } - - this.biometricError = this.i18nService.t(error.description); - } finally { - this.pendingBiometric = false; - } - - return success; - } -} diff --git a/apps/browser/src/popup/app-routing.module.ts b/apps/browser/src/popup/app-routing.module.ts index 73147cace23..549a677857c 100644 --- a/apps/browser/src/popup/app-routing.module.ts +++ b/apps/browser/src/popup/app-routing.module.ts @@ -17,7 +17,6 @@ import { unauthGuardFn, } from "@bitwarden/angular/auth/guards"; import { canAccessFeature } from "@bitwarden/angular/platform/guard/feature-flag.guard"; -import { extensionRefreshRedirect } from "@bitwarden/angular/utils/extension-refresh-redirect"; import { extensionRefreshSwap } from "@bitwarden/angular/utils/extension-refresh-swap"; import { NewDeviceVerificationNoticeGuard } from "@bitwarden/angular/vault/guards"; import { @@ -26,7 +25,7 @@ import { LoginComponent, LoginSecondaryContentComponent, LockIcon, - LockV2Component, + LockComponent, LoginViaAuthRequestComponent, PasswordHintComponent, RegistrationFinishComponent, @@ -60,7 +59,6 @@ import { } from "../auth/popup/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component"; import { HintComponent } from "../auth/popup/hint.component"; import { HomeComponent } from "../auth/popup/home.component"; -import { LockComponent } from "../auth/popup/lock.component"; import { LoginDecryptionOptionsComponentV1 } from "../auth/popup/login-decryption-options/login-decryption-options-v1.component"; import { LoginComponentV1 } from "../auth/popup/login-v1.component"; import { LoginViaAuthRequestComponentV1 } from "../auth/popup/login-via-auth-request-v1.component"; @@ -173,13 +171,6 @@ const routes: Routes = [ canActivate: [fido2AuthGuard], data: { elevation: 1 } satisfies RouteDataProperties, }), - { - path: "lock", - component: LockComponent, - canActivate: [lockGuard()], - canMatch: [extensionRefreshRedirect("/lockV2")], - data: { elevation: 1, doNotSaveUrl: true } satisfies RouteDataProperties, - }, ...twofactorRefactorSwap( TwoFactorComponent, AnonLayoutWrapperComponent, @@ -650,8 +641,8 @@ const routes: Routes = [ ], }, { - path: "lockV2", - canActivate: [canAccessFeature(FeatureFlag.ExtensionRefresh), lockGuard()], + path: "lock", + canActivate: [lockGuard()], data: { pageIcon: LockIcon, pageTitle: { @@ -662,19 +653,19 @@ const routes: Routes = [ elevation: 1, /** * This ensures that in a passkey flow the `/fido2?` URL does not get - * overwritten in the `BrowserRouterService` by the `/lockV2` route. This way, after + * overwritten in the `BrowserRouterService` by the `/lock` route. This way, after * unlocking, the user can be redirected back to the `/fido2?` URL. * * Also, this prevents a routing loop when using biometrics to unlock the vault in MV2 (Firefox), * locking up the browser (https://bitwarden.atlassian.net/browse/PM-16116). This involves the - * `popup-router-cache.service` pushing the `lockV2` route to the history. + * `popup-router-cache.service` pushing the `lock` route to the history. */ doNotSaveUrl: true, } satisfies ExtensionAnonLayoutWrapperData & RouteDataProperties, children: [ { path: "", - component: LockV2Component, + component: LockComponent, }, ], }, diff --git a/apps/browser/src/popup/app.module.ts b/apps/browser/src/popup/app.module.ts index 4695f0820b2..3d8cb267798 100644 --- a/apps/browser/src/popup/app.module.ts +++ b/apps/browser/src/popup/app.module.ts @@ -23,7 +23,6 @@ import { EnvironmentComponent } from "../auth/popup/environment.component"; import { ExtensionAnonLayoutWrapperComponent } from "../auth/popup/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component"; import { HintComponent } from "../auth/popup/hint.component"; import { HomeComponent } from "../auth/popup/home.component"; -import { LockComponent } from "../auth/popup/lock.component"; import { LoginDecryptionOptionsComponentV1 } from "../auth/popup/login-decryption-options/login-decryption-options-v1.component"; import { LoginComponentV1 } from "../auth/popup/login-v1.component"; import { LoginViaAuthRequestComponentV1 } from "../auth/popup/login-via-auth-request-v1.component"; @@ -156,7 +155,6 @@ import "../platform/popup/locales"; VaultFilterComponent, HintComponent, HomeComponent, - LockComponent, LoginViaAuthRequestComponentV1, LoginComponentV1, LoginDecryptionOptionsComponentV1, diff --git a/apps/desktop/src/app/app-routing.module.ts b/apps/desktop/src/app/app-routing.module.ts index c7642638dc3..7e82bb004fa 100644 --- a/apps/desktop/src/app/app-routing.module.ts +++ b/apps/desktop/src/app/app-routing.module.ts @@ -15,7 +15,6 @@ import { unauthGuardFn, } from "@bitwarden/angular/auth/guards"; import { canAccessFeature } from "@bitwarden/angular/platform/guard/feature-flag.guard"; -import { extensionRefreshRedirect } from "@bitwarden/angular/utils/extension-refresh-redirect"; import { NewDeviceVerificationNoticeGuard } from "@bitwarden/angular/vault/guards"; import { AnonLayoutWrapperComponent, @@ -23,7 +22,7 @@ import { LoginComponent, LoginSecondaryContentComponent, LockIcon, - LockV2Component, + LockComponent, LoginViaAuthRequestComponent, PasswordHintComponent, RegistrationFinishComponent, @@ -51,7 +50,6 @@ import { twofactorRefactorSwap } from "../../../../libs/angular/src/utils/two-fa import { AccessibilityCookieComponent } from "../auth/accessibility-cookie.component"; import { maxAccountsGuardFn } from "../auth/guards/max-accounts.guard"; import { HintComponent } from "../auth/hint.component"; -import { LockComponent } from "../auth/lock.component"; import { LoginDecryptionOptionsComponentV1 } from "../auth/login/login-decryption-options/login-decryption-options-v1.component"; import { LoginComponentV1 } from "../auth/login/login-v1.component"; import { LoginViaAuthRequestComponentV1 } from "../auth/login/login-via-auth-request-v1.component"; @@ -81,12 +79,6 @@ const routes: Routes = [ children: [], // Children lets us have an empty component. canActivate: [redirectGuard({ loggedIn: "/vault", loggedOut: "/login", locked: "/lock" })], }, - { - path: "lock", - component: LockComponent, - canActivate: [lockGuard()], - canMatch: [extensionRefreshRedirect("/lockV2")], - }, ...twofactorRefactorSwap( TwoFactorComponent, AnonLayoutWrapperComponent, @@ -373,8 +365,8 @@ const routes: Routes = [ ], }, { - path: "lockV2", - canActivate: [canAccessFeature(FeatureFlag.ExtensionRefresh), lockGuard()], + path: "lock", + canActivate: [lockGuard()], data: { pageIcon: LockIcon, pageTitle: { @@ -385,7 +377,7 @@ const routes: Routes = [ children: [ { path: "", - component: LockV2Component, + component: LockComponent, }, ], }, diff --git a/apps/desktop/src/app/app.module.ts b/apps/desktop/src/app/app.module.ts index 5bd1c66b87c..5b9a1e3539d 100644 --- a/apps/desktop/src/app/app.module.ts +++ b/apps/desktop/src/app/app.module.ts @@ -13,7 +13,6 @@ import { AccessibilityCookieComponent } from "../auth/accessibility-cookie.compo import { DeleteAccountComponent } from "../auth/delete-account.component"; import { EnvironmentComponent } from "../auth/environment.component"; import { HintComponent } from "../auth/hint.component"; -import { LockComponent } from "../auth/lock.component"; import { LoginModule } from "../auth/login/login.module"; import { RegisterComponent } from "../auth/register.component"; import { RemovePasswordComponent } from "../auth/remove-password.component"; @@ -78,7 +77,6 @@ import { SendComponent } from "./tools/send/send.component"; FolderAddEditComponent, HeaderComponent, HintComponent, - LockComponent, NavComponent, GeneratorComponent, PasswordGeneratorHistoryComponent, diff --git a/apps/desktop/src/auth/lock.component.html b/apps/desktop/src/auth/lock.component.html deleted file mode 100644 index 895eda91e83..00000000000 --- a/apps/desktop/src/auth/lock.component.html +++ /dev/null @@ -1,80 +0,0 @@ -
    -
    - -

    {{ "yourVaultIsLocked" | i18n }}

    -
    -
    -
    -
    - - -
    -
    - - -
    -
    - -
    -
    -
    - -
    -
    -
    - -
    -
    - - -
    -
    -
    -
    diff --git a/apps/desktop/src/auth/lock.component.spec.ts b/apps/desktop/src/auth/lock.component.spec.ts deleted file mode 100644 index 4e59acf89c1..00000000000 --- a/apps/desktop/src/auth/lock.component.spec.ts +++ /dev/null @@ -1,478 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { NO_ERRORS_SCHEMA } from "@angular/core"; -import { ComponentFixture, TestBed, fakeAsync, tick } from "@angular/core/testing"; -import { ActivatedRoute } from "@angular/router"; -import { MockProxy, mock } from "jest-mock-extended"; -import { of } from "rxjs"; - -import { LockComponent as BaseLockComponent } from "@bitwarden/angular/auth/components/lock.component"; -import { I18nPipe } from "@bitwarden/angular/platform/pipes/i18n.pipe"; -import { PinServiceAbstraction } from "@bitwarden/auth/common"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service"; -import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service"; -import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction"; -import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; -import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; -import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction"; -import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; -import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; -import { FakeMasterPasswordService } from "@bitwarden/common/auth/services/master-password/fake-master-password.service"; -import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; -import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; -import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec"; -import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength"; -import { UserId } from "@bitwarden/common/types/guid"; -import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; -import { DialogService, ToastService } from "@bitwarden/components"; -import { - KdfConfigService, - KeyService, - BiometricsService as AbstractBiometricService, - BiometricStateService, -} from "@bitwarden/key-management"; - -import { BiometricsService } from "../key-management/biometrics/biometrics.service"; - -import { LockComponent } from "./lock.component"; - -// ipc mock global -const isWindowVisibleMock = jest.fn(); -(global as any).ipc = { - platform: { - isWindowVisible: isWindowVisibleMock, - }, - keyManagement: { - biometric: { - enabled: jest.fn(), - }, - }, -}; - -describe("LockComponent", () => { - let component: LockComponent; - let fixture: ComponentFixture; - let stateServiceMock: MockProxy; - let biometricStateService: MockProxy; - let biometricsService: MockProxy; - let messagingServiceMock: MockProxy; - let broadcasterServiceMock: MockProxy; - let platformUtilsServiceMock: MockProxy; - let activatedRouteMock: MockProxy; - let mockMasterPasswordService: FakeMasterPasswordService; - let mockToastService: MockProxy; - - const mockUserId = Utils.newGuid() as UserId; - const accountService: FakeAccountService = mockAccountServiceWith(mockUserId); - - beforeEach(async () => { - stateServiceMock = mock(); - - messagingServiceMock = mock(); - broadcasterServiceMock = mock(); - platformUtilsServiceMock = mock(); - mockToastService = mock(); - - activatedRouteMock = mock(); - activatedRouteMock.queryParams = mock(); - - mockMasterPasswordService = new FakeMasterPasswordService(); - - biometricStateService = mock(); - biometricStateService.dismissedRequirePasswordOnStartCallout$ = of(false); - biometricStateService.promptAutomatically$ = of(false); - biometricStateService.promptCancelled$ = of(false); - - await TestBed.configureTestingModule({ - declarations: [LockComponent, I18nPipe], - providers: [ - { provide: InternalMasterPasswordServiceAbstraction, useValue: mockMasterPasswordService }, - { - provide: I18nService, - useValue: mock(), - }, - { - provide: PlatformUtilsService, - useValue: platformUtilsServiceMock, - }, - { - provide: MessagingService, - useValue: messagingServiceMock, - }, - { - provide: KeyService, - useValue: mock(), - }, - { - provide: VaultTimeoutService, - useValue: mock(), - }, - { - provide: VaultTimeoutSettingsService, - useValue: mock(), - }, - { - provide: EnvironmentService, - useValue: mock(), - }, - { - provide: StateService, - useValue: stateServiceMock, - }, - { - provide: ApiService, - useValue: mock(), - }, - { - provide: ActivatedRoute, - useValue: activatedRouteMock, - }, - { - provide: BroadcasterService, - useValue: broadcasterServiceMock, - }, - { - provide: PolicyApiServiceAbstraction, - useValue: mock(), - }, - { - provide: InternalPolicyService, - useValue: mock(), - }, - { - provide: PasswordStrengthServiceAbstraction, - useValue: mock(), - }, - { - provide: LogService, - useValue: mock(), - }, - { - provide: DialogService, - useValue: mock(), - }, - { - provide: DeviceTrustServiceAbstraction, - useValue: mock(), - }, - { - provide: UserVerificationService, - useValue: mock(), - }, - { - provide: PinServiceAbstraction, - useValue: mock(), - }, - { - provide: BiometricStateService, - useValue: biometricStateService, - }, - { - provide: AbstractBiometricService, - useValue: biometricsService, - }, - { - provide: AccountService, - useValue: accountService, - }, - { - provide: AuthService, - useValue: mock(), - }, - { - provide: KdfConfigService, - useValue: mock(), - }, - { - provide: SyncService, - useValue: mock(), - }, - { - provide: ToastService, - useValue: mockToastService, - }, - ], - schemas: [NO_ERRORS_SCHEMA], - }).compileComponents(); - - fixture = TestBed.createComponent(LockComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - afterEach(() => { - jest.resetAllMocks(); - }); - - describe("ngOnInit", () => { - it("should call super.ngOnInit() once", async () => { - const superNgOnInitSpy = jest.spyOn(BaseLockComponent.prototype, "ngOnInit"); - await component.ngOnInit(); - expect(superNgOnInitSpy).toHaveBeenCalledTimes(1); - }); - - it('should set "autoPromptBiometric" to true if "biometricState.promptAutomatically$" resolves to true', async () => { - biometricStateService.promptAutomatically$ = of(true); - - await component.ngOnInit(); - expect(component["autoPromptBiometric"]).toBe(true); - }); - - it('should set "autoPromptBiometric" to false if "biometricState.promptAutomatically$" resolves to false', async () => { - biometricStateService.promptAutomatically$ = of(false); - - await component.ngOnInit(); - expect(component["autoPromptBiometric"]).toBe(false); - }); - - it('should set "biometricReady" to true if "stateService.getBiometricReady()" resolves to true', async () => { - component["canUseBiometric"] = jest.fn().mockResolvedValue(true); - - await component.ngOnInit(); - expect(component["biometricReady"]).toBe(true); - }); - - it('should set "biometricReady" to false if "stateService.getBiometricReady()" resolves to false', async () => { - component["canUseBiometric"] = jest.fn().mockResolvedValue(false); - - await component.ngOnInit(); - expect(component["biometricReady"]).toBe(false); - }); - - it("should call displayBiometricUpdateWarning", async () => { - component["displayBiometricUpdateWarning"] = jest.fn(); - await component.ngOnInit(); - expect(component["displayBiometricUpdateWarning"]).toHaveBeenCalledTimes(1); - }); - - it("should call delayedAskForBiometric", async () => { - component["delayedAskForBiometric"] = jest.fn(); - await component.ngOnInit(); - expect(component["delayedAskForBiometric"]).toHaveBeenCalledTimes(1); - expect(component["delayedAskForBiometric"]).toHaveBeenCalledWith(500); - }); - - it("should call delayedAskForBiometric when queryParams change", async () => { - activatedRouteMock.queryParams = of({ promptBiometric: true }); - component["delayedAskForBiometric"] = jest.fn(); - await component.ngOnInit(); - - expect(component["delayedAskForBiometric"]).toHaveBeenCalledTimes(1); - expect(component["delayedAskForBiometric"]).toHaveBeenCalledWith(500); - }); - - it("should call messagingService.send", async () => { - await component.ngOnInit(); - expect(messagingServiceMock.send).toHaveBeenCalledWith("getWindowIsFocused"); - }); - - describe("broadcasterService.subscribe", () => { - it('should call onWindowHidden() when "broadcasterService.subscribe" is called with "windowHidden"', async () => { - component["onWindowHidden"] = jest.fn(); - await component.ngOnInit(); - broadcasterServiceMock.subscribe.mock.calls[0][1]({ command: "windowHidden" }); - expect(component["onWindowHidden"]).toHaveBeenCalledTimes(1); - }); - - it('should call focusInput() when "broadcasterService.subscribe" is called with "windowIsFocused" is true and deferFocus is false', async () => { - component["focusInput"] = jest.fn(); - component["deferFocus"] = null; - await component.ngOnInit(); - broadcasterServiceMock.subscribe.mock.calls[0][1]({ - command: "windowIsFocused", - windowIsFocused: true, - } as any); - expect(component["deferFocus"]).toBe(false); - expect(component["focusInput"]).toHaveBeenCalledTimes(1); - }); - - it('should not call focusInput() when "broadcasterService.subscribe" is called with "windowIsFocused" is true and deferFocus is true', async () => { - component["focusInput"] = jest.fn(); - component["deferFocus"] = null; - await component.ngOnInit(); - broadcasterServiceMock.subscribe.mock.calls[0][1]({ - command: "windowIsFocused", - windowIsFocused: false, - } as any); - expect(component["deferFocus"]).toBe(true); - expect(component["focusInput"]).toHaveBeenCalledTimes(0); - }); - - it('should call focusInput() when "broadcasterService.subscribe" is called with "windowIsFocused" is true and deferFocus is true', async () => { - component["focusInput"] = jest.fn(); - component["deferFocus"] = true; - await component.ngOnInit(); - broadcasterServiceMock.subscribe.mock.calls[0][1]({ - command: "windowIsFocused", - windowIsFocused: true, - } as any); - expect(component["deferFocus"]).toBe(false); - expect(component["focusInput"]).toHaveBeenCalledTimes(1); - }); - - it('should not call focusInput() when "broadcasterService.subscribe" is called with "windowIsFocused" is false and deferFocus is true', async () => { - component["focusInput"] = jest.fn(); - component["deferFocus"] = true; - await component.ngOnInit(); - broadcasterServiceMock.subscribe.mock.calls[0][1]({ - command: "windowIsFocused", - windowIsFocused: false, - } as any); - expect(component["deferFocus"]).toBe(true); - expect(component["focusInput"]).toHaveBeenCalledTimes(0); - }); - }); - }); - - describe("ngOnDestroy", () => { - it("should call super.ngOnDestroy()", () => { - const superNgOnDestroySpy = jest.spyOn(BaseLockComponent.prototype, "ngOnDestroy"); - component.ngOnDestroy(); - expect(superNgOnDestroySpy).toHaveBeenCalledTimes(1); - }); - - it("should call broadcasterService.unsubscribe()", () => { - component.ngOnDestroy(); - expect(broadcasterServiceMock.unsubscribe).toHaveBeenCalledTimes(1); - }); - }); - - describe("focusInput", () => { - it('should call "focus" on #pin input if pinEnabled is true', () => { - component["pinEnabled"] = true; - global.document.getElementById = jest.fn().mockReturnValue({ focus: jest.fn() }); - component["focusInput"](); - expect(global.document.getElementById).toHaveBeenCalledWith("pin"); - }); - - it('should call "focus" on #masterPassword input if pinEnabled is false', () => { - component["pinEnabled"] = false; - global.document.getElementById = jest.fn().mockReturnValue({ focus: jest.fn() }); - component["focusInput"](); - expect(global.document.getElementById).toHaveBeenCalledWith("masterPassword"); - }); - }); - - describe("delayedAskForBiometric", () => { - beforeEach(() => { - component["supportsBiometric"] = true; - component["autoPromptBiometric"] = true; - }); - - it('should wait for "delay" milliseconds', fakeAsync(async () => { - const delaySpy = jest.spyOn(global, "setTimeout"); - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - component["delayedAskForBiometric"](5000); - - tick(4000); - component["biometricAsked"] = false; - - tick(1000); - component["biometricAsked"] = true; - - expect(delaySpy).toHaveBeenCalledWith(expect.any(Function), 5000); - })); - - it('should return; if "params" is defined and "params.promptBiometric" is false', fakeAsync(async () => { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - component["delayedAskForBiometric"](5000, { promptBiometric: false }); - tick(5000); - expect(component["biometricAsked"]).toBe(false); - })); - - it('should not return; if "params" is defined and "params.promptBiometric" is true', fakeAsync(async () => { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - component["delayedAskForBiometric"](5000, { promptBiometric: true }); - tick(5000); - expect(component["biometricAsked"]).toBe(true); - })); - - it('should not return; if "params" is undefined', fakeAsync(async () => { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - component["delayedAskForBiometric"](5000); - tick(5000); - expect(component["biometricAsked"]).toBe(true); - })); - - it('should return; if "supportsBiometric" is false', fakeAsync(async () => { - component["supportsBiometric"] = false; - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - component["delayedAskForBiometric"](5000); - tick(5000); - expect(component["biometricAsked"]).toBe(false); - })); - - it('should return; if "autoPromptBiometric" is false', fakeAsync(async () => { - component["autoPromptBiometric"] = false; - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - component["delayedAskForBiometric"](5000); - tick(5000); - expect(component["biometricAsked"]).toBe(false); - })); - - it("should call unlockBiometric() if biometricAsked is false and window is visible", fakeAsync(async () => { - isWindowVisibleMock.mockResolvedValue(true); - component["unlockBiometric"] = jest.fn(); - component["biometricAsked"] = false; - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - component["delayedAskForBiometric"](5000); - tick(5000); - - expect(component["unlockBiometric"]).toHaveBeenCalledTimes(1); - })); - - it("should not call unlockBiometric() if biometricAsked is false and window is not visible", fakeAsync(async () => { - isWindowVisibleMock.mockResolvedValue(false); - component["unlockBiometric"] = jest.fn(); - component["biometricAsked"] = false; - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - component["delayedAskForBiometric"](5000); - tick(5000); - - expect(component["unlockBiometric"]).toHaveBeenCalledTimes(0); - })); - - it("should not call unlockBiometric() if biometricAsked is true", fakeAsync(async () => { - isWindowVisibleMock.mockResolvedValue(true); - component["unlockBiometric"] = jest.fn(); - component["biometricAsked"] = true; - - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - component["delayedAskForBiometric"](5000); - tick(5000); - - expect(component["unlockBiometric"]).toHaveBeenCalledTimes(0); - })); - }); - - describe("canUseBiometric", () => { - it("should call biometric.enabled with current active user", async () => { - await component["canUseBiometric"](); - - expect(ipc.keyManagement.biometric.enabled).toHaveBeenCalledWith(mockUserId); - }); - }); - - it('onWindowHidden() should set "showPassword" to false', () => { - component["showPassword"] = true; - component["onWindowHidden"](); - expect(component["showPassword"]).toBe(false); - }); -}); diff --git a/apps/desktop/src/auth/lock.component.ts b/apps/desktop/src/auth/lock.component.ts deleted file mode 100644 index ea2ac546ec1..00000000000 --- a/apps/desktop/src/auth/lock.component.ts +++ /dev/null @@ -1,235 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { Component, NgZone, OnDestroy, OnInit } from "@angular/core"; -import { ActivatedRoute, Router } from "@angular/router"; -import { firstValueFrom, map, switchMap } from "rxjs"; - -import { LockComponent as BaseLockComponent } from "@bitwarden/angular/auth/components/lock.component"; -import { PinServiceAbstraction } from "@bitwarden/auth/common"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service"; -import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service"; -import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction"; -import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; -import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; -import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction"; -import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; -import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; -import { DeviceType } from "@bitwarden/common/enums"; -import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; -import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; -import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength"; -import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; -import { DialogService, ToastService } from "@bitwarden/components"; -import { - KdfConfigService, - KeyService, - BiometricsService, - BiometricStateService, -} from "@bitwarden/key-management"; - -const BroadcasterSubscriptionId = "LockComponent"; - -@Component({ - selector: "app-lock", - templateUrl: "lock.component.html", -}) -export class LockComponent extends BaseLockComponent implements OnInit, OnDestroy { - private deferFocus: boolean = null; - protected biometricReady = false; - private biometricAsked = false; - private autoPromptBiometric = false; - private timerId: any; - - constructor( - masterPasswordService: InternalMasterPasswordServiceAbstraction, - router: Router, - i18nService: I18nService, - platformUtilsService: PlatformUtilsService, - messagingService: MessagingService, - keyService: KeyService, - vaultTimeoutService: VaultTimeoutService, - vaultTimeoutSettingsService: VaultTimeoutSettingsService, - environmentService: EnvironmentService, - protected override stateService: StateService, - apiService: ApiService, - private route: ActivatedRoute, - private broadcasterService: BroadcasterService, - ngZone: NgZone, - policyApiService: PolicyApiServiceAbstraction, - policyService: InternalPolicyService, - passwordStrengthService: PasswordStrengthServiceAbstraction, - logService: LogService, - dialogService: DialogService, - deviceTrustService: DeviceTrustServiceAbstraction, - userVerificationService: UserVerificationService, - pinService: PinServiceAbstraction, - biometricStateService: BiometricStateService, - biometricsService: BiometricsService, - accountService: AccountService, - authService: AuthService, - kdfConfigService: KdfConfigService, - syncService: SyncService, - toastService: ToastService, - ) { - super( - masterPasswordService, - router, - i18nService, - platformUtilsService, - messagingService, - keyService, - vaultTimeoutService, - vaultTimeoutSettingsService, - environmentService, - stateService, - apiService, - logService, - ngZone, - policyApiService, - policyService, - passwordStrengthService, - dialogService, - deviceTrustService, - userVerificationService, - pinService, - biometricStateService, - biometricsService, - accountService, - authService, - kdfConfigService, - syncService, - toastService, - ); - } - - async ngOnInit() { - await super.ngOnInit(); - this.autoPromptBiometric = await firstValueFrom( - this.biometricStateService.promptAutomatically$, - ); - this.biometricReady = await this.canUseBiometric(); - - await this.displayBiometricUpdateWarning(); - - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.delayedAskForBiometric(500); - this.route.queryParams.pipe(switchMap((params) => this.delayedAskForBiometric(500, params))); - - this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => { - this.ngZone.run(() => { - switch (message.command) { - case "windowHidden": - this.onWindowHidden(); - break; - case "windowIsFocused": - if (this.deferFocus === null) { - this.deferFocus = !message.windowIsFocused; - if (!this.deferFocus) { - this.focusInput(); - } - } else if (this.deferFocus && message.windowIsFocused) { - this.focusInput(); - this.deferFocus = false; - } - break; - default: - } - }); - }); - this.messagingService.send("getWindowIsFocused"); - - // start background listener until destroyed on interval - this.timerId = setInterval(async () => { - this.supportsBiometric = await this.biometricsService.supportsBiometric(); - this.biometricReady = await this.canUseBiometric(); - }, 1000); - } - - ngOnDestroy() { - super.ngOnDestroy(); - this.broadcasterService.unsubscribe(BroadcasterSubscriptionId); - clearInterval(this.timerId); - } - - onWindowHidden() { - this.showPassword = false; - } - - private async delayedAskForBiometric(delay: number, params?: any) { - await new Promise((resolve) => setTimeout(resolve, delay)); - - if (params && !params.promptBiometric) { - return; - } - - if (!this.supportsBiometric || !this.autoPromptBiometric || this.biometricAsked) { - return; - } - - if (await firstValueFrom(this.biometricStateService.promptCancelled$)) { - return; - } - - this.biometricAsked = true; - if (await ipc.platform.isWindowVisible()) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.unlockBiometric(); - } - } - - private async canUseBiometric() { - const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.id))); - return await ipc.keyManagement.biometric.enabled(userId); - } - - private focusInput() { - document.getElementById(this.pinEnabled ? "pin" : "masterPassword")?.focus(); - } - - private async displayBiometricUpdateWarning(): Promise { - if (await firstValueFrom(this.biometricStateService.dismissedRequirePasswordOnStartCallout$)) { - return; - } - - if (this.platformUtilsService.getDevice() !== DeviceType.WindowsDesktop) { - return; - } - - if (await firstValueFrom(this.biometricStateService.biometricUnlockEnabled$)) { - const response = await this.dialogService.openSimpleDialog({ - title: { key: "windowsBiometricUpdateWarningTitle" }, - content: { key: "windowsBiometricUpdateWarning" }, - type: "warning", - }); - - await this.biometricStateService.setRequirePasswordOnStart(response); - if (response) { - await this.biometricStateService.setPromptAutomatically(false); - } - this.supportsBiometric = await this.canUseBiometric(); - await this.biometricStateService.setDismissedRequirePasswordOnStartCallout(); - } - } - - get biometricText() { - switch (this.platformUtilsService.getDevice()) { - case DeviceType.MacOsDesktop: - return "unlockWithTouchId"; - case DeviceType.WindowsDesktop: - return "unlockWithWindowsHello"; - case DeviceType.LinuxDesktop: - return "unlockWithPolkit"; - default: - throw new Error("Unsupported platform"); - } - } -} diff --git a/apps/web/src/app/auth/lock.component.html b/apps/web/src/app/auth/lock.component.html deleted file mode 100644 index f630906223b..00000000000 --- a/apps/web/src/app/auth/lock.component.html +++ /dev/null @@ -1,27 +0,0 @@ -
    - - {{ "masterPass" | i18n }} - - - {{ "loggedInAsEmailOn" | i18n: email : webVaultHostname }} - - -
    - -
    - - -
    -
    diff --git a/apps/web/src/app/auth/lock.component.ts b/apps/web/src/app/auth/lock.component.ts deleted file mode 100644 index 36e9c81f2c7..00000000000 --- a/apps/web/src/app/auth/lock.component.ts +++ /dev/null @@ -1,51 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { Component, OnInit, inject } from "@angular/core"; -import { FormBuilder, Validators } from "@angular/forms"; - -import { LockComponent as BaseLockComponent } from "@bitwarden/angular/auth/components/lock.component"; - -import { SharedModule } from "../shared"; - -@Component({ - selector: "app-lock", - templateUrl: "lock.component.html", - standalone: true, - imports: [SharedModule], -}) -export class LockComponent extends BaseLockComponent implements OnInit { - formBuilder = inject(FormBuilder); - - formGroup = this.formBuilder.group({ - masterPassword: ["", { validators: Validators.required, updateOn: "submit" }], - }); - - get masterPasswordFormControl() { - return this.formGroup.controls.masterPassword; - } - - async ngOnInit() { - await super.ngOnInit(); - - this.masterPasswordFormControl.setValue(this.masterPassword); - - this.onSuccessfulSubmit = async () => { - await this.router.navigateByUrl(this.successRoute); - }; - } - - async superSubmit() { - await super.submit(); - } - - submit = async () => { - this.formGroup.markAllAsTouched(); - - if (this.formGroup.invalid) { - return; - } - - this.masterPassword = this.masterPasswordFormControl.value; - await this.superSubmit(); - }; -} diff --git a/apps/web/src/app/oss-routing.module.ts b/apps/web/src/app/oss-routing.module.ts index 9f2a86c1c06..ad536110b74 100644 --- a/apps/web/src/app/oss-routing.module.ts +++ b/apps/web/src/app/oss-routing.module.ts @@ -12,7 +12,6 @@ import { } from "@bitwarden/angular/auth/guards"; import { canAccessFeature } from "@bitwarden/angular/platform/guard/feature-flag.guard"; import { generatorSwap } from "@bitwarden/angular/tools/generator/generator-swap"; -import { extensionRefreshSwap } from "@bitwarden/angular/utils/extension-refresh-swap"; import { NewDeviceVerificationNoticeGuard } from "@bitwarden/angular/vault/guards"; import { AnonLayoutWrapperComponent, @@ -26,7 +25,7 @@ import { RegistrationLinkExpiredComponent, LoginComponent, LoginSecondaryContentComponent, - LockV2Component, + LockComponent, LockIcon, TwoFactorTimeoutIcon, UserLockIcon, @@ -56,7 +55,6 @@ import { FamiliesForEnterpriseSetupComponent } from "./admin-console/organizatio import { CreateOrganizationComponent } from "./admin-console/settings/create-organization.component"; import { deepLinkGuard } from "./auth/guards/deep-link.guard"; import { HintComponent } from "./auth/hint.component"; -import { LockComponent } from "./auth/lock.component"; import { LoginDecryptionOptionsComponentV1 } from "./auth/login/login-decryption-options/login-decryption-options-v1.component"; import { LoginComponentV1 } from "./auth/login/login-v1.component"; import { LoginViaAuthRequestComponentV1 } from "./auth/login/login-via-auth-request-v1.component"; @@ -509,44 +507,23 @@ const routes: Routes = [ }, }, }, - ...extensionRefreshSwap( - LockComponent, - LockV2Component, - { - path: "lock", - canActivate: [deepLinkGuard(), lockGuard()], - children: [ - { - path: "", - component: LockComponent, - }, - ], - data: { - pageTitle: { - key: "yourVaultIsLockedV2", - }, - pageIcon: LockIcon, - showReadonlyHostname: true, - } satisfies AnonLayoutWrapperData, - }, - { - path: "lock", - canActivate: [deepLinkGuard(), lockGuard()], - children: [ - { - path: "", - component: LockV2Component, - }, - ], - data: { - pageTitle: { - key: "yourVaultIsLockedV2", - }, - pageIcon: LockIcon, - showReadonlyHostname: true, - } satisfies AnonLayoutWrapperData, - }, - ), + { + path: "lock", + canActivate: [deepLinkGuard(), lockGuard()], + children: [ + { + path: "", + component: LockComponent, + }, + ], + data: { + pageTitle: { + key: "yourVaultIsLockedV2", + }, + pageIcon: LockIcon, + showReadonlyHostname: true, + } satisfies AnonLayoutWrapperData, + }, { path: "2fa", canActivate: [unauthGuardFn()], diff --git a/libs/angular/src/auth/components/lock.component.ts b/libs/angular/src/auth/components/lock.component.ts deleted file mode 100644 index 20ad37fd2b4..00000000000 --- a/libs/angular/src/auth/components/lock.component.ts +++ /dev/null @@ -1,398 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { Directive, NgZone, OnDestroy, OnInit } from "@angular/core"; -import { Router } from "@angular/router"; -import { firstValueFrom, Subject } from "rxjs"; -import { concatMap, map, take, takeUntil } from "rxjs/operators"; - -import { PinServiceAbstraction, PinLockType } from "@bitwarden/auth/common"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service"; -import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service"; -import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction"; -import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; -import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options"; -import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; -import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction"; -import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; -import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; -import { VerificationType } from "@bitwarden/common/auth/enums/verification-type"; -import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason"; -import { - MasterPasswordVerification, - MasterPasswordVerificationResponse, -} from "@bitwarden/common/auth/types/verification"; -import { ClientType } from "@bitwarden/common/enums"; -import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; -import { KeySuffixOptions } from "@bitwarden/common/platform/enums"; -import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength"; -import { UserId } from "@bitwarden/common/types/guid"; -import { UserKey } from "@bitwarden/common/types/key"; -import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; -import { DialogService, ToastService } from "@bitwarden/components"; -import { - KdfConfigService, - KeyService, - BiometricStateService, - BiometricsService, -} from "@bitwarden/key-management"; - -@Directive() -export class LockComponent implements OnInit, OnDestroy { - masterPassword = ""; - pin = ""; - showPassword = false; - email: string; - pinEnabled = false; - masterPasswordEnabled = false; - webVaultHostname = ""; - formPromise: Promise; - supportsBiometric: boolean; - biometricLock: boolean; - - private activeUserId: UserId; - protected successRoute = "vault"; - protected forcePasswordResetRoute = "update-temp-password"; - protected onSuccessfulSubmit: () => Promise; - - private invalidPinAttempts = 0; - private pinLockType: PinLockType; - - private enforcedMasterPasswordOptions: MasterPasswordPolicyOptions = undefined; - - private destroy$ = new Subject(); - - constructor( - protected masterPasswordService: InternalMasterPasswordServiceAbstraction, - protected router: Router, - protected i18nService: I18nService, - protected platformUtilsService: PlatformUtilsService, - protected messagingService: MessagingService, - protected keyService: KeyService, - protected vaultTimeoutService: VaultTimeoutService, - protected vaultTimeoutSettingsService: VaultTimeoutSettingsService, - protected environmentService: EnvironmentService, - protected stateService: StateService, - protected apiService: ApiService, - protected logService: LogService, - protected ngZone: NgZone, - protected policyApiService: PolicyApiServiceAbstraction, - protected policyService: InternalPolicyService, - protected passwordStrengthService: PasswordStrengthServiceAbstraction, - protected dialogService: DialogService, - protected deviceTrustService: DeviceTrustServiceAbstraction, - protected userVerificationService: UserVerificationService, - protected pinService: PinServiceAbstraction, - protected biometricStateService: BiometricStateService, - protected biometricsService: BiometricsService, - protected accountService: AccountService, - protected authService: AuthService, - protected kdfConfigService: KdfConfigService, - protected syncService: SyncService, - protected toastService: ToastService, - ) {} - - async ngOnInit() { - this.accountService.activeAccount$ - .pipe( - concatMap(async (account) => { - this.activeUserId = account?.id; - await this.load(account?.id); - }), - takeUntil(this.destroy$), - ) - .subscribe(); - } - - ngOnDestroy() { - this.destroy$.next(); - this.destroy$.complete(); - } - - async submit() { - if (this.pinEnabled) { - return await this.handlePinRequiredUnlock(); - } - - await this.handleMasterPasswordRequiredUnlock(); - } - - async logOut() { - const confirmed = await this.dialogService.openSimpleDialog({ - title: { key: "logOut" }, - content: { key: "logOutConfirmation" }, - acceptButtonText: { key: "logOut" }, - type: "warning", - }); - - if (confirmed) { - this.messagingService.send("logout", { userId: this.activeUserId }); - } - } - - async unlockBiometric(): Promise { - if (!this.biometricLock) { - return; - } - - await this.biometricStateService.setUserPromptCancelled(); - const userKey = await this.keyService.getUserKeyFromStorage( - KeySuffixOptions.Biometric, - this.activeUserId, - ); - - if (userKey) { - await this.setUserKeyAndContinue(userKey, this.activeUserId, false); - } - - return !!userKey; - } - - async isBiometricUnlockAvailable(): Promise { - if (!(await this.biometricsService.supportsBiometric())) { - return false; - } - return this.biometricsService.isBiometricUnlockAvailable(); - } - - togglePassword() { - this.showPassword = !this.showPassword; - const input = document.getElementById(this.pinEnabled ? "pin" : "masterPassword"); - if (this.ngZone.isStable) { - input.focus(); - } else { - this.ngZone.onStable.pipe(take(1)).subscribe(() => input.focus()); - } - } - - private async handlePinRequiredUnlock() { - if (this.pin == null || this.pin === "") { - this.toastService.showToast({ - variant: "error", - title: this.i18nService.t("errorOccurred"), - message: this.i18nService.t("pinRequired"), - }); - return; - } - - return await this.doUnlockWithPin(); - } - - private async doUnlockWithPin() { - const MAX_INVALID_PIN_ENTRY_ATTEMPTS = 5; - - try { - const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id; - const userKey = await this.pinService.decryptUserKeyWithPin(this.pin, userId); - - if (userKey) { - await this.setUserKeyAndContinue(userKey, userId); - return; // successfully unlocked - } - - // Failure state: invalid PIN or failed decryption - this.invalidPinAttempts++; - - // Log user out if they have entered an invalid PIN too many times - if (this.invalidPinAttempts >= MAX_INVALID_PIN_ENTRY_ATTEMPTS) { - this.toastService.showToast({ - variant: "error", - title: null, - message: this.i18nService.t("tooManyInvalidPinEntryAttemptsLoggingOut"), - }); - this.messagingService.send("logout"); - return; - } - - this.toastService.showToast({ - variant: "error", - title: this.i18nService.t("errorOccurred"), - message: this.i18nService.t("invalidPin"), - }); - } catch { - this.toastService.showToast({ - variant: "error", - title: this.i18nService.t("errorOccurred"), - message: this.i18nService.t("unexpectedError"), - }); - } - } - - private async handleMasterPasswordRequiredUnlock() { - if (this.masterPassword == null || this.masterPassword === "") { - this.toastService.showToast({ - variant: "error", - title: this.i18nService.t("errorOccurred"), - message: this.i18nService.t("masterPasswordRequired"), - }); - return; - } - await this.doUnlockWithMasterPassword(); - } - - private async doUnlockWithMasterPassword() { - const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id; - - const verification = { - type: VerificationType.MasterPassword, - secret: this.masterPassword, - } as MasterPasswordVerification; - - let passwordValid = false; - let response: MasterPasswordVerificationResponse; - try { - this.formPromise = this.userVerificationService.verifyUserByMasterPassword( - verification, - userId, - this.email, - ); - response = await this.formPromise; - this.enforcedMasterPasswordOptions = MasterPasswordPolicyOptions.fromResponse( - response.policyOptions, - ); - passwordValid = true; - } catch (e) { - this.logService.error(e); - } finally { - this.formPromise = null; - } - - if (!passwordValid) { - this.toastService.showToast({ - variant: "error", - title: this.i18nService.t("errorOccurred"), - message: this.i18nService.t("invalidMasterPassword"), - }); - return; - } - - const userKey = await this.masterPasswordService.decryptUserKeyWithMasterKey( - response.masterKey, - userId, - ); - await this.setUserKeyAndContinue(userKey, userId, true); - } - - private async setUserKeyAndContinue( - key: UserKey, - userId: UserId, - evaluatePasswordAfterUnlock = false, - ) { - await this.keyService.setUserKey(key, userId); - - // Now that we have a decrypted user key in memory, we can check if we - // need to establish trust on the current device - const activeAccount = await firstValueFrom(this.accountService.activeAccount$); - await this.deviceTrustService.trustDeviceIfRequired(activeAccount.id); - - await this.doContinue(evaluatePasswordAfterUnlock); - } - - private async doContinue(evaluatePasswordAfterUnlock: boolean) { - await this.biometricStateService.resetUserPromptCancelled(); - this.messagingService.send("unlocked"); - - if (evaluatePasswordAfterUnlock) { - try { - // If we do not have any saved policies, attempt to load them from the service - if (this.enforcedMasterPasswordOptions == undefined) { - this.enforcedMasterPasswordOptions = await firstValueFrom( - this.policyService.masterPasswordPolicyOptions$(), - ); - } - - if (this.requirePasswordChange()) { - const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id; - await this.masterPasswordService.setForceSetPasswordReason( - ForceSetPasswordReason.WeakMasterPassword, - userId, - ); - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.router.navigate([this.forcePasswordResetRoute]); - return; - } - } catch (e) { - // Do not prevent unlock if there is an error evaluating policies - this.logService.error(e); - } - } - - // Vault can be de-synced since notifications get ignored while locked. Need to check whether sync is required using the sync service. - const clientType = this.platformUtilsService.getClientType(); - if (clientType === ClientType.Browser || clientType === ClientType.Desktop) { - // Desktop and Browser have better offline support and to facilitate this we don't make the user wait for what - // could be an HTTP Timeout because their server is unreachable. - await Promise.race([ - this.syncService - .fullSync(false) - .catch((err) => this.logService.error("Error during unlock sync", err)), - new Promise((resolve) => - setTimeout(() => { - this.logService.warning("Skipping sync wait, continuing to unlock."); - resolve(); - }, 5_000), - ), - ]); - } else { - await this.syncService.fullSync(false); - } - - if (this.onSuccessfulSubmit != null) { - await this.onSuccessfulSubmit(); - } else if (this.router != null) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.router.navigate([this.successRoute]); - } - } - - private async load(userId: UserId) { - this.pinLockType = await this.pinService.getPinLockType(userId); - - this.pinEnabled = await this.pinService.isPinDecryptionAvailable(userId); - - this.masterPasswordEnabled = await this.userVerificationService.hasMasterPassword(); - - this.supportsBiometric = await this.biometricsService.supportsBiometric(); - this.biometricLock = - (await this.vaultTimeoutSettingsService.isBiometricLockSet()) && - ((await this.keyService.hasUserKeyStored(KeySuffixOptions.Biometric)) || - !this.platformUtilsService.supportsSecureStorage()); - this.email = await firstValueFrom( - this.accountService.activeAccount$.pipe(map((a) => a?.email)), - ); - - this.webVaultHostname = (await this.environmentService.getEnvironment()).getHostname(); - } - - /** - * Checks if the master password meets the enforced policy requirements - * If not, returns false - */ - private requirePasswordChange(): boolean { - if ( - this.enforcedMasterPasswordOptions == undefined || - !this.enforcedMasterPasswordOptions.enforceOnLogin - ) { - return false; - } - - const passwordStrength = this.passwordStrengthService.getPasswordStrength( - this.masterPassword, - this.email, - )?.score; - - return !this.policyService.evaluateMasterPassword( - passwordStrength, - this.masterPassword, - this.enforcedMasterPasswordOptions, - ); - } -} diff --git a/libs/auth/src/angular/lock/lock.component.ts b/libs/auth/src/angular/lock/lock.component.ts index bcbc2bd5751..aa7b43c2e53 100644 --- a/libs/auth/src/angular/lock/lock.component.ts +++ b/libs/auth/src/angular/lock/lock.component.ts @@ -75,7 +75,7 @@ const clientTypeToSuccessRouteRecord: Partial> = { IconButtonModule, ], }) -export class LockV2Component implements OnInit, OnDestroy { +export class LockComponent implements OnInit, OnDestroy { private destroy$ = new Subject(); activeAccount: Account | null; @@ -543,8 +543,8 @@ export class LockV2Component implements OnInit, OnDestroy { const previousUrl = this.lockComponentService.getPreviousUrl(); /** * In a passkey flow, the `previousUrl` will still be `/fido2?` at this point - * because the `/lockV2` route doesn't save the URL in the `BrowserRouterService`. This is - * handled by the `doNotSaveUrl` property on the `lockV2` route in `app-routing.module.ts`. + * because the `/lock` route doesn't save the URL in the `BrowserRouterService`. This is + * handled by the `doNotSaveUrl` property on the `/lock` route in `app-routing.module.ts`. */ if (previousUrl) { await this.router.navigateByUrl(previousUrl); From ce5ae478a8a46575be9d20a04d9b4d33cba53132 Mon Sep 17 00:00:00 2001 From: Daniel James Smith <2670567+djsmith85@users.noreply.github.com> Date: Fri, 20 Dec 2024 19:46:24 +0100 Subject: [PATCH 19/75] [PM-15921] Remove v1 generator and generator history (#12345) * Remove v1 generator, generator history page and extension refresh conditional routing * Remove unused keys from en/messages.json --------- Co-authored-by: Daniel James Smith --- apps/browser/src/_locales/en/messages.json | 22 - apps/browser/src/popup/app-routing.module.ts | 14 +- apps/browser/src/popup/app.module.ts | 4 - .../popup/generator/generator.component.html | 588 ------------------ .../popup/generator/generator.component.ts | 88 --- .../password-generator-history.component.html | 48 -- .../password-generator-history.component.ts | 28 - 7 files changed, 7 insertions(+), 785 deletions(-) delete mode 100644 apps/browser/src/tools/popup/generator/generator.component.html delete mode 100644 apps/browser/src/tools/popup/generator/generator.component.ts delete mode 100644 apps/browser/src/tools/popup/generator/password-generator-history.component.html delete mode 100644 apps/browser/src/tools/popup/generator/password-generator-history.component.ts diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index de438a09467..4b7d9eb2992 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -454,9 +454,6 @@ "length": { "message": "Length" }, - "passwordMinLength": { - "message": "Minimum password length" - }, "uppercase": { "message": "Uppercase (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -528,10 +525,6 @@ "minSpecial": { "message": "Minimum special" }, - "avoidAmbChar": { - "message": "Avoid ambiguous characters", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Avoid ambiguous characters", "description": "Label for the avoid ambiguous characters checkbox." @@ -2047,9 +2040,6 @@ "clone": { "message": "Clone" }, - "passwordGeneratorPolicyInEffect": { - "message": "One or more organization policies are affecting your generator settings." - }, "passwordGenerator": { "message": "Password generator" }, @@ -2884,9 +2874,6 @@ "error": { "message": "Error" }, - "regenerateUsername": { - "message": "Regenerate username" - }, "generateUsername": { "message": "Generate username" }, @@ -2927,9 +2914,6 @@ } } }, - "usernameType": { - "message": "Username type" - }, "plusAddressedEmail": { "message": "Plus addressed email", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2952,12 +2936,6 @@ "websiteName": { "message": "Website name" }, - "whatWouldYouLikeToGenerate": { - "message": "What would you like to generate?" - }, - "passwordType": { - "message": "Password type" - }, "service": { "message": "Service" }, diff --git a/apps/browser/src/popup/app-routing.module.ts b/apps/browser/src/popup/app-routing.module.ts index 549a677857c..1f42c1cf31d 100644 --- a/apps/browser/src/popup/app-routing.module.ts +++ b/apps/browser/src/popup/app-routing.module.ts @@ -86,8 +86,6 @@ import BrowserPopupUtils from "../platform/popup/browser-popup-utils"; import { popupRouterCacheGuard } from "../platform/popup/view-cache/popup-router-cache.service"; import { CredentialGeneratorHistoryComponent } from "../tools/popup/generator/credential-generator-history.component"; import { CredentialGeneratorComponent } from "../tools/popup/generator/credential-generator.component"; -import { GeneratorComponent } from "../tools/popup/generator/generator.component"; -import { PasswordGeneratorHistoryComponent } from "../tools/popup/generator/password-generator-history.component"; import { SendAddEditComponent } from "../tools/popup/send/send-add-edit.component"; import { SendGroupingsComponent } from "../tools/popup/send/send-groupings.component"; import { SendTypeComponent } from "../tools/popup/send/send-type.component"; @@ -330,15 +328,16 @@ const routes: Routes = [ }), { path: "generator", - component: GeneratorComponent, + component: CredentialGeneratorComponent, canActivate: [authGuard], data: { elevation: 0 } satisfies RouteDataProperties, }, - ...extensionRefreshSwap(PasswordGeneratorHistoryComponent, CredentialGeneratorHistoryComponent, { + { path: "generator-history", + component: CredentialGeneratorHistoryComponent, canActivate: [authGuard], data: { elevation: 1 } satisfies RouteDataProperties, - }), + }, { path: "import", component: ImportBrowserV2Component, @@ -757,11 +756,12 @@ const routes: Routes = [ canDeactivate: [clearVaultStateGuard], data: { elevation: 0 } satisfies RouteDataProperties, }, - ...extensionRefreshSwap(GeneratorComponent, CredentialGeneratorComponent, { + { path: "generator", + component: CredentialGeneratorComponent, canActivate: [authGuard], data: { elevation: 0 } satisfies RouteDataProperties, - }), + }, { path: "settings", component: SettingsV2Component, diff --git a/apps/browser/src/popup/app.module.ts b/apps/browser/src/popup/app.module.ts index 3d8cb267798..c2697b2d490 100644 --- a/apps/browser/src/popup/app.module.ts +++ b/apps/browser/src/popup/app.module.ts @@ -56,8 +56,6 @@ import { PopupHeaderComponent } from "../platform/popup/layout/popup-header.comp import { PopupPageComponent } from "../platform/popup/layout/popup-page.component"; import { PopupTabNavigationComponent } from "../platform/popup/layout/popup-tab-navigation.component"; import { FilePopoutCalloutComponent } from "../tools/popup/components/file-popout-callout.component"; -import { GeneratorComponent } from "../tools/popup/generator/generator.component"; -import { PasswordGeneratorHistoryComponent } from "../tools/popup/generator/password-generator-history.component"; import { SendListComponent } from "../tools/popup/send/components/send-list.component"; import { SendAddEditComponent } from "../tools/popup/send/send-add-edit.component"; import { SendGroupingsComponent } from "../tools/popup/send/send-groupings.component"; @@ -160,8 +158,6 @@ import "../platform/popup/locales"; LoginDecryptionOptionsComponentV1, NotificationsSettingsV1Component, AppearanceComponent, - GeneratorComponent, - PasswordGeneratorHistoryComponent, PasswordHistoryComponent, PremiumComponent, RegisterComponent, diff --git a/apps/browser/src/tools/popup/generator/generator.component.html b/apps/browser/src/tools/popup/generator/generator.component.html deleted file mode 100644 index d92d32a5623..00000000000 --- a/apps/browser/src/tools/popup/generator/generator.component.html +++ /dev/null @@ -1,588 +0,0 @@ - -
    - - -
    -

    - {{ "generator" | i18n }} -

    -
    - -
    -
    -
    - - {{ "passwordGeneratorPolicyInEffect" | i18n }} - -
    -
    -
    - - -
    -
    -
    -
    -
    - - -
    -
    -
    -
    -
    - -
    - - -
    -
    -
    -
    - -
    -

    - {{ "options" | i18n }} -

    -
    -
    - -
    - - -
    -
    -
    -
    -
    -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - - -
    -
    - {{ "passwordMinLength" | i18n }} - - {{ passwordOptionsMinLengthForReader$ | async }} - - {{ passwordOptions.minLength }} -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    -
    -
    -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    -
    -
    - -
    - -
    -

    - {{ "options" | i18n }} -

    -
    -
    - -
    - - -
    -
    -
    -
    -
    -
    -
    - - -
    - -
    - - -
    -
    - - -
    -
    - -
    - - -
    -
    - -
    - - -
    -
    - - -
    -
    - - -
    -
    - -
    - - -
    -
    - -
    - - -
    -
    - -
    - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    - - -
    -
    - -
    - - -
    -
    -
    - - -
    -
    -
    -
    -
    -
    - - -
    -
    - -
    - - -
    -
    -
    - - -
    -
    -
    -
    -
    -
    - - -
    -
    - - -
    -
    -
    -
    -
    diff --git a/apps/browser/src/tools/popup/generator/generator.component.ts b/apps/browser/src/tools/popup/generator/generator.component.ts deleted file mode 100644 index d744c58f4d1..00000000000 --- a/apps/browser/src/tools/popup/generator/generator.component.ts +++ /dev/null @@ -1,88 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { Location } from "@angular/common"; -import { Component, NgZone, OnInit } from "@angular/core"; -import { ActivatedRoute } from "@angular/router"; -import { firstValueFrom } from "rxjs"; - -import { GeneratorComponent as BaseGeneratorComponent } from "@bitwarden/angular/tools/generator/components/generator.component"; -import { AccountService } from "@bitwarden/common/auth/abstractions/account.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 { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; -import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; -import { AddEditCipherInfo } from "@bitwarden/common/vault/types/add-edit-cipher-info"; -import { ToastService } from "@bitwarden/components"; -import { - PasswordGenerationServiceAbstraction, - UsernameGenerationServiceAbstraction, -} from "@bitwarden/generator-legacy"; - -@Component({ - selector: "app-generator", - templateUrl: "generator.component.html", -}) -export class GeneratorComponent extends BaseGeneratorComponent implements OnInit { - private addEditCipherInfo: AddEditCipherInfo; - private cipherState: CipherView; - private cipherService: CipherService; - - constructor( - passwordGenerationService: PasswordGenerationServiceAbstraction, - usernameGenerationService: UsernameGenerationServiceAbstraction, - platformUtilsService: PlatformUtilsService, - i18nService: I18nService, - accountService: AccountService, - cipherService: CipherService, - route: ActivatedRoute, - logService: LogService, - ngZone: NgZone, - private location: Location, - toastService: ToastService, - ) { - super( - passwordGenerationService, - usernameGenerationService, - platformUtilsService, - accountService, - i18nService, - logService, - route, - ngZone, - window, - toastService, - ); - this.cipherService = cipherService; - } - - async ngOnInit() { - this.addEditCipherInfo = await firstValueFrom(this.cipherService.addEditCipherInfo$); - if (this.addEditCipherInfo != null) { - this.cipherState = this.addEditCipherInfo.cipher; - } - this.comingFromAddEdit = this.cipherState != null; - if (this.cipherState?.login?.hasUris) { - this.usernameWebsite = this.cipherState.login.uris[0].hostname; - } - await super.ngOnInit(); - } - - select() { - super.select(); - if (this.type === "password") { - this.cipherState.login.password = this.password; - } else if (this.type === "username") { - this.cipherState.login.username = this.username; - } - this.addEditCipherInfo.cipher = this.cipherState; - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.cipherService.setAddEditCipherInfo(this.addEditCipherInfo); - this.close(); - } - - close() { - this.location.back(); - } -} diff --git a/apps/browser/src/tools/popup/generator/password-generator-history.component.html b/apps/browser/src/tools/popup/generator/password-generator-history.component.html deleted file mode 100644 index 8f4a246fc5e..00000000000 --- a/apps/browser/src/tools/popup/generator/password-generator-history.component.html +++ /dev/null @@ -1,48 +0,0 @@ -
    -
    - -
    -

    - {{ "passwordHistory" | i18n }} -

    -
    - -
    -
    -
    -
    -
    -
    -
    -
    -
    - {{ h.date | date: "medium" }} -
    -
    -
    - -
    -
    -
    -
    -
    -

    {{ "noPasswordsInList" | i18n }}

    -
    -
    diff --git a/apps/browser/src/tools/popup/generator/password-generator-history.component.ts b/apps/browser/src/tools/popup/generator/password-generator-history.component.ts deleted file mode 100644 index 2436ae51a7d..00000000000 --- a/apps/browser/src/tools/popup/generator/password-generator-history.component.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Location } from "@angular/common"; -import { Component } from "@angular/core"; - -import { PasswordGeneratorHistoryComponent as BasePasswordGeneratorHistoryComponent } from "@bitwarden/angular/tools/generator/components/password-generator-history.component"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { ToastService } from "@bitwarden/components"; -import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy"; - -@Component({ - selector: "app-password-generator-history", - templateUrl: "password-generator-history.component.html", -}) -export class PasswordGeneratorHistoryComponent extends BasePasswordGeneratorHistoryComponent { - constructor( - passwordGenerationService: PasswordGenerationServiceAbstraction, - platformUtilsService: PlatformUtilsService, - i18nService: I18nService, - private location: Location, - toastService: ToastService, - ) { - super(passwordGenerationService, platformUtilsService, i18nService, window, toastService); - } - - close() { - this.location.back(); - } -} From 64166a9354dc2e1397d38ec5de10f795e68213e3 Mon Sep 17 00:00:00 2001 From: Victoria League Date: Fri, 20 Dec 2024 14:03:50 -0500 Subject: [PATCH 20/75] [PM-16102] Increase clickable area for bit-item actions (#12450) --- .../components/src/badge/badge.component.html | 3 +++ ...{badge.directive.ts => badge.component.ts} | 13 ++++++----- libs/components/src/badge/badge.module.ts | 6 ++--- libs/components/src/badge/badge.stories.ts | 10 ++++----- libs/components/src/badge/index.ts | 2 +- .../src/item/item-action.component.ts | 8 +++++++ .../src/item/item-content.component.ts | 4 ++++ libs/components/src/item/item.stories.ts | 22 +++++++++---------- 8 files changed, 43 insertions(+), 25 deletions(-) create mode 100644 libs/components/src/badge/badge.component.html rename libs/components/src/badge/{badge.directive.ts => badge.component.ts} (89%) diff --git a/libs/components/src/badge/badge.component.html b/libs/components/src/badge/badge.component.html new file mode 100644 index 00000000000..6f586ec3523 --- /dev/null +++ b/libs/components/src/badge/badge.component.html @@ -0,0 +1,3 @@ + + + diff --git a/libs/components/src/badge/badge.directive.ts b/libs/components/src/badge/badge.component.ts similarity index 89% rename from libs/components/src/badge/badge.directive.ts rename to libs/components/src/badge/badge.component.ts index eef876a664d..29644a11ac7 100644 --- a/libs/components/src/badge/badge.directive.ts +++ b/libs/components/src/badge/badge.component.ts @@ -1,6 +1,7 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { Directive, ElementRef, HostBinding, Input } from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { Component, ElementRef, HostBinding, Input } from "@angular/core"; import { FocusableElement } from "../shared/focusable-element"; @@ -28,12 +29,14 @@ const hoverStyles: Record = { info: ["hover:tw-bg-info-600", "hover:tw-border-info-600", "hover:!tw-text-black"], }; -@Directive({ +@Component({ selector: "span[bitBadge], a[bitBadge], button[bitBadge]", - providers: [{ provide: FocusableElement, useExisting: BadgeDirective }], + providers: [{ provide: FocusableElement, useExisting: BadgeComponent }], + imports: [CommonModule], + templateUrl: "badge.component.html", standalone: true, }) -export class BadgeDirective implements FocusableElement { +export class BadgeComponent implements FocusableElement { @HostBinding("class") get classList() { return [ "tw-inline-block", @@ -63,7 +66,7 @@ export class BadgeDirective implements FocusableElement { ] .concat(styles[this.variant]) .concat(this.hasHoverEffects ? hoverStyles[this.variant] : []) - .concat(this.truncate ? ["tw-truncate", this.maxWidthClass] : []); + .concat(this.truncate ? this.maxWidthClass : []); } @HostBinding("attr.title") get titleAttr() { if (this.title !== undefined) { diff --git a/libs/components/src/badge/badge.module.ts b/libs/components/src/badge/badge.module.ts index e7f3770785a..d9a6e712820 100644 --- a/libs/components/src/badge/badge.module.ts +++ b/libs/components/src/badge/badge.module.ts @@ -1,9 +1,9 @@ import { NgModule } from "@angular/core"; -import { BadgeDirective } from "./badge.directive"; +import { BadgeComponent } from "./badge.component"; @NgModule({ - imports: [BadgeDirective], - exports: [BadgeDirective], + imports: [BadgeComponent], + exports: [BadgeComponent], }) export class BadgeModule {} diff --git a/libs/components/src/badge/badge.stories.ts b/libs/components/src/badge/badge.stories.ts index b8ac7ec8efe..5d697f8ad8f 100644 --- a/libs/components/src/badge/badge.stories.ts +++ b/libs/components/src/badge/badge.stories.ts @@ -1,14 +1,14 @@ import { CommonModule } from "@angular/common"; import { Meta, moduleMetadata, StoryObj } from "@storybook/angular"; -import { BadgeDirective } from "./badge.directive"; +import { BadgeComponent } from "./badge.component"; export default { title: "Component Library/Badge", - component: BadgeDirective, + component: BadgeComponent, decorators: [ moduleMetadata({ - imports: [CommonModule, BadgeDirective], + imports: [CommonModule, BadgeComponent], }), ], args: { @@ -21,9 +21,9 @@ export default { url: "https://www.figma.com/file/Zt3YSeb6E6lebAffrNLa0h/Tailwind-Component-Library?node-id=1881%3A16956", }, }, -} as Meta; +} as Meta; -type Story = StoryObj; +type Story = StoryObj; export const Variants: Story = { render: (args) => ({ diff --git a/libs/components/src/badge/index.ts b/libs/components/src/badge/index.ts index a8f5babda91..ae19f4df288 100644 --- a/libs/components/src/badge/index.ts +++ b/libs/components/src/badge/index.ts @@ -1,2 +1,2 @@ -export { BadgeDirective, BadgeVariant } from "./badge.directive"; +export { BadgeComponent, BadgeVariant } from "./badge.component"; export * from "./badge.module"; diff --git a/libs/components/src/item/item-action.component.ts b/libs/components/src/item/item-action.component.ts index 8cabf5c5c23..ae8cb719724 100644 --- a/libs/components/src/item/item-action.component.ts +++ b/libs/components/src/item/item-action.component.ts @@ -8,5 +8,13 @@ import { A11yCellDirective } from "../a11y/a11y-cell.directive"; imports: [], template: ``, providers: [{ provide: A11yCellDirective, useExisting: ItemActionComponent }], + host: { + class: + /** + * `top` and `bottom` units should be kept in sync with `item-content.component.ts`'s y-axis padding. + * we want this `:after` element to be the same height as the `item-content` + */ + "[&>button]:tw-relative [&>button:not([bit-item-content])]:after:tw-content-[''] [&>button]:after:tw-absolute [&>button]:after:tw-block bit-compact:[&>button]:after:tw-top-[-0.8rem] bit-compact:[&>button]:after:tw-bottom-[-0.8rem] [&>button]:after:tw-top-[-0.85rem] [&>button]:after:tw-bottom-[-0.85rem] [&>button]:after:tw-right-[-0.25rem] [&>button]:after:tw-left-[-0.25rem]", + }, }) export class ItemActionComponent extends A11yCellDirective {} diff --git a/libs/components/src/item/item-content.component.ts b/libs/components/src/item/item-content.component.ts index 824b6a596a0..f6cc3f133ad 100644 --- a/libs/components/src/item/item-content.component.ts +++ b/libs/components/src/item/item-content.component.ts @@ -19,6 +19,10 @@ import { TypographyModule } from "../typography"; templateUrl: `item-content.component.html`, host: { class: + /** + * y-axis padding should be kept in sync with `item-action.component.ts`'s `top` and `bottom` units. + * we want this to be the same height as the `item-action`'s `:after` element + */ "fvw-target tw-outline-none tw-text-main hover:tw-text-main tw-no-underline hover:tw-no-underline tw-text-base tw-py-2 tw-px-4 bit-compact:tw-py-1.5 bit-compact:tw-px-2 tw-bg-transparent tw-w-full tw-border-none tw-flex tw-gap-4 tw-items-center tw-justify-between", }, changeDetection: ChangeDetectionStrategy.OnPush, diff --git a/libs/components/src/item/item.stories.ts b/libs/components/src/item/item.stories.ts index 675172565f1..5adf9d3c49d 100644 --- a/libs/components/src/item/item.stories.ts +++ b/libs/components/src/item/item.stories.ts @@ -70,7 +70,7 @@ export const Default: Story = { - + @@ -163,7 +163,7 @@ const multipleActionListTemplate = /*html*/ ` - + @@ -182,7 +182,7 @@ const multipleActionListTemplate = /*html*/ ` - + @@ -201,7 +201,7 @@ const multipleActionListTemplate = /*html*/ ` - + @@ -220,7 +220,7 @@ const multipleActionListTemplate = /*html*/ ` - + @@ -239,7 +239,7 @@ const multipleActionListTemplate = /*html*/ ` - + @@ -258,7 +258,7 @@ const multipleActionListTemplate = /*html*/ ` - + @@ -332,14 +332,14 @@ export const SingleActionWithBadge: Story = { Foobar - Auto-fill + Fill Helloooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo! - Auto-fill + Fill @@ -375,7 +375,7 @@ export const VirtualScrolling: Story = { - + @@ -405,7 +405,7 @@ export const WithoutBorderRadius: Story = { - + From 4b42092c93537d69f2357e9fc5090fd7f48600a2 Mon Sep 17 00:00:00 2001 From: cd-bitwarden <106776772+cd-bitwarden@users.noreply.github.com> Date: Fri, 20 Dec 2024 14:42:15 -0500 Subject: [PATCH 21/75] [PM-15047] Updating admin console 3 dots menu to use th enewer attachments modal (#12402) * Updating admin console 3 dots menu to use th enewer attachments modal * Update apps/web/src/app/vault/org-vault/vault.component.ts Co-authored-by: Shane Melton * lint fix --------- Co-authored-by: --global <> Co-authored-by: Shane Melton --- .../app/vault/org-vault/vault.component.ts | 38 ++++++++----------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/apps/web/src/app/vault/org-vault/vault.component.ts b/apps/web/src/app/vault/org-vault/vault.component.ts index e1aca825960..15453a8c064 100644 --- a/apps/web/src/app/vault/org-vault/vault.component.ts +++ b/apps/web/src/app/vault/org-vault/vault.component.ts @@ -104,6 +104,10 @@ import { } from "../components/vault-item-dialog/vault-item-dialog.component"; import { VaultItemEvent } from "../components/vault-items/vault-item-event"; import { VaultItemsModule } from "../components/vault-items/vault-items.module"; +import { + AttachmentDialogResult, + AttachmentsV2Component, +} from "../individual-vault/attachments-v2.component"; import { BulkDeleteDialogResult, openBulkDeleteDialog, @@ -119,7 +123,6 @@ import { VaultHeaderComponent } from "../org-vault/vault-header/vault-header.com import { getNestedCollectionTree } from "../utils/collection-utils"; import { AddEditComponent } from "./add-edit.component"; -import { AttachmentsComponent } from "./attachments.component"; import { BulkCollectionsDialogComponent, BulkCollectionsDialogResult, @@ -761,29 +764,18 @@ export class VaultComponent implements OnInit, OnDestroy { return; } - let madeAttachmentChanges = false; - - const [modal] = await this.modalService.openViewRef( - AttachmentsComponent, - this.attachmentsModalRef, - (comp) => { - comp.organization = this.organization; - comp.cipherId = cipher.id; - comp.onUploadedAttachment - .pipe(takeUntil(this.destroy$)) - .subscribe(() => (madeAttachmentChanges = true)); - comp.onDeletedAttachment - .pipe(takeUntil(this.destroy$)) - .subscribe(() => (madeAttachmentChanges = true)); - }, - ); - - modal.onClosed.pipe(takeUntil(this.destroy$)).subscribe(() => { - if (madeAttachmentChanges) { - this.refresh(); - } - madeAttachmentChanges = false; + const dialogRef = AttachmentsV2Component.open(this.dialogService, { + cipherId: cipher.id as CipherId, }); + + const result = await firstValueFrom(dialogRef.closed); + + if ( + result.action === AttachmentDialogResult.Removed || + result.action === AttachmentDialogResult.Uploaded + ) { + this.refresh(); + } } async addCipher(cipherType?: CipherType) { From 0619ef507fb6224ecbdc2813192766300752e68a Mon Sep 17 00:00:00 2001 From: Daniel James Smith <2670567+djsmith85@users.noreply.github.com> Date: Fri, 20 Dec 2024 20:46:34 +0100 Subject: [PATCH 22/75] [PM-7114] Remove legacy Send code from browser extension (#12342) * Remove Send grouping, type and state * Delete Send list and add-edit --------- Co-authored-by: Daniel James Smith --- .github/whitelist-capital-letters.txt | 1 - apps/browser/src/_locales/en/messages.json | 88 ---- .../src/models/browserSendComponentState.ts | 20 - apps/browser/src/popup/app-routing.module.ts | 22 +- apps/browser/src/popup/app.component.ts | 4 - apps/browser/src/popup/app.module.ts | 8 - .../src/popup/services/services.module.ts | 6 - .../send/components/send-list.component.html | 98 ----- .../send/components/send-list.component.ts | 38 -- .../popup/send/send-add-edit.component.html | 411 ------------------ .../popup/send/send-add-edit.component.ts | 140 ------ .../popup/send/send-groupings.component.html | 119 ----- .../popup/send/send-groupings.component.ts | 203 --------- .../tools/popup/send/send-type.component.html | 68 --- .../tools/popup/send/send-type.component.ts | 190 -------- .../browser-send-state.service.spec.ts | 59 --- .../services/browser-send-state.service.ts | 71 --- .../popup/services/key-definitions.spec.ts | 39 -- .../tools/popup/services/key-definitions.ts | 27 -- 19 files changed, 8 insertions(+), 1604 deletions(-) delete mode 100644 apps/browser/src/models/browserSendComponentState.ts delete mode 100644 apps/browser/src/tools/popup/send/components/send-list.component.html delete mode 100644 apps/browser/src/tools/popup/send/components/send-list.component.ts delete mode 100644 apps/browser/src/tools/popup/send/send-add-edit.component.html delete mode 100644 apps/browser/src/tools/popup/send/send-add-edit.component.ts delete mode 100644 apps/browser/src/tools/popup/send/send-groupings.component.html delete mode 100644 apps/browser/src/tools/popup/send/send-groupings.component.ts delete mode 100644 apps/browser/src/tools/popup/send/send-type.component.html delete mode 100644 apps/browser/src/tools/popup/send/send-type.component.ts delete mode 100644 apps/browser/src/tools/popup/services/browser-send-state.service.spec.ts delete mode 100644 apps/browser/src/tools/popup/services/browser-send-state.service.ts delete mode 100644 apps/browser/src/tools/popup/services/key-definitions.spec.ts delete mode 100644 apps/browser/src/tools/popup/services/key-definitions.ts diff --git a/.github/whitelist-capital-letters.txt b/.github/whitelist-capital-letters.txt index a320149281b..73d323851e5 100644 --- a/.github/whitelist-capital-letters.txt +++ b/.github/whitelist-capital-letters.txt @@ -37,7 +37,6 @@ ./apps/browser/store/windows/AppxManifest.xml ./apps/browser/src/background/nativeMessaging.background.ts ./apps/browser/src/models/browserComponentState.ts -./apps/browser/src/models/browserSendComponentState.ts ./apps/browser/src/models/browserGroupingsComponentState.ts ./apps/browser/src/models/biometricErrors.ts ./apps/browser/src/browser/safariApp.ts diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 4b7d9eb2992..8fe283ba289 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -1150,9 +1150,6 @@ "moveToOrganization": { "message": "Move to organization" }, - "share": { - "message": "Share" - }, "movedItemToOrg": { "message": "$ITEMNAME$ moved to $ORGNAME$", "placeholders": { @@ -2379,14 +2376,6 @@ "message": "Send details", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Search Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Add Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Text" }, @@ -2403,16 +2392,9 @@ "hideTextByDefault": { "message": "Hide text by default" }, - "maxAccessCountReached": { - "message": "Max access count reached", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Expired" }, - "pendingDeletion": { - "message": "Pending deletion" - }, "passwordProtected": { "message": "Password protected" }, @@ -2462,24 +2444,9 @@ "message": "Edit Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "What type of Send is this?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "A friendly name to describe this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "The file you want to send." - }, "deletionDate": { "message": "Deletion date" }, - "deletionDateDesc": { - "message": "The Send will be permanently deleted on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "The Send will be permanently deleted on this date.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2487,10 +2454,6 @@ "expirationDate": { "message": "Expiration date" }, - "expirationDateDesc": { - "message": "If set, access to this Send will expire on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 day" }, @@ -2506,43 +2469,10 @@ "custom": { "message": "Custom" }, - "maximumAccessCount": { - "message": "Maximum Access Count" - }, - "maximumAccessCountDesc": { - "message": "If set, users will no longer be able to access this Send once the maximum access count is reached.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Optionally require a password for users to access this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Private notes about this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Deactivate this Send so that no one can access it.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Copy this Send's link to clipboard upon save.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "The text you want to send." - }, - "sendHideText": { - "message": "Hide this Send's text by default.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Current access count" - }, "createSend": { "message": "New Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2625,18 +2555,6 @@ "sendFileCalloutHeader": { "message": "Before you start" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "To use a calendar style date picker", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "click here", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "to pop out your window.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "The expiration date provided is not valid." }, @@ -2652,15 +2570,9 @@ "dateParsingError": { "message": "There was an error saving your deletion and expiration dates." }, - "hideEmail": { - "message": "Hide my email address from recipients." - }, "hideYourEmail": { "message": "Hide your email address from viewers." }, - "sendOptionsPolicyInEffect": { - "message": "One or more organization policies are affecting your Send options." - }, "passwordPrompt": { "message": "Master password re-prompt" }, diff --git a/apps/browser/src/models/browserSendComponentState.ts b/apps/browser/src/models/browserSendComponentState.ts deleted file mode 100644 index 204595df272..00000000000 --- a/apps/browser/src/models/browserSendComponentState.ts +++ /dev/null @@ -1,20 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; -import { DeepJsonify } from "@bitwarden/common/types/deep-jsonify"; - -import { BrowserComponentState } from "./browserComponentState"; - -export class BrowserSendComponentState extends BrowserComponentState { - sends: SendView[]; - - static fromJSON(json: DeepJsonify) { - if (json == null) { - return null; - } - - return Object.assign(new BrowserSendComponentState(), json, { - sends: json.sends?.map((s) => SendView.fromJSON(s)), - }); - } -} diff --git a/apps/browser/src/popup/app-routing.module.ts b/apps/browser/src/popup/app-routing.module.ts index 1f42c1cf31d..0a6ff456938 100644 --- a/apps/browser/src/popup/app-routing.module.ts +++ b/apps/browser/src/popup/app-routing.module.ts @@ -86,9 +86,6 @@ import BrowserPopupUtils from "../platform/popup/browser-popup-utils"; import { popupRouterCacheGuard } from "../platform/popup/view-cache/popup-router-cache.service"; import { CredentialGeneratorHistoryComponent } from "../tools/popup/generator/credential-generator-history.component"; import { CredentialGeneratorComponent } from "../tools/popup/generator/credential-generator.component"; -import { SendAddEditComponent } from "../tools/popup/send/send-add-edit.component"; -import { SendGroupingsComponent } from "../tools/popup/send/send-groupings.component"; -import { SendTypeComponent } from "../tools/popup/send/send-type.component"; import { SendAddEditComponent as SendAddEditV2Component } from "../tools/popup/send-v2/add-edit/send-add-edit.component"; import { SendCreatedComponent } from "../tools/popup/send-v2/send-created/send-created.component"; import { SendV2Component } from "../tools/popup/send-v2/send-v2.component"; @@ -415,21 +412,17 @@ const routes: Routes = [ data: { elevation: 1 } satisfies RouteDataProperties, }), { - path: "send-type", - component: SendTypeComponent, - canActivate: [authGuard], - data: { elevation: 1 } satisfies RouteDataProperties, - }, - ...extensionRefreshSwap(SendAddEditComponent, SendAddEditV2Component, { path: "add-send", + component: SendAddEditV2Component, canActivate: [authGuard], data: { elevation: 1 } satisfies RouteDataProperties, - }), - ...extensionRefreshSwap(SendAddEditComponent, SendAddEditV2Component, { + }, + { path: "edit-send", + component: SendAddEditV2Component, canActivate: [authGuard], data: { elevation: 1 } satisfies RouteDataProperties, - }), + }, { path: "send-created", component: SendCreatedComponent, @@ -768,11 +761,12 @@ const routes: Routes = [ canActivate: [authGuard], data: { elevation: 0 } satisfies RouteDataProperties, }, - ...extensionRefreshSwap(SendGroupingsComponent, SendV2Component, { + { path: "send", + component: SendV2Component, canActivate: [authGuard], data: { elevation: 0 } satisfies RouteDataProperties, - }), + }, ], }, { diff --git a/apps/browser/src/popup/app.component.ts b/apps/browser/src/popup/app.component.ts index 248050ed61c..3382e24689a 100644 --- a/apps/browser/src/popup/app.component.ts +++ b/apps/browser/src/popup/app.component.ts @@ -26,7 +26,6 @@ import { PopupCompactModeService } from "../platform/popup/layout/popup-compact- import { PopupWidthService } from "../platform/popup/layout/popup-width.service"; import { PopupViewCacheService } from "../platform/popup/view-cache/popup-view-cache.service"; import { initPopupClosedListener } from "../platform/services/popup-view-cache-background.service"; -import { BrowserSendStateService } from "../tools/popup/services/browser-send-state.service"; import { VaultBrowserStateService } from "../vault/services/vault-browser-state.service"; import { routerTransition } from "./app-routing.animations"; @@ -57,7 +56,6 @@ export class AppComponent implements OnInit, OnDestroy { private i18nService: I18nService, private router: Router, private stateService: StateService, - private browserSendStateService: BrowserSendStateService, private vaultBrowserStateService: VaultBrowserStateService, private cipherService: CipherService, private changeDetectorRef: ChangeDetectorRef, @@ -243,8 +241,6 @@ export class AppComponent implements OnInit, OnDestroy { await Promise.all([ this.vaultBrowserStateService.setBrowserGroupingsComponentState(null), this.vaultBrowserStateService.setBrowserVaultItemsComponentState(null), - this.browserSendStateService.setBrowserSendComponentState(null), - this.browserSendStateService.setBrowserSendTypeComponentState(null), ]); } diff --git a/apps/browser/src/popup/app.module.ts b/apps/browser/src/popup/app.module.ts index c2697b2d490..b547078084b 100644 --- a/apps/browser/src/popup/app.module.ts +++ b/apps/browser/src/popup/app.module.ts @@ -56,10 +56,6 @@ import { PopupHeaderComponent } from "../platform/popup/layout/popup-header.comp import { PopupPageComponent } from "../platform/popup/layout/popup-page.component"; import { PopupTabNavigationComponent } from "../platform/popup/layout/popup-tab-navigation.component"; import { FilePopoutCalloutComponent } from "../tools/popup/components/file-popout-callout.component"; -import { SendListComponent } from "../tools/popup/send/components/send-list.component"; -import { SendAddEditComponent } from "../tools/popup/send/send-add-edit.component"; -import { SendGroupingsComponent } from "../tools/popup/send/send-groupings.component"; -import { SendTypeComponent } from "../tools/popup/send/send-type.component"; import { ActionButtonsComponent } from "../vault/popup/components/action-buttons.component"; import { CipherRowComponent } from "../vault/popup/components/cipher-row.component"; import { AddEditCustomFieldsComponent } from "../vault/popup/components/vault/add-edit-custom-fields.component"; @@ -161,10 +157,6 @@ import "../platform/popup/locales"; PasswordHistoryComponent, PremiumComponent, RegisterComponent, - SendAddEditComponent, - SendGroupingsComponent, - SendListComponent, - SendTypeComponent, SetPasswordComponent, VaultSettingsComponent, ShareComponent, diff --git a/apps/browser/src/popup/services/services.module.ts b/apps/browser/src/popup/services/services.module.ts index 7014d908ac3..5b27833636f 100644 --- a/apps/browser/src/popup/services/services.module.ts +++ b/apps/browser/src/popup/services/services.module.ts @@ -152,7 +152,6 @@ import { ForegroundSyncService } from "../../platform/sync/foreground-sync.servi import { fromChromeRuntimeMessaging } from "../../platform/utils/from-chrome-runtime-messaging"; import { ExtensionLockComponentService } from "../../services/extension-lock-component.service"; import { ForegroundVaultTimeoutService } from "../../services/vault-timeout/foreground-vault-timeout.service"; -import { BrowserSendStateService } from "../../tools/popup/services/browser-send-state.service"; import { FilePopoutUtilsService } from "../../tools/popup/services/file-popout-utils.service"; import { Fido2UserVerificationService } from "../../vault/services/fido2-user-verification.service"; import { VaultBrowserStateService } from "../../vault/services/vault-browser-state.service"; @@ -473,11 +472,6 @@ const safeProviders: SafeProvider[] = [ useClass: UserNotificationSettingsService, deps: [StateProvider], }), - safeProvider({ - provide: BrowserSendStateService, - useClass: BrowserSendStateService, - deps: [StateProvider], - }), safeProvider({ provide: MessageListener, useFactory: (subject: Subject>>, ngZone: NgZone) => diff --git a/apps/browser/src/tools/popup/send/components/send-list.component.html b/apps/browser/src/tools/popup/send/components/send-list.component.html deleted file mode 100644 index 05c8e3e3754..00000000000 --- a/apps/browser/src/tools/popup/send/components/send-list.component.html +++ /dev/null @@ -1,98 +0,0 @@ -
    - -
    - - - -
    -
    diff --git a/apps/browser/src/tools/popup/send/components/send-list.component.ts b/apps/browser/src/tools/popup/send/components/send-list.component.ts deleted file mode 100644 index bab818cce6a..00000000000 --- a/apps/browser/src/tools/popup/send/components/send-list.component.ts +++ /dev/null @@ -1,38 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { Component, EventEmitter, Input, Output } from "@angular/core"; - -import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; -import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; - -@Component({ - selector: "app-send-list", - templateUrl: "send-list.component.html", -}) -export class SendListComponent { - @Input() sends: SendView[]; - @Input() title: string; - @Input() disabledByPolicy = false; - @Output() onSelected = new EventEmitter(); - @Output() onCopySendLink = new EventEmitter(); - @Output() onRemovePassword = new EventEmitter(); - @Output() onDeleteSend = new EventEmitter(); - - sendType = SendType; - - selectSend(s: SendView) { - this.onSelected.emit(s); - } - - copySendLink(s: SendView) { - this.onCopySendLink.emit(s); - } - - removePassword(s: SendView) { - this.onRemovePassword.emit(s); - } - - delete(s: SendView) { - this.onDeleteSend.emit(s); - } -} diff --git a/apps/browser/src/tools/popup/send/send-add-edit.component.html b/apps/browser/src/tools/popup/send/send-add-edit.component.html deleted file mode 100644 index 38c8a8175bb..00000000000 --- a/apps/browser/src/tools/popup/send/send-add-edit.component.html +++ /dev/null @@ -1,411 +0,0 @@ -
    -
    -
    - -
    -

    - {{ title }} -

    -
    - -
    -
    -
    - - - {{ "sendDisabledWarning" | i18n }} - - - {{ "sendOptionsPolicyInEffect" | i18n }} - - - - -
    -
    -
    - - -
    -
    - -
    - -
    -
    -
    - -
    - - -
    -
    -
    -
    - -
    -
    -
    - -
    {{ send.file.fileName }} ({{ send.file.sizeName }})
    -
    -
    - - -
    -
    - -
    - -
    -
    -
    - - -
    -
    - -
    -
    - - -
    -
    -
    - -
    -

    - {{ "share" | i18n }} -

    -
    - -
    - - -
    -
    -
    - -
    -

    - -

    -
    -
    - -
    -
    - -
    - - -
    -
    - -
    -
    -
    - - -
    -
    - -
    - - -
    -
    - -
    - - -
    -
    - -
    -
    -
    -
    - - -
    - -
    -
    - -
    - -
    -
    -
    - - -
    -
    - -
    - -
    -
    -
    - - -
    -
    -
    - -
    -
    -
    -
    - - - -
    -
    - -
    -
    -
    - -
    - -
    -
    -
    - - -
    -
    - -
    - -
    -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    -
    - -
    -
    - -
    -
    -
    -
    diff --git a/apps/browser/src/tools/popup/send/send-add-edit.component.ts b/apps/browser/src/tools/popup/send/send-add-edit.component.ts deleted file mode 100644 index 66b0355bb76..00000000000 --- a/apps/browser/src/tools/popup/send/send-add-edit.component.ts +++ /dev/null @@ -1,140 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { DatePipe, Location } from "@angular/common"; -import { Component, OnInit } from "@angular/core"; -import { FormBuilder } from "@angular/forms"; -import { ActivatedRoute, Router } from "@angular/router"; -import { first } from "rxjs/operators"; - -import { AddEditComponent as BaseAddEditComponent } from "@bitwarden/angular/tools/send/add-edit.component"; -import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; -import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; -import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; -import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; -import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; -import { DialogService, ToastService } from "@bitwarden/components"; - -import BrowserPopupUtils from "../../../platform/popup/browser-popup-utils"; -import { FilePopoutUtilsService } from "../services/file-popout-utils.service"; - -@Component({ - selector: "app-send-add-edit", - templateUrl: "send-add-edit.component.html", -}) -// eslint-disable-next-line rxjs-angular/prefer-takeuntil -export class SendAddEditComponent extends BaseAddEditComponent implements OnInit { - // Options header - showOptions = false; - // File visibility - isFirefox = false; - inPopout = false; - showFileSelector = false; - - constructor( - i18nService: I18nService, - platformUtilsService: PlatformUtilsService, - stateService: StateService, - messagingService: MessagingService, - policyService: PolicyService, - environmentService: EnvironmentService, - datePipe: DatePipe, - sendService: SendService, - private route: ActivatedRoute, - private router: Router, - private location: Location, - logService: LogService, - sendApiService: SendApiService, - dialogService: DialogService, - formBuilder: FormBuilder, - private filePopoutUtilsService: FilePopoutUtilsService, - billingAccountProfileStateService: BillingAccountProfileStateService, - accountService: AccountService, - toastService: ToastService, - ) { - super( - i18nService, - platformUtilsService, - environmentService, - datePipe, - sendService, - messagingService, - policyService, - logService, - stateService, - sendApiService, - dialogService, - formBuilder, - billingAccountProfileStateService, - accountService, - toastService, - ); - } - - popOutWindow() { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - BrowserPopupUtils.openCurrentPagePopout(window); - } - - async ngOnInit() { - // File visibility - this.showFileSelector = - !this.editMode && !this.filePopoutUtilsService.showFilePopoutMessage(window); - this.inPopout = BrowserPopupUtils.inPopout(window); - this.isFirefox = this.platformUtilsService.isFirefox(); - - // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe - this.route.queryParams.pipe(first()).subscribe(async (params) => { - if (params.sendId) { - this.sendId = params.sendId; - } - if (params.type) { - const type = parseInt(params.type, null); - this.type = type; - } - await super.ngOnInit(); - }); - - window.setTimeout(() => { - if (!this.editMode) { - document.getElementById("name").focus(); - } - }, 200); - } - - async submit(): Promise { - if (await super.submit()) { - this.cancel(); - return true; - } - - return false; - } - - async delete(): Promise { - if (await super.delete()) { - this.cancel(); - return true; - } - - return false; - } - - cancel() { - // If true, the window was pop'd out on the add-send page. location.back will not work - const isPopup = (window as any)?.previousPopupUrl?.startsWith("/add-send") ?? false; - if (!isPopup) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.router.navigate(["tabs/send"]); - } else { - this.location.back(); - } - } -} diff --git a/apps/browser/src/tools/popup/send/send-groupings.component.html b/apps/browser/src/tools/popup/send/send-groupings.component.html deleted file mode 100644 index 213afdfa227..00000000000 --- a/apps/browser/src/tools/popup/send/send-groupings.component.html +++ /dev/null @@ -1,119 +0,0 @@ - -
    - -
    -

    {{ "send" | i18n }}

    - -
    - -
    -
    -
    - - {{ "sendDisabledWarning" | i18n }} - -
    - - - -

    {{ "noItemsInList" | i18n }}

    - -
    -
    - -
    -

    - {{ "types" | i18n }} -

    -
    - - -
    -
    -
    -

    - {{ "allSends" | i18n }} -
    {{ sends.length }}
    -

    -
    - -
    -
    -
    - -
    -

    {{ "noItemsInList" | i18n }}

    -
    -
    -
    - - -
    -
    -
    -
    diff --git a/apps/browser/src/tools/popup/send/send-groupings.component.ts b/apps/browser/src/tools/popup/send/send-groupings.component.ts deleted file mode 100644 index 80c2de1cdac..00000000000 --- a/apps/browser/src/tools/popup/send/send-groupings.component.ts +++ /dev/null @@ -1,203 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { ChangeDetectorRef, Component, NgZone, OnDestroy, OnInit } from "@angular/core"; -import { Router } from "@angular/router"; - -import { SendComponent as BaseSendComponent } from "@bitwarden/angular/tools/send/send.component"; -import { SearchService } from "@bitwarden/common/abstractions/search.service"; -import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; -import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; -import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.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 { SendType } from "@bitwarden/common/tools/send/enums/send-type"; -import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; -import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; -import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; -import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; -import { DialogService, ToastService } from "@bitwarden/components"; - -import { BrowserSendComponentState } from "../../../models/browserSendComponentState"; -import BrowserPopupUtils from "../../../platform/popup/browser-popup-utils"; -import { BrowserSendStateService } from "../services/browser-send-state.service"; - -const ComponentId = "SendComponent"; - -@Component({ - selector: "app-send-groupings", - templateUrl: "send-groupings.component.html", -}) -export class SendGroupingsComponent extends BaseSendComponent implements OnInit, OnDestroy { - // Header - showLeftHeader = true; - // State Handling - state: BrowserSendComponentState; - private loadedTimeout: number; - - constructor( - sendService: SendService, - i18nService: I18nService, - platformUtilsService: PlatformUtilsService, - environmentService: EnvironmentService, - ngZone: NgZone, - policyService: PolicyService, - searchService: SearchService, - private stateService: BrowserSendStateService, - private router: Router, - private syncService: SyncService, - private changeDetectorRef: ChangeDetectorRef, - private broadcasterService: BroadcasterService, - logService: LogService, - sendApiService: SendApiService, - dialogService: DialogService, - toastService: ToastService, - ) { - super( - sendService, - i18nService, - platformUtilsService, - environmentService, - ngZone, - searchService, - policyService, - logService, - sendApiService, - dialogService, - toastService, - ); - this.onSuccessfulLoad = async () => { - this.selectAll(); - }; - } - - async ngOnInit() { - // Determine Header details - this.showLeftHeader = !( - BrowserPopupUtils.inSidebar(window) && this.platformUtilsService.isFirefox() - ); - // Clear state of Send Type Component - await this.stateService.setBrowserSendTypeComponentState(null); - // Let super class finish - await super.ngOnInit(); - // Handle State Restore if necessary - const restoredScopeState = await this.restoreState(); - if (this.state?.searchText != null) { - this.searchText = this.state.searchText; - } - - if (!this.syncService.syncInProgress) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.load(); - } else { - this.loadedTimeout = window.setTimeout(() => { - if (!this.loaded) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.load(); - } - }, 5000); - } - - if (!this.syncService.syncInProgress || restoredScopeState) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - BrowserPopupUtils.setContentScrollY(window, this.state?.scrollY); - } - - // Load all sends if sync completed in background - this.broadcasterService.subscribe(ComponentId, (message: any) => { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.ngZone.run(async () => { - switch (message.command) { - case "syncCompleted": - window.setTimeout(() => { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.load(); - }, 500); - break; - default: - break; - } - - this.changeDetectorRef.detectChanges(); - }); - }); - } - - ngOnDestroy() { - // Remove timeout - if (this.loadedTimeout != null) { - window.clearTimeout(this.loadedTimeout); - } - // Save state - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.saveState(); - // Unsubscribe - this.broadcasterService.unsubscribe(ComponentId); - } - - async selectType(type: SendType) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.router.navigate(["/send-type"], { queryParams: { type: type } }); - } - - async selectSend(s: SendView) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.router.navigate(["/edit-send"], { queryParams: { sendId: s.id } }); - } - - async addSend() { - if (this.disableSend) { - return; - } - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.router.navigate(["/add-send"]); - } - - async removePassword(s: SendView): Promise { - if (this.disableSend) { - return; - } - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - super.removePassword(s); - } - - showSearching() { - return this.hasSearched || (!this.searchPending && this.isSearchable); - } - - getSendCount(sends: SendView[], type: SendType): number { - return sends.filter((s) => s.type === type).length; - } - - private async saveState() { - this.state = Object.assign(new BrowserSendComponentState(), { - scrollY: BrowserPopupUtils.getContentScrollY(window), - searchText: this.searchText, - sends: this.sends, - }); - await this.stateService.setBrowserSendComponentState(this.state); - } - - private async restoreState(): Promise { - this.state = await this.stateService.getBrowserSendComponentState(); - if (this.state == null) { - return false; - } - - if (this.state.sends != null) { - this.sends = this.state.sends; - } - - return true; - } -} diff --git a/apps/browser/src/tools/popup/send/send-type.component.html b/apps/browser/src/tools/popup/send/send-type.component.html deleted file mode 100644 index 0ad6bed2881..00000000000 --- a/apps/browser/src/tools/popup/send/send-type.component.html +++ /dev/null @@ -1,68 +0,0 @@ -
    -
    - -
    -

    {{ "send" | i18n }}

    - -
    - -
    -
    -
    - - {{ "sendDisabledWarning" | i18n }} - -
    - - -

    {{ "noItemsInList" | i18n }}

    - -
    -
    -
    -

    - {{ groupingTitle }} - {{ filteredSends.length }} -

    -
    - - -
    -
    -
    diff --git a/apps/browser/src/tools/popup/send/send-type.component.ts b/apps/browser/src/tools/popup/send/send-type.component.ts deleted file mode 100644 index bcc24b443fc..00000000000 --- a/apps/browser/src/tools/popup/send/send-type.component.ts +++ /dev/null @@ -1,190 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { Location } from "@angular/common"; -import { ChangeDetectorRef, Component, NgZone, OnDestroy, OnInit } from "@angular/core"; -import { ActivatedRoute, Router } from "@angular/router"; -import { first } from "rxjs/operators"; - -import { SendComponent as BaseSendComponent } from "@bitwarden/angular/tools/send/send.component"; -import { SearchService } from "@bitwarden/common/abstractions/search.service"; -import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; -import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; -import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.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 { SendType } from "@bitwarden/common/tools/send/enums/send-type"; -import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; -import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; -import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; -import { DialogService, ToastService } from "@bitwarden/components"; - -import { BrowserComponentState } from "../../../models/browserComponentState"; -import BrowserPopupUtils from "../../../platform/popup/browser-popup-utils"; -import { BrowserSendStateService } from "../services/browser-send-state.service"; - -const ComponentId = "SendTypeComponent"; - -@Component({ - selector: "app-send-type", - templateUrl: "send-type.component.html", -}) -export class SendTypeComponent extends BaseSendComponent implements OnInit, OnDestroy { - groupingTitle: string; - // State Handling - state: BrowserComponentState; - private refreshTimeout: number; - private applySavedState = true; - - constructor( - sendService: SendService, - i18nService: I18nService, - platformUtilsService: PlatformUtilsService, - environmentService: EnvironmentService, - ngZone: NgZone, - policyService: PolicyService, - searchService: SearchService, - private stateService: BrowserSendStateService, - private route: ActivatedRoute, - private location: Location, - private changeDetectorRef: ChangeDetectorRef, - private broadcasterService: BroadcasterService, - private router: Router, - logService: LogService, - sendApiService: SendApiService, - dialogService: DialogService, - toastService: ToastService, - ) { - super( - sendService, - i18nService, - platformUtilsService, - environmentService, - ngZone, - searchService, - policyService, - logService, - sendApiService, - dialogService, - toastService, - ); - this.onSuccessfulLoad = async () => { - this.selectType(this.type); - }; - this.applySavedState = - (window as any).previousPopupUrl != null && - !(window as any).previousPopupUrl.startsWith("/send-type"); - } - - async ngOnInit() { - // Let super class finish - await super.ngOnInit(); - // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe - this.route.queryParams.pipe(first()).subscribe(async (params) => { - if (this.applySavedState) { - this.state = await this.stateService.getBrowserSendTypeComponentState(); - if (this.state?.searchText != null) { - this.searchText = this.state.searchText; - } - } - - if (params.type != null) { - this.type = parseInt(params.type, null); - switch (this.type) { - case SendType.Text: - this.groupingTitle = this.i18nService.t("sendTypeText"); - break; - case SendType.File: - this.groupingTitle = this.i18nService.t("sendTypeFile"); - break; - default: - break; - } - await this.load((s) => s.type === this.type); - } - - // Restore state and remove reference - if (this.applySavedState && this.state != null) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - BrowserPopupUtils.setContentScrollY(window, this.state?.scrollY); - } - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.stateService.setBrowserSendTypeComponentState(null); - }); - - // Refresh Send list if sync completed in background - this.broadcasterService.subscribe(ComponentId, (message: any) => { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.ngZone.run(async () => { - switch (message.command) { - case "syncCompleted": - if (message.successfully) { - this.refreshTimeout = window.setTimeout(() => { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.refresh(); - }, 500); - } - break; - default: - break; - } - - this.changeDetectorRef.detectChanges(); - }); - }); - } - - ngOnDestroy() { - // Remove timeout - if (this.refreshTimeout != null) { - window.clearTimeout(this.refreshTimeout); - } - // Save state - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.saveState(); - // Unsubscribe - this.broadcasterService.unsubscribe(ComponentId); - } - - async selectSend(s: SendView) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.router.navigate(["/edit-send"], { queryParams: { sendId: s.id } }); - } - - async addSend() { - if (this.disableSend) { - return; - } - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.router.navigate(["/add-send"], { queryParams: { type: this.type } }); - } - - async removePassword(s: SendView): Promise { - if (this.disableSend) { - return; - } - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - super.removePassword(s); - } - - back() { - (window as any).routeDirection = "b"; - this.location.back(); - } - - private async saveState() { - this.state = { - scrollY: BrowserPopupUtils.getContentScrollY(window), - searchText: this.searchText, - }; - await this.stateService.setBrowserSendTypeComponentState(this.state); - } -} diff --git a/apps/browser/src/tools/popup/services/browser-send-state.service.spec.ts b/apps/browser/src/tools/popup/services/browser-send-state.service.spec.ts deleted file mode 100644 index 6f0ae1455ad..00000000000 --- a/apps/browser/src/tools/popup/services/browser-send-state.service.spec.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { - FakeAccountService, - mockAccountServiceWith, -} from "@bitwarden/common/../spec/fake-account-service"; -import { FakeStateProvider } from "@bitwarden/common/../spec/fake-state-provider"; -import { awaitAsync } from "@bitwarden/common/../spec/utils"; - -import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { UserId } from "@bitwarden/common/types/guid"; - -import { BrowserComponentState } from "../../../models/browserComponentState"; -import { BrowserSendComponentState } from "../../../models/browserSendComponentState"; - -import { BrowserSendStateService } from "./browser-send-state.service"; - -describe("Browser Send State Service", () => { - let stateProvider: FakeStateProvider; - - let accountService: FakeAccountService; - let stateService: BrowserSendStateService; - const mockUserId = Utils.newGuid() as UserId; - - beforeEach(() => { - accountService = mockAccountServiceWith(mockUserId); - stateProvider = new FakeStateProvider(accountService); - - stateService = new BrowserSendStateService(stateProvider); - }); - - describe("getBrowserSendComponentState", () => { - it("should return BrowserSendComponentState", async () => { - const state = new BrowserSendComponentState(); - state.scrollY = 0; - state.searchText = "test"; - - await stateService.setBrowserSendComponentState(state); - - await awaitAsync(); - - const actual = await stateService.getBrowserSendComponentState(); - expect(actual).toStrictEqual(state); - }); - }); - - describe("getBrowserSendTypeComponentState", () => { - it("should return BrowserComponentState", async () => { - const state = new BrowserComponentState(); - state.scrollY = 0; - state.searchText = "test"; - - await stateService.setBrowserSendTypeComponentState(state); - - await awaitAsync(); - - const actual = await stateService.getBrowserSendTypeComponentState(); - expect(actual).toStrictEqual(state); - }); - }); -}); diff --git a/apps/browser/src/tools/popup/services/browser-send-state.service.ts b/apps/browser/src/tools/popup/services/browser-send-state.service.ts deleted file mode 100644 index d6c5ae4fd10..00000000000 --- a/apps/browser/src/tools/popup/services/browser-send-state.service.ts +++ /dev/null @@ -1,71 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { Observable, firstValueFrom } from "rxjs"; - -import { ActiveUserState, StateProvider } from "@bitwarden/common/platform/state"; - -import { BrowserComponentState } from "../../../models/browserComponentState"; -import { BrowserSendComponentState } from "../../../models/browserSendComponentState"; - -import { BROWSER_SEND_COMPONENT, BROWSER_SEND_TYPE_COMPONENT } from "./key-definitions"; - -/** Get or set the active user's component state for the Send browser component - */ -export class BrowserSendStateService { - /** Observable that contains the current state for active user Sends including the send data and type counts - * along with the search text and scroll position - */ - browserSendComponentState$: Observable; - - /** Observable that contains the current state for active user Sends that only includes the search text - * and scroll position - */ - browserSendTypeComponentState$: Observable; - - private activeUserBrowserSendComponentState: ActiveUserState; - private activeUserBrowserSendTypeComponentState: ActiveUserState; - - constructor(protected stateProvider: StateProvider) { - this.activeUserBrowserSendComponentState = this.stateProvider.getActive(BROWSER_SEND_COMPONENT); - this.browserSendComponentState$ = this.activeUserBrowserSendComponentState.state$; - - this.activeUserBrowserSendTypeComponentState = this.stateProvider.getActive( - BROWSER_SEND_TYPE_COMPONENT, - ); - this.browserSendTypeComponentState$ = this.activeUserBrowserSendTypeComponentState.state$; - } - - /** Get the active user's browser send component state - * @returns { BrowserSendComponentState } contains the sends and type counts along with the scroll position and search text for the - * send component on the browser - */ - async getBrowserSendComponentState(): Promise { - return await firstValueFrom(this.browserSendComponentState$); - } - - /** Set the active user's browser send component state - * @param { BrowserSendComponentState } value sets the sends along with the scroll position and search text for - * the send component on the browser - */ - async setBrowserSendComponentState(value: BrowserSendComponentState): Promise { - await this.activeUserBrowserSendComponentState.update(() => value, { - shouldUpdate: (current) => !(current == null && value == null), - }); - } - - /** Get the active user's browser component state - * @returns { BrowserComponentState } contains the scroll position and search text for the sends menu on the browser - */ - async getBrowserSendTypeComponentState(): Promise { - return await firstValueFrom(this.browserSendTypeComponentState$); - } - - /** Set the active user's browser component state - * @param { BrowserComponentState } value set the scroll position and search text for the send component on the browser - */ - async setBrowserSendTypeComponentState(value: BrowserComponentState): Promise { - await this.activeUserBrowserSendTypeComponentState.update(() => value, { - shouldUpdate: (current) => !(current == null && value == null), - }); - } -} diff --git a/apps/browser/src/tools/popup/services/key-definitions.spec.ts b/apps/browser/src/tools/popup/services/key-definitions.spec.ts deleted file mode 100644 index 7517771669c..00000000000 --- a/apps/browser/src/tools/popup/services/key-definitions.spec.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Jsonify } from "type-fest"; - -import { BrowserSendComponentState } from "../../../models/browserSendComponentState"; - -import { BROWSER_SEND_COMPONENT, BROWSER_SEND_TYPE_COMPONENT } from "./key-definitions"; - -describe("Key definitions", () => { - describe("BROWSER_SEND_COMPONENT", () => { - it("should deserialize BrowserSendComponentState", () => { - const keyDef = BROWSER_SEND_COMPONENT; - - const expectedState = { - scrollY: 0, - searchText: "test", - }; - - const result = keyDef.deserializer( - JSON.parse(JSON.stringify(expectedState)) as Jsonify, - ); - - expect(result).toEqual(expectedState); - }); - }); - - describe("BROWSER_SEND_TYPE_COMPONENT", () => { - it("should deserialize BrowserComponentState", () => { - const keyDef = BROWSER_SEND_TYPE_COMPONENT; - - const expectedState = { - scrollY: 0, - searchText: "test", - }; - - const result = keyDef.deserializer(JSON.parse(JSON.stringify(expectedState))); - - expect(result).toEqual(expectedState); - }); - }); -}); diff --git a/apps/browser/src/tools/popup/services/key-definitions.ts b/apps/browser/src/tools/popup/services/key-definitions.ts deleted file mode 100644 index 87c6da126cb..00000000000 --- a/apps/browser/src/tools/popup/services/key-definitions.ts +++ /dev/null @@ -1,27 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { Jsonify } from "type-fest"; - -import { BROWSER_SEND_MEMORY, UserKeyDefinition } from "@bitwarden/common/platform/state"; - -import { BrowserComponentState } from "../../../models/browserComponentState"; -import { BrowserSendComponentState } from "../../../models/browserSendComponentState"; - -export const BROWSER_SEND_COMPONENT = new UserKeyDefinition( - BROWSER_SEND_MEMORY, - "browser_send_component", - { - deserializer: (obj: Jsonify) => - BrowserSendComponentState.fromJSON(obj), - clearOn: ["logout", "lock"], - }, -); - -export const BROWSER_SEND_TYPE_COMPONENT = new UserKeyDefinition( - BROWSER_SEND_MEMORY, - "browser_send_type_component", - { - deserializer: (obj: Jsonify) => BrowserComponentState.fromJSON(obj), - clearOn: ["logout", "lock"], - }, -); From 23210f4a39f7bf61c8b6224b9eda7c1d29cd7380 Mon Sep 17 00:00:00 2001 From: Victoria League Date: Mon, 23 Dec 2024 10:37:12 -0500 Subject: [PATCH 23/75] [PM-16102] Display autofill shortcut on Fill button hover (#12502) --- .../vault-list-items-container.component.html | 2 +- .../vault-list-items-container.component.ts | 29 +++++++++++++++++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.html b/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.html index a493ea2171c..067c8dbdf0b 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.html @@ -55,7 +55,7 @@

    bitBadge variant="primary" (click)="doAutofill(cipher)" - [title]="'autofillTitle' | i18n: cipher.name" + [title]="autofillShortcutTooltip() ?? ('autofillTitle' | i18n: cipher.name)" [attr.aria-label]="'autofillTitle' | i18n: cipher.name" > {{ "fill" | i18n }} diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.ts b/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.ts index 3b139d79015..b4022560c35 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.ts @@ -2,12 +2,22 @@ // @ts-strict-ignore import { ScrollingModule } from "@angular/cdk/scrolling"; import { CommonModule } from "@angular/common"; -import { booleanAttribute, Component, EventEmitter, inject, Input, Output } from "@angular/core"; +import { + AfterViewInit, + booleanAttribute, + Component, + EventEmitter, + inject, + Input, + Output, + signal, +} from "@angular/core"; import { Router, RouterLink } from "@angular/router"; import { map } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { @@ -50,7 +60,7 @@ import { ItemMoreOptionsComponent } from "../item-more-options/item-more-options templateUrl: "vault-list-items-container.component.html", standalone: true, }) -export class VaultListItemsContainerComponent { +export class VaultListItemsContainerComponent implements AfterViewInit { private compactModeService = inject(CompactModeService); /** @@ -133,14 +143,29 @@ export class VaultListItemsContainerComponent { return cipher.collections[0]?.name; } + protected autofillShortcutTooltip = signal(undefined); + constructor( private i18nService: I18nService, private vaultPopupAutofillService: VaultPopupAutofillService, private passwordRepromptService: PasswordRepromptService, private cipherService: CipherService, private router: Router, + private platformUtilsService: PlatformUtilsService, ) {} + async ngAfterViewInit() { + const autofillShortcut = await this.platformUtilsService.getAutofillKeyboardShortcut(); + + if (autofillShortcut === "") { + this.autofillShortcutTooltip.set(undefined); + } else { + const autofillTitle = this.i18nService.t("autoFill"); + + this.autofillShortcutTooltip.set(`${autofillTitle} ${autofillShortcut}`); + } + } + /** * Launches the login cipher in a new browser tab. */ From 395258d63ed39305a6fa7f21becbc842210a8db5 Mon Sep 17 00:00:00 2001 From: Victoria League Date: Mon, 23 Dec 2024 12:08:52 -0500 Subject: [PATCH 24/75] [PM-16102] Add min width on interactive badges (#12514) --- libs/components/src/badge/badge.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/components/src/badge/badge.component.ts b/libs/components/src/badge/badge.component.ts index 29644a11ac7..0f8a64ee998 100644 --- a/libs/components/src/badge/badge.component.ts +++ b/libs/components/src/badge/badge.component.ts @@ -65,7 +65,7 @@ export class BadgeComponent implements FocusableElement { "disabled:tw-cursor-not-allowed", ] .concat(styles[this.variant]) - .concat(this.hasHoverEffects ? hoverStyles[this.variant] : []) + .concat(this.hasHoverEffects ? [...hoverStyles[this.variant], "tw-min-w-10"] : []) .concat(this.truncate ? this.maxWidthClass : []); } @HostBinding("attr.title") get titleAttr() { From 35f4edddcff85b55e191857407c7873c60174f8e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 24 Dec 2024 09:41:40 -0500 Subject: [PATCH 25/75] [deps] Platform: Update @types/node to v22.10.2 (#12544) 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 +- package-lock.json | 8 ++++---- package.json | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/desktop/native-messaging-test-runner/package-lock.json b/apps/desktop/native-messaging-test-runner/package-lock.json index 469f8bd1da1..4bf939c8b41 100644 --- a/apps/desktop/native-messaging-test-runner/package-lock.json +++ b/apps/desktop/native-messaging-test-runner/package-lock.json @@ -18,7 +18,7 @@ "yargs": "17.7.2" }, "devDependencies": { - "@types/node": "22.10.1", + "@types/node": "22.10.2", "@types/node-ipc": "9.2.3", "typescript": "4.7.4" } @@ -106,9 +106,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.10.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.1.tgz", - "integrity": "sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ==", + "version": "22.10.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.2.tgz", + "integrity": "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==", "license": "MIT", "dependencies": { "undici-types": "~6.20.0" diff --git a/apps/desktop/native-messaging-test-runner/package.json b/apps/desktop/native-messaging-test-runner/package.json index e32b9e8e987..6ab267be4a8 100644 --- a/apps/desktop/native-messaging-test-runner/package.json +++ b/apps/desktop/native-messaging-test-runner/package.json @@ -23,7 +23,7 @@ "yargs": "17.7.2" }, "devDependencies": { - "@types/node": "22.10.1", + "@types/node": "22.10.2", "@types/node-ipc": "9.2.3", "typescript": "4.7.4" }, diff --git a/package-lock.json b/package-lock.json index a8ae6daf236..6112ddbd966 100644 --- a/package-lock.json +++ b/package-lock.json @@ -111,7 +111,7 @@ "@types/koa-json": "2.0.23", "@types/lowdb": "1.0.15", "@types/lunr": "2.3.7", - "@types/node": "22.10.1", + "@types/node": "22.10.2", "@types/node-fetch": "2.6.4", "@types/node-forge": "1.3.11", "@types/node-ipc": "9.2.3", @@ -10072,9 +10072,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.10.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.1.tgz", - "integrity": "sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ==", + "version": "22.10.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.2.tgz", + "integrity": "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 03991f9d70b..5522f9c5be5 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "@types/koa-json": "2.0.23", "@types/lowdb": "1.0.15", "@types/lunr": "2.3.7", - "@types/node": "22.10.1", + "@types/node": "22.10.2", "@types/node-fetch": "2.6.4", "@types/node-forge": "1.3.11", "@types/node-ipc": "9.2.3", From ef70f7ddced2f7ae064090b94273d3c1d5ef09b1 Mon Sep 17 00:00:00 2001 From: Patrick-Pimentel-Bitwarden Date: Tue, 24 Dec 2024 12:03:04 -0500 Subject: [PATCH 26/75] [PM-4949] Update Accept Organization Invite Flow (#12202) * fix(accept-organization): Update accept organization invite flow Removed old accept organization flow and updated stylings to tailwind. --- .../accept-organization.component.html | 41 ++----------------- .../accept-organization.component.ts | 10 +---- 2 files changed, 6 insertions(+), 45 deletions(-) diff --git a/apps/web/src/app/auth/organization-invite/accept-organization.component.html b/apps/web/src/app/auth/organization-invite/accept-organization.component.html index 88eaa37e8d2..cc08b840c30 100644 --- a/apps/web/src/app/auth/organization-invite/accept-organization.component.html +++ b/apps/web/src/app/auth/organization-invite/accept-organization.component.html @@ -1,9 +1,9 @@ -
    +
    - -

    + +

    @@ -11,36 +11,3 @@

    -
    -
    -
    -

    {{ "joinOrganization" | i18n }}

    -
    -
    -

    - {{ orgName$ | async }} - {{ email }} -

    -

    {{ "joinOrganizationDesc" | i18n }}

    -
    - -
    -
    -
    -
    -
    diff --git a/apps/web/src/app/auth/organization-invite/accept-organization.component.ts b/apps/web/src/app/auth/organization-invite/accept-organization.component.ts index f0425210b3d..660b2759988 100644 --- a/apps/web/src/app/auth/organization-invite/accept-organization.component.ts +++ b/apps/web/src/app/auth/organization-invite/accept-organization.component.ts @@ -56,20 +56,14 @@ export class AcceptOrganizationComponent extends BaseAcceptComponent { async unauthedHandler(qParams: Params): Promise { const invite = OrganizationInvite.fromParams(qParams); await this.acceptOrganizationInviteService.setOrganizationInvitation(invite); - await this.accelerateInviteAcceptIfPossible(invite); + await this.navigateInviteAcceptance(invite); } /** * In certain scenarios, we want to accelerate the user through the accept org invite process * For example, if the user has a BW account already, we want them to be taken to login instead of creation. */ - private async accelerateInviteAcceptIfPossible(invite: OrganizationInvite): Promise { - // if orgUserHasExistingUser is null, we can't determine the user's status - // so we don't want to accelerate the process - if (invite.orgUserHasExistingUser == null) { - return; - } - + private async navigateInviteAcceptance(invite: OrganizationInvite): Promise { // if user exists, send user to login if (invite.orgUserHasExistingUser) { await this.router.navigate(["/login"], { From eb7b2cd3e0b4f6689986340070066379903ee897 Mon Sep 17 00:00:00 2001 From: Daniel Riera Date: Thu, 26 Dec 2024 11:16:57 -0500 Subject: [PATCH 27/75] PM-15593 Standard Login UI is shown when item has no Auth key added (#12432) * PM-15593 - Added getFilteredCiphersForTotpField method - Implemented getFilteredCiphersForTotpField on update list method - Updated tests to not always have a top field * account for no totp code ciphers in totp fields --- .../autofill-inline-menu-list.spec.ts.snap | 596 ++---------------- .../list/autofill-inline-menu-list.spec.ts | 52 +- .../pages/list/autofill-inline-menu-list.ts | 27 +- .../src/autofill/spec/autofill-mocks.ts | 2 +- 4 files changed, 115 insertions(+), 562 deletions(-) diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/list/__snapshots__/autofill-inline-menu-list.spec.ts.snap b/apps/browser/src/autofill/overlay/inline-menu/pages/list/__snapshots__/autofill-inline-menu-list.spec.ts.snap index 3b8458ec2ab..acd06fb8c65 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/pages/list/__snapshots__/autofill-inline-menu-list.spec.ts.snap +++ b/apps/browser/src/autofill/overlay/inline-menu/pages/list/__snapshots__/autofill-inline-menu-list.spec.ts.snap @@ -681,6 +681,7 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f class="cipher-container" > - -
    - -
  • -
    - - -
    -
  • - -

    -`; - -exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers for an authenticated user creates the view for a totp field 1`] = ` -
    -
      -
    • -
      - - -
      -
    • -
    • -
      - - -
      -
    • -
    • -
      -
    • +
    +
    +`; + +exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers for an authenticated user creates the view for a totp field 1`] = ` +
    +
    - - -
  • -
    - -
    {{ startToTrayDescText }} -
    +
    -

    - {{ "premiumMembership" | i18n }} -

    -
    - -
    -
    - -

    {{ "premiumNotCurrentMember" | i18n }}

    -

    {{ "premiumSignUpAndGet" | i18n }}

    -
      -
    • - - {{ "ppremiumSignUpStorage" | i18n }} -
    • -
    • - - {{ "premiumSignUpTwoStepOptions" | i18n }} -
    • -
    • - - {{ "ppremiumSignUpReports" | i18n }} -
    • -
    • - - {{ "ppremiumSignUpTotp" | i18n }} -
    • -
    • - - {{ "ppremiumSignUpSupport" | i18n }} -
    • -
    • - - {{ "ppremiumSignUpFuture" | i18n }} -
    • -
    -

    {{ priceString }}

    - - -
    - -

    {{ "premiumCurrentMember" | i18n }}

    -

    {{ "premiumCurrentMemberThanks" | i18n }}

    - -
    -
    -
    diff --git a/apps/browser/src/billing/popup/settings/premium.component.ts b/apps/browser/src/billing/popup/settings/premium.component.ts deleted file mode 100644 index 70c8667646c..00000000000 --- a/apps/browser/src/billing/popup/settings/premium.component.ts +++ /dev/null @@ -1,61 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { CurrencyPipe, Location } from "@angular/common"; -import { Component } from "@angular/core"; - -import { PremiumComponent as BasePremiumComponent } from "@bitwarden/angular/vault/components/premium.component"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; -import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.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 { DialogService } from "@bitwarden/components"; - -@Component({ - selector: "app-premium", - templateUrl: "premium.component.html", -}) -export class PremiumComponent extends BasePremiumComponent { - priceString: string; - - constructor( - i18nService: I18nService, - platformUtilsService: PlatformUtilsService, - apiService: ApiService, - configService: ConfigService, - logService: LogService, - private location: Location, - private currencyPipe: CurrencyPipe, - dialogService: DialogService, - environmentService: EnvironmentService, - billingAccountProfileStateService: BillingAccountProfileStateService, - ) { - super( - i18nService, - platformUtilsService, - apiService, - configService, - logService, - dialogService, - environmentService, - billingAccountProfileStateService, - ); - - // Support old price string. Can be removed in future once all translations are properly updated. - const thePrice = this.currencyPipe.transform(this.price, "$"); - // Safari extension crashes due to $1 appearing in the price string ($10.00). Escape the $ to fix. - const formattedPrice = this.platformUtilsService.isSafari() - ? thePrice.replace("$", "$$$") - : thePrice; - this.priceString = i18nService.t("premiumPrice", formattedPrice); - if (this.priceString.indexOf("%price%") > -1) { - this.priceString = this.priceString.replace("%price%", thePrice); - } - } - - goBack() { - this.location.back(); - } -} diff --git a/apps/browser/src/popup/app-routing.module.ts b/apps/browser/src/popup/app-routing.module.ts index 0a6ff456938..67d51a93cde 100644 --- a/apps/browser/src/popup/app-routing.module.ts +++ b/apps/browser/src/popup/app-routing.module.ts @@ -81,7 +81,6 @@ import { ExcludedDomainsComponent } from "../autofill/popup/settings/excluded-do import { NotificationsSettingsV1Component } from "../autofill/popup/settings/notifications-v1.component"; import { NotificationsSettingsComponent } from "../autofill/popup/settings/notifications.component"; import { PremiumV2Component } from "../billing/popup/settings/premium-v2.component"; -import { PremiumComponent } from "../billing/popup/settings/premium.component"; import BrowserPopupUtils from "../platform/popup/browser-popup-utils"; import { popupRouterCacheGuard } from "../platform/popup/view-cache/popup-router-cache.service"; import { CredentialGeneratorHistoryComponent } from "../tools/popup/generator/credential-generator-history.component"; @@ -395,12 +394,12 @@ const routes: Routes = [ canActivate: [authGuard], data: { elevation: 2 } satisfies RouteDataProperties, }), - ...extensionRefreshSwap(PremiumComponent, PremiumV2Component, { + { path: "premium", - component: PremiumComponent, + component: PremiumV2Component, canActivate: [authGuard], data: { elevation: 1 } satisfies RouteDataProperties, - }), + }, ...extensionRefreshSwap(AppearanceComponent, AppearanceV2Component, { path: "appearance", canActivate: [authGuard], diff --git a/apps/browser/src/popup/app.module.ts b/apps/browser/src/popup/app.module.ts index b547078084b..04d681812fe 100644 --- a/apps/browser/src/popup/app.module.ts +++ b/apps/browser/src/popup/app.module.ts @@ -48,7 +48,6 @@ import { ExcludedDomainsV1Component } from "../autofill/popup/settings/excluded- import { ExcludedDomainsComponent } from "../autofill/popup/settings/excluded-domains.component"; import { NotificationsSettingsV1Component } from "../autofill/popup/settings/notifications-v1.component"; import { NotificationsSettingsComponent } from "../autofill/popup/settings/notifications.component"; -import { PremiumComponent } from "../billing/popup/settings/premium.component"; import { PopOutComponent } from "../platform/popup/components/pop-out.component"; import { HeaderComponent } from "../platform/popup/header.component"; import { PopupFooterComponent } from "../platform/popup/layout/popup-footer.component"; @@ -155,7 +154,6 @@ import "../platform/popup/locales"; NotificationsSettingsV1Component, AppearanceComponent, PasswordHistoryComponent, - PremiumComponent, RegisterComponent, SetPasswordComponent, VaultSettingsComponent, From 6539f06654e4596d06c63c85dcd5c5fa04ee290e Mon Sep 17 00:00:00 2001 From: Victoria League Date: Mon, 30 Dec 2024 14:46:29 -0500 Subject: [PATCH 45/75] [CL-135] Fix import statement for standalone component (#12589) --- libs/components/src/badge-list/badge-list.stories.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libs/components/src/badge-list/badge-list.stories.ts b/libs/components/src/badge-list/badge-list.stories.ts index 24024cb6bbd..ede005f6fd6 100644 --- a/libs/components/src/badge-list/badge-list.stories.ts +++ b/libs/components/src/badge-list/badge-list.stories.ts @@ -13,8 +13,7 @@ export default { component: BadgeListComponent, decorators: [ moduleMetadata({ - imports: [SharedModule, BadgeModule], - declarations: [BadgeListComponent], + imports: [SharedModule, BadgeModule, BadgeListComponent], providers: [ { provide: I18nService, From a95427eee0265c30322afec4c48e489a71ad1d7c Mon Sep 17 00:00:00 2001 From: Victoria League Date: Mon, 30 Dec 2024 16:42:53 -0500 Subject: [PATCH 46/75] [CL-524] Ignore flaky elements in Chromatic tests (#12561) --- .../src/platform/popup/layout/popup-layout.stories.ts | 10 ++++++++-- libs/components/src/menu/menu-trigger-for.directive.ts | 2 +- .../dialog-virtual-scroll-block.component.ts | 4 ++-- .../src/stories/kitchen-sink/kitchen-sink.stories.ts | 3 +++ 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/apps/browser/src/platform/popup/layout/popup-layout.stories.ts b/apps/browser/src/platform/popup/layout/popup-layout.stories.ts index cd467cecbd2..c1ac8823261 100644 --- a/apps/browser/src/platform/popup/layout/popup-layout.stories.ts +++ b/apps/browser/src/platform/popup/layout/popup-layout.stories.ts @@ -41,7 +41,7 @@ class ExtensionContainerComponent {} @Component({ selector: "vault-placeholder", - template: ` + template: /*html*/ ` @@ -53,7 +53,7 @@ class ExtensionContainerComponent {} - + @@ -301,6 +301,12 @@ class MockVaultSubpageComponent {} export default { title: "Browser/Popup Layout", component: PopupPageComponent, + parameters: { + chromatic: { + // Disable tests while we troubleshoot their flaky-ness + disableSnapshot: true, + }, + }, decorators: [ moduleMetadata({ imports: [ diff --git a/libs/components/src/menu/menu-trigger-for.directive.ts b/libs/components/src/menu/menu-trigger-for.directive.ts index 786554e981c..96d430c5e6a 100644 --- a/libs/components/src/menu/menu-trigger-for.directive.ts +++ b/libs/components/src/menu/menu-trigger-for.directive.ts @@ -36,7 +36,7 @@ export class MenuTriggerForDirective implements OnDestroy { private defaultMenuConfig: OverlayConfig = { panelClass: "bit-menu-panel", hasBackdrop: true, - backdropClass: "cdk-overlay-transparent-backdrop", + backdropClass: ["cdk-overlay-transparent-backdrop", "bit-menu-panel-backdrop"], scrollStrategy: this.overlay.scrollStrategies.reposition(), positionStrategy: this.overlay .position() diff --git a/libs/components/src/stories/kitchen-sink/components/dialog-virtual-scroll-block.component.ts b/libs/components/src/stories/kitchen-sink/components/dialog-virtual-scroll-block.component.ts index a867d9cdf53..02b49a3e915 100644 --- a/libs/components/src/stories/kitchen-sink/components/dialog-virtual-scroll-block.component.ts +++ b/libs/components/src/stories/kitchen-sink/components/dialog-virtual-scroll-block.component.ts @@ -10,9 +10,9 @@ import { TableDataSource, TableModule } from "../../../table"; selector: "dialog-virtual-scroll-block", standalone: true, imports: [DialogModule, IconButtonModule, SectionComponent, TableModule, ScrollingModule], - template: ` + template: /*html*/ ` - + Id diff --git a/libs/components/src/stories/kitchen-sink/kitchen-sink.stories.ts b/libs/components/src/stories/kitchen-sink/kitchen-sink.stories.ts index 203c510f814..44080e29049 100644 --- a/libs/components/src/stories/kitchen-sink/kitchen-sink.stories.ts +++ b/libs/components/src/stories/kitchen-sink/kitchen-sink.stories.ts @@ -130,6 +130,9 @@ export const MenuOpen: Story = { const menuButton = getAllByRole(table, "button")[0]; await userEvent.click(menuButton); }, + parameters: { + chromatic: { ignoreSelectors: [".bit-menu-panel-backdrop"] }, + }, }; export const DefaultDialogOpen: Story = { From 894dd2c89669acb018aaf6caecdc65b9cfda22a2 Mon Sep 17 00:00:00 2001 From: Jason Ng Date: Mon, 30 Dec 2024 19:24:31 -0500 Subject: [PATCH 47/75] [PM-16507] update new device verification notice state definition (#12608) --- libs/common/src/platform/state/state-definitions.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libs/common/src/platform/state/state-definitions.ts b/libs/common/src/platform/state/state-definitions.ts index 1ae5b080360..ce9eb5a15c0 100644 --- a/libs/common/src/platform/state/state-definitions.ts +++ b/libs/common/src/platform/state/state-definitions.ts @@ -180,5 +180,8 @@ export const VAULT_BROWSER_UI_ONBOARDING = new StateDefinition("vaultBrowserUiOn export const NEW_DEVICE_VERIFICATION_NOTICE = new StateDefinition( "newDeviceVerificationNotice", "disk", + { + web: "disk-local", + }, ); export const VAULT_APPEARANCE = new StateDefinition("vaultAppearance", "disk"); From 03d09578144b9b362f698fbd2825135a878ecefe Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Tue, 31 Dec 2024 14:56:23 +0100 Subject: [PATCH 48/75] Fix infinite password prompt loop on ssh-key import (#12569) --- apps/desktop/src/vault/app/vault/add-edit.component.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/desktop/src/vault/app/vault/add-edit.component.ts b/apps/desktop/src/vault/app/vault/add-edit.component.ts index b4f62a1254c..a798e61aa88 100644 --- a/apps/desktop/src/vault/app/vault/add-edit.component.ts +++ b/apps/desktop/src/vault/app/vault/add-edit.component.ts @@ -208,6 +208,9 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit, On }); } else { password = await this.getSshKeyPassword(); + if (password === "") { + return; + } await this.importSshKeyFromClipboard(password); } return; From 899b16966af90878bf9628f9b41a6c4d16cf727a Mon Sep 17 00:00:00 2001 From: cyprain-okeke <108260115+cyprain-okeke@users.noreply.github.com> Date: Tue, 31 Dec 2024 18:06:45 +0100 Subject: [PATCH 49/75] [PM-15814]Alert owners of reseller-managed orgs to renewal events (#12607) * Changes for the reseller alert * Resolve the null error * Refactor the reseller service * Fix the a failing test due to null date * Fix the No overload matches error * Resolve the null error * Resolve the null error * Resolve the null error * Change the date format * Remove unwanted comment * Refactor changes * Add the feature flag --- .../services/reseller-warning.service.ts | 142 ++++++++++++++++++ .../app/vault/org-vault/vault.component.html | 12 ++ .../app/vault/org-vault/vault.component.ts | 21 +++ apps/web/src/locales/en/messages.json | 43 ++++++ .../organization-billing-metadata.response.ts | 13 ++ libs/common/src/enums/feature-flag.enum.ts | 2 + 6 files changed, 233 insertions(+) create mode 100644 apps/web/src/app/billing/services/reseller-warning.service.ts diff --git a/apps/web/src/app/billing/services/reseller-warning.service.ts b/apps/web/src/app/billing/services/reseller-warning.service.ts new file mode 100644 index 00000000000..bfd5be3233a --- /dev/null +++ b/apps/web/src/app/billing/services/reseller-warning.service.ts @@ -0,0 +1,142 @@ +import { Injectable } from "@angular/core"; + +import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { OrganizationBillingMetadataResponse } from "@bitwarden/common/billing/models/response/organization-billing-metadata.response"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; + +export interface ResellerWarning { + type: "info" | "warning"; + message: string; +} + +@Injectable({ providedIn: "root" }) +export class ResellerWarningService { + private readonly RENEWAL_WARNING_DAYS = 14; + private readonly GRACE_PERIOD_DAYS = 30; + + constructor(private i18nService: I18nService) {} + + getWarning( + organization: Organization, + organizationBillingMetadata: OrganizationBillingMetadataResponse, + ): ResellerWarning | null { + if (!organization.hasReseller) { + return null; // If no reseller, return null immediately + } + + // Check for past due warning first (highest priority) + if (this.shouldShowPastDueWarning(organizationBillingMetadata)) { + const gracePeriodEnd = this.getGracePeriodEndDate(organizationBillingMetadata.invoiceDueDate); + if (!gracePeriodEnd) { + return null; + } + return { + type: "warning", + message: this.i18nService.t( + "resellerPastDueWarning", + organization.providerName, + this.formatDate(gracePeriodEnd), + ), + } as ResellerWarning; + } + + // Check for open invoice warning + if (this.shouldShowInvoiceWarning(organizationBillingMetadata)) { + const invoiceCreatedDate = organizationBillingMetadata.invoiceCreatedDate; + const invoiceDueDate = organizationBillingMetadata.invoiceDueDate; + if (!invoiceCreatedDate || !invoiceDueDate) { + return null; + } + return { + type: "info", + message: this.i18nService.t( + "resellerOpenInvoiceWarning", + organization.providerName, + this.formatDate(organizationBillingMetadata.invoiceCreatedDate), + this.formatDate(organizationBillingMetadata.invoiceDueDate), + ), + } as ResellerWarning; + } + + // Check for renewal warning + if (this.shouldShowRenewalWarning(organizationBillingMetadata)) { + const subPeriodEndDate = organizationBillingMetadata.subPeriodEndDate; + if (!subPeriodEndDate) { + return null; + } + + return { + type: "info", + message: this.i18nService.t( + "resellerRenewalWarning", + organization.providerName, + this.formatDate(organizationBillingMetadata.subPeriodEndDate), + ), + } as ResellerWarning; + } + + return null; + } + + private shouldShowRenewalWarning( + organizationBillingMetadata: OrganizationBillingMetadataResponse, + ): boolean { + if ( + !organizationBillingMetadata.hasSubscription || + !organizationBillingMetadata.subPeriodEndDate + ) { + return false; + } + const renewalDate = new Date(organizationBillingMetadata.subPeriodEndDate); + const daysUntilRenewal = Math.ceil( + (renewalDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24), + ); + return daysUntilRenewal <= this.RENEWAL_WARNING_DAYS; + } + + private shouldShowInvoiceWarning( + organizationBillingMetadata: OrganizationBillingMetadataResponse, + ): boolean { + if ( + !organizationBillingMetadata.hasOpenInvoice || + !organizationBillingMetadata.invoiceDueDate + ) { + return false; + } + const invoiceDueDate = new Date(organizationBillingMetadata.invoiceDueDate); + return invoiceDueDate > new Date(); + } + + private shouldShowPastDueWarning( + organizationBillingMetadata: OrganizationBillingMetadataResponse, + ): boolean { + if ( + !organizationBillingMetadata.hasOpenInvoice || + !organizationBillingMetadata.invoiceDueDate + ) { + return false; + } + const invoiceDueDate = new Date(organizationBillingMetadata.invoiceDueDate); + return invoiceDueDate <= new Date() && !organizationBillingMetadata.isSubscriptionUnpaid; + } + + private getGracePeriodEndDate(dueDate: Date | null): Date | null { + if (!dueDate) { + return null; + } + const gracePeriodEnd = new Date(dueDate); + gracePeriodEnd.setDate(gracePeriodEnd.getDate() + this.GRACE_PERIOD_DAYS); + return gracePeriodEnd; + } + + private formatDate(date: Date | null): string { + if (!date) { + return "N/A"; + } + return new Date(date).toLocaleDateString("en-US", { + month: "short", + day: "2-digit", + year: "numeric", + }); + } +} diff --git a/apps/web/src/app/vault/org-vault/vault.component.html b/apps/web/src/app/vault/org-vault/vault.component.html index 52f3ea026ff..512f97144de 100644 --- a/apps/web/src/app/vault/org-vault/vault.component.html +++ b/apps/web/src/app/vault/org-vault/vault.component.html @@ -19,6 +19,18 @@ + + + {{ resellerWarning?.message }} + + (false); protected currentSearchText$: Observable; protected freeTrial$: Observable; + protected resellerWarning$: Observable; /** * A list of collections that the user can assign items to and edit those items within. * @protected @@ -203,6 +208,7 @@ export class VaultComponent implements OnInit, OnDestroy { private destroy$ = new Subject(); protected addAccessStatus$ = new BehaviorSubject(0); private extensionRefreshEnabled: boolean; + private resellerManagedOrgAlert: boolean; private vaultItemDialogRef?: DialogRef | undefined; private readonly unpaidSubscriptionDialog$ = this.organizationService.organizations$.pipe( @@ -259,6 +265,7 @@ export class VaultComponent implements OnInit, OnDestroy { private trialFlowService: TrialFlowService, protected billingApiService: BillingApiServiceAbstraction, private organizationBillingService: OrganizationBillingServiceAbstraction, + private resellerWarningService: ResellerWarningService, ) {} async ngOnInit() { @@ -266,6 +273,10 @@ export class VaultComponent implements OnInit, OnDestroy { FeatureFlag.ExtensionRefresh, ); + this.resellerManagedOrgAlert = await this.configService.getFeatureFlag( + FeatureFlag.ResellerManagedOrgAlert, + ); + this.trashCleanupWarning = this.i18nService.t( this.platformUtilsService.isSelfHost() ? "trashCleanupWarningSelfHosted" @@ -612,6 +623,16 @@ export class VaultComponent implements OnInit, OnDestroy { }), ); + this.resellerWarning$ = organization$.pipe( + filter((org) => org.isOwner && this.resellerManagedOrgAlert), + switchMap((org) => + from(this.billingApiService.getOrganizationBillingMetadata(org.id)).pipe( + map((metadata) => ({ org, metadata })), + ), + ), + map(({ org, metadata }) => this.resellerWarningService.getWarning(org, metadata)), + ); + firstSetup$ .pipe( switchMap(() => this.refresh$), diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index acbb348048c..7c338fc6e97 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -10011,5 +10011,48 @@ }, "organizationNameMaxLength": { "message": "Organization name cannot exceed 50 characters." + }, + "resellerRenewalWarning": { + "message": "Your subscription will renew soon. To insure uninterrupted service, contact $RESELLER$ to confirm your renewal before $RENEWAL_DATE$.", + "placeholders": { + "reseller": { + "content": "$1", + "example": "Reseller Name" + }, + "renewal_date": { + "content": "$2", + "example": "01/01/2024" + } + } + }, + "resellerOpenInvoiceWarning": { + "message": "An invoice for your subscription was issued on $ISSUED_DATE$. To insure uninterrupted service, contact $RESELLER$ to confirm your renewal before $DUE_DATE$.", + "placeholders": { + "reseller": { + "content": "$1", + "example": "Reseller Name" + }, + "issued_date": { + "content": "$2", + "example": "01/01/2024" + }, + "due_date": { + "content": "$3", + "example": "01/15/2024" + } + } + }, + "resellerPastDueWarning": { + "message": "The invoice for your subscription has not been paid. To insure uninterrupted service, contact $RESELLER$ to confirm your renewal before $GRACE_PERIOD_END$.", + "placeholders": { + "reseller": { + "content": "$1", + "example": "Reseller Name" + }, + "grace_period_end": { + "content": "$2", + "example": "02/14/2024" + } + } } } diff --git a/libs/common/src/billing/models/response/organization-billing-metadata.response.ts b/libs/common/src/billing/models/response/organization-billing-metadata.response.ts index d9733aa80f2..c5023cb64c1 100644 --- a/libs/common/src/billing/models/response/organization-billing-metadata.response.ts +++ b/libs/common/src/billing/models/response/organization-billing-metadata.response.ts @@ -6,6 +6,10 @@ export class OrganizationBillingMetadataResponse extends BaseResponse { isOnSecretsManagerStandalone: boolean; isSubscriptionUnpaid: boolean; hasSubscription: boolean; + hasOpenInvoice: boolean; + invoiceDueDate: Date | null; + invoiceCreatedDate: Date | null; + subPeriodEndDate: Date | null; constructor(response: any) { super(response); @@ -14,5 +18,14 @@ export class OrganizationBillingMetadataResponse extends BaseResponse { this.isOnSecretsManagerStandalone = this.getResponseProperty("IsOnSecretsManagerStandalone"); this.isSubscriptionUnpaid = this.getResponseProperty("IsSubscriptionUnpaid"); this.hasSubscription = this.getResponseProperty("HasSubscription"); + this.hasOpenInvoice = this.getResponseProperty("HasOpenInvoice"); + + this.invoiceDueDate = this.parseDate(this.getResponseProperty("InvoiceDueDate")); + this.invoiceCreatedDate = this.parseDate(this.getResponseProperty("InvoiceCreatedDate")); + this.subPeriodEndDate = this.parseDate(this.getResponseProperty("SubPeriodEndDate")); + } + + private parseDate(dateString: any): Date | null { + return dateString ? new Date(dateString) : null; } } diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index cc2abed3ba1..135119bf133 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -43,6 +43,7 @@ export enum FeatureFlag { PM11360RemoveProviderExportPermission = "pm-11360-remove-provider-export-permission", PM12443RemovePagingLogic = "pm-12443-remove-paging-logic", PrivateKeyRegeneration = "pm-12241-private-key-regeneration", + ResellerManagedOrgAlert = "PM-15814-alert-owners-of-reseller-managed-orgs", } export type AllowedFeatureFlagTypes = boolean | number | string; @@ -96,6 +97,7 @@ export const DefaultFeatureFlagValue = { [FeatureFlag.PM11360RemoveProviderExportPermission]: FALSE, [FeatureFlag.PM12443RemovePagingLogic]: FALSE, [FeatureFlag.PrivateKeyRegeneration]: FALSE, + [FeatureFlag.ResellerManagedOrgAlert]: FALSE, } satisfies Record; export type DefaultFeatureFlagValueType = typeof DefaultFeatureFlagValue; From e1778f4282f92fba7e0abd00081b4b20c7161641 Mon Sep 17 00:00:00 2001 From: Brant DeBow <125889545+brant-livefront@users.noreply.github.com> Date: Tue, 31 Dec 2024 15:16:31 -0500 Subject: [PATCH 50/75] [PM-16530] [BRE-283] Changes to support hardening on the Mac desktop app (#12632) * [DEVOPS-1424] Changes to support hardening on the Mac desktop app * Remove unsigned memory exception * Remove exceptions from the local (non-MAS) mac builds as well --------- Co-authored-by: Matt Bishop --- apps/desktop/electron-builder.json | 2 +- .../resources/entitlements.desktop_proxy.inherit.plist | 2 ++ apps/desktop/resources/entitlements.desktop_proxy.plist | 2 ++ apps/desktop/resources/entitlements.mac.plist | 4 ---- apps/desktop/resources/entitlements.mas.inherit.plist | 4 +--- apps/desktop/resources/entitlements.mas.plist | 2 ++ 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/desktop/electron-builder.json b/apps/desktop/electron-builder.json index 898ad086b29..c8114d947e4 100644 --- a/apps/desktop/electron-builder.json +++ b/apps/desktop/electron-builder.json @@ -133,7 +133,7 @@ "entitlements": "resources/entitlements.mas.plist", "entitlementsInherit": "resources/entitlements.mas.inherit.plist", "entitlementsLoginHelper": "resources/entitlements.mas.loginhelper.plist", - "hardenedRuntime": false, + "hardenedRuntime": true, "extendInfo": { "LSMinimumSystemVersion": "12", "ElectronTeamID": "LTZ2PFU5D6" diff --git a/apps/desktop/resources/entitlements.desktop_proxy.inherit.plist b/apps/desktop/resources/entitlements.desktop_proxy.inherit.plist index 794eada1cad..fca5f02d52d 100644 --- a/apps/desktop/resources/entitlements.desktop_proxy.inherit.plist +++ b/apps/desktop/resources/entitlements.desktop_proxy.inherit.plist @@ -6,5 +6,7 @@ com.apple.security.inherit + com.apple.security.cs.allow-jit + diff --git a/apps/desktop/resources/entitlements.desktop_proxy.plist b/apps/desktop/resources/entitlements.desktop_proxy.plist index d5c7b8a2cc8..1a39a482389 100644 --- a/apps/desktop/resources/entitlements.desktop_proxy.plist +++ b/apps/desktop/resources/entitlements.desktop_proxy.plist @@ -8,5 +8,7 @@ LTZ2PFU5D6.com.bitwarden.desktop + com.apple.security.cs.allow-jit + diff --git a/apps/desktop/resources/entitlements.mac.plist b/apps/desktop/resources/entitlements.mac.plist index 34c561bd03f..e273bcc7eca 100644 --- a/apps/desktop/resources/entitlements.mac.plist +++ b/apps/desktop/resources/entitlements.mac.plist @@ -4,10 +4,6 @@ com.apple.security.cs.allow-jit - com.apple.security.cs.allow-unsigned-executable-memory - - com.apple.security.cs.disable-library-validation - {{ "estimatedTax" | i18n }}: {{ estimatedTax | currency: "USD $" }}
    diff --git a/apps/web/src/app/billing/individual/premium/premium-v2.component.ts b/apps/web/src/app/billing/individual/premium/premium-v2.component.ts index 72dc5a30d5f..2abab57b7e0 100644 --- a/apps/web/src/app/billing/individual/premium/premium-v2.component.ts +++ b/apps/web/src/app/billing/individual/premium/premium-v2.component.ts @@ -5,10 +5,13 @@ import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { FormControl, FormGroup, Validators } from "@angular/forms"; import { ActivatedRoute, Router } from "@angular/router"; import { combineLatest, concatMap, from, Observable, of } from "rxjs"; +import { debounceTime } from "rxjs/operators"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions"; +import { TaxServiceAbstraction } from "@bitwarden/common/billing/abstractions/tax.service.abstraction"; +import { PreviewIndividualInvoiceRequest } from "@bitwarden/common/billing/models/request/preview-individual-invoice.request"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; @@ -44,6 +47,7 @@ export class PremiumV2Component { FeatureFlag.PM11901_RefactorSelfHostingLicenseUploader, ); + protected estimatedTax: number = 0; protected readonly familyPlanMaxUserCount = 6; protected readonly premiumPrice = 10; protected readonly storageGBPrice = 4; @@ -60,6 +64,7 @@ export class PremiumV2Component { private syncService: SyncService, private toastService: ToastService, private tokenService: TokenService, + private taxService: TaxServiceAbstraction, ) { this.isSelfHost = this.platformUtilsService.isSelfHost(); @@ -82,6 +87,12 @@ export class PremiumV2Component { }), ) .subscribe(); + + this.addOnFormGroup.controls.additionalStorage.valueChanges + .pipe(debounceTime(1000), takeUntilDestroyed()) + .subscribe(() => { + this.refreshSalesTax(); + }); } finalizeUpgrade = async () => { @@ -158,12 +169,6 @@ export class PremiumV2Component { return this.storageGBPrice * this.addOnFormGroup.value.additionalStorage; } - protected get estimatedTax(): number { - return this.taxInfoComponent?.taxRate != null - ? (this.taxInfoComponent.taxRate / 100) * this.subtotal - : 0; - } - protected get premiumURL(): string { return `${this.cloudWebVaultURL}/#/settings/subscription/premium`; } @@ -179,4 +184,36 @@ export class PremiumV2Component { protected async onLicenseFileSelectedChanged(): Promise { await this.postFinalizeUpgrade(); } + + private refreshSalesTax(): void { + if (!this.taxInfoComponent.country || !this.taxInfoComponent.postalCode) { + return; + } + const request: PreviewIndividualInvoiceRequest = { + passwordManager: { + additionalStorage: this.addOnFormGroup.value.additionalStorage, + }, + taxInformation: { + postalCode: this.taxInfoComponent.postalCode, + country: this.taxInfoComponent.country, + }, + }; + + this.taxService + .previewIndividualInvoice(request) + .then((invoice) => { + this.estimatedTax = invoice.taxAmount; + }) + .catch((error) => { + this.toastService.showToast({ + title: "", + variant: "error", + message: this.i18nService.t(error.message), + }); + }); + } + + protected onTaxInformationChanged(): void { + this.refreshSalesTax(); + } } diff --git a/apps/web/src/app/billing/individual/premium/premium.component.html b/apps/web/src/app/billing/individual/premium/premium.component.html index 8b848b48dab..12b6932d0f5 100644 --- a/apps/web/src/app/billing/individual/premium/premium.component.html +++ b/apps/web/src/app/billing/individual/premium/premium.component.html @@ -122,7 +122,7 @@

    {{ "summary" | i18n }}

    {{ "paymentInformation" | i18n }}

    - +
    {{ "planPrice" | i18n }}: {{ subtotal | currency: "USD $" }} diff --git a/apps/web/src/app/billing/individual/premium/premium.component.ts b/apps/web/src/app/billing/individual/premium/premium.component.ts index 9e2be6dcab1..76ca25c8cc6 100644 --- a/apps/web/src/app/billing/individual/premium/premium.component.ts +++ b/apps/web/src/app/billing/individual/premium/premium.component.ts @@ -1,13 +1,17 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { Component, OnInit, ViewChild } from "@angular/core"; +import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { FormControl, FormGroup, Validators } from "@angular/forms"; import { Router } from "@angular/router"; import { firstValueFrom, Observable } from "rxjs"; +import { debounceTime } from "rxjs/operators"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; +import { TaxServiceAbstraction } from "@bitwarden/common/billing/abstractions/tax.service.abstraction"; +import { PreviewIndividualInvoiceRequest } from "@bitwarden/common/billing/models/request/preview-individual-invoice.request"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; @@ -39,6 +43,9 @@ export class PremiumComponent implements OnInit { protected addonForm = new FormGroup({ additionalStorage: new FormControl(0, [Validators.max(99), Validators.min(0)]), }); + + private estimatedTax: number = 0; + constructor( private apiService: ApiService, private i18nService: I18nService, @@ -50,9 +57,16 @@ export class PremiumComponent implements OnInit { private environmentService: EnvironmentService, private billingAccountProfileStateService: BillingAccountProfileStateService, private toastService: ToastService, + private taxService: TaxServiceAbstraction, ) { this.selfHosted = platformUtilsService.isSelfHost(); this.canAccessPremium$ = billingAccountProfileStateService.hasPremiumFromAnySource$; + + this.addonForm.controls.additionalStorage.valueChanges + .pipe(debounceTime(1000), takeUntilDestroyed()) + .subscribe(() => { + this.refreshSalesTax(); + }); } protected setSelectedFile(event: Event) { const fileInputEl = event.target; @@ -154,12 +168,42 @@ export class PremiumComponent implements OnInit { } get taxCharges(): number { - return this.taxInfoComponent != null && this.taxInfoComponent.taxRate != null - ? (this.taxInfoComponent.taxRate / 100) * this.subtotal - : 0; + return this.estimatedTax; } get total(): number { return this.subtotal + this.taxCharges || 0; } + + private refreshSalesTax(): void { + if (!this.taxInfoComponent.country || !this.taxInfoComponent.postalCode) { + return; + } + const request: PreviewIndividualInvoiceRequest = { + passwordManager: { + additionalStorage: this.addonForm.value.additionalStorage, + }, + taxInformation: { + postalCode: this.taxInfoComponent.postalCode, + country: this.taxInfoComponent.country, + }, + }; + + this.taxService + .previewIndividualInvoice(request) + .then((invoice) => { + this.estimatedTax = invoice.taxAmount; + }) + .catch((error) => { + this.toastService.showToast({ + title: "", + variant: "error", + message: this.i18nService.t(error.message), + }); + }); + } + + protected onTaxInformationChanged(): void { + this.refreshSalesTax(); + } } diff --git a/apps/web/src/app/billing/organizations/change-plan-dialog.component.html b/apps/web/src/app/billing/organizations/change-plan-dialog.component.html index 93751f0ef72..78005275f12 100644 --- a/apps/web/src/app/billing/organizations/change-plan-dialog.component.html +++ b/apps/web/src/app/billing/organizations/change-plan-dialog.component.html @@ -344,25 +344,14 @@

    {{ "paymentMethod" | i18n }}

    - - - - + + + + +

    {{ "paymentMethod" | i18n }}

    +
    + +

    + + {{ "estimatedTax" | i18n }} + + + {{ estimatedTax | currency: "USD" : "$" }} + +

    +
    +

    (); + protected taxInformation: TaxInformation; + constructor( @Inject(DIALOG_DATA) private dialogParams: ChangePlanDialogParams, private dialogRef: DialogRef, @@ -189,6 +195,7 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { private organizationApiService: OrganizationApiServiceAbstraction, private configService: ConfigService, private billingApiService: BillingApiServiceAbstraction, + private taxService: TaxServiceAbstraction, ) {} async ngOnInit(): Promise { @@ -267,6 +274,11 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { this.setInitialPlanSelection(); this.loading = false; + + const taxInfo = await this.organizationApiService.getTaxInfo(this.organizationId); + this.taxInformation = TaxInformation.from(taxInfo); + + this.refreshSalesTax(); } setInitialPlanSelection() { @@ -402,6 +414,12 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { } this.selectedPlan = plan; this.formGroup.patchValue({ productTier: plan.productTier }); + + try { + this.refreshSalesTax(); + } catch { + this.estimatedTax = 0; + } } ngOnDestroy() { @@ -567,12 +585,6 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { ); } - get taxCharges() { - return this.taxComponent != null && this.taxComponent.taxRate != null - ? (this.taxComponent.taxRate / 100) * this.passwordManagerSubtotal - : 0; - } - get passwordManagerSeats() { if (this.selectedPlan.productTier === ProductTierType.Families) { return this.selectedPlan.PasswordManager.baseSeats; @@ -584,15 +596,15 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { if (this.organization.useSecretsManager) { return ( this.passwordManagerSubtotal + - this.additionalStorageTotal(this.selectedPlan) + - this.secretsManagerSubtotal + - this.taxCharges || 0 + this.additionalStorageTotal(this.selectedPlan) + + this.secretsManagerSubtotal + + this.estimatedTax ); } return ( this.passwordManagerSubtotal + - this.additionalStorageTotal(this.selectedPlan) + - this.taxCharges || 0 + this.additionalStorageTotal(this.selectedPlan) + + this.estimatedTax ); } @@ -645,8 +657,8 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { } changedCountry() { - if (this.deprecateStripeSourcesAPI && this.paymentV2Component && this.taxComponent) { - this.paymentV2Component.showBankAccount = this.taxComponent.country === "US"; + if (this.deprecateStripeSourcesAPI && this.paymentV2Component) { + this.paymentV2Component.showBankAccount = this.taxInformation.country === "US"; if ( !this.paymentV2Component.showBankAccount && @@ -654,8 +666,8 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { ) { this.paymentV2Component.select(PaymentMethodType.Card); } - } else if (this.paymentComponent && this.taxComponent) { - this.paymentComponent!.hideBank = this.taxComponent?.taxFormGroup?.value.country !== "US"; + } else if (this.paymentComponent && this.taxInformation) { + this.paymentComponent!.hideBank = this.taxInformation.country !== "US"; // Bank Account payments are only available for US customers if ( this.paymentComponent.hideBank && @@ -667,9 +679,14 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { } } + protected taxInformationChanged(event: TaxInformation): void { + this.taxInformation = event; + this.changedCountry(); + this.refreshSalesTax(); + } + submit = async () => { - if (!this.taxComponent?.taxFormGroup.valid && this.taxComponent?.taxFormGroup.touched) { - this.taxComponent?.taxFormGroup.markAllAsTouched(); + if (this.taxComponent !== undefined && !this.taxComponent.validate()) { return; } @@ -723,8 +740,8 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { this.formGroup.controls.premiumAccessAddon.value; request.planType = this.selectedPlan.type; if (this.showPayment) { - request.billingAddressCountry = this.taxComponent.taxFormGroup?.value.country; - request.billingAddressPostalCode = this.taxComponent.taxFormGroup?.value.postalCode; + request.billingAddressCountry = this.taxInformation.country; + request.billingAddressPostalCode = this.taxInformation.postalCode; } // Secrets Manager @@ -735,15 +752,9 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { const tokenizedPaymentSource = await this.paymentV2Component.tokenize(); const updatePaymentMethodRequest = new UpdatePaymentMethodRequest(); updatePaymentMethodRequest.paymentSource = tokenizedPaymentSource; - updatePaymentMethodRequest.taxInformation = { - country: this.taxComponent.country, - postalCode: this.taxComponent.postalCode, - taxId: this.taxComponent.taxId, - line1: this.taxComponent.line1, - line2: this.taxComponent.line2, - city: this.taxComponent.city, - state: this.taxComponent.state, - }; + updatePaymentMethodRequest.taxInformation = ExpandedTaxInfoUpdateRequest.From( + this.taxInformation, + ); await this.billingApiService.updateOrganizationPaymentMethod( this.organizationId, @@ -754,8 +765,8 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { const paymentRequest = new PaymentRequest(); paymentRequest.paymentToken = tokenResult[0]; paymentRequest.paymentMethodType = tokenResult[1]; - paymentRequest.country = this.taxComponent.taxFormGroup?.value.country; - paymentRequest.postalCode = this.taxComponent.taxFormGroup?.value.postalCode; + paymentRequest.country = this.taxInformation.country; + paymentRequest.postalCode = this.taxInformation.postalCode; await this.organizationApiService.updatePayment(this.organizationId, paymentRequest); } } @@ -944,4 +955,48 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { manageSelectableProduct(index: number) { return index; } + + private refreshSalesTax(): void { + if (!this.taxInformation.country || !this.taxInformation.postalCode) { + return; + } + + const request: PreviewOrganizationInvoiceRequest = { + organizationId: this.organizationId, + passwordManager: { + additionalStorage: 0, + plan: this.selectedPlan?.type, + seats: this.sub.seats, + }, + taxInformation: { + postalCode: this.taxInformation.postalCode, + country: this.taxInformation.country, + taxId: this.taxInformation.taxId, + }, + }; + + if (this.organization.useSecretsManager) { + request.secretsManager = { + seats: this.sub.smSeats, + additionalMachineAccounts: this.sub.smServiceAccounts, + }; + } + + this.taxService + .previewOrganizationInvoice(request) + .then((invoice) => { + this.estimatedTax = invoice.taxAmount; + }) + .catch((error) => { + this.toastService.showToast({ + title: "", + variant: "error", + message: this.i18nService.t(error.message), + }); + }); + } + + protected canUpdatePaymentInformation(): boolean { + return this.upgradeRequiresPaymentMethod || this.showPayment || this.isPaymentSourceEmpty(); + } } diff --git a/apps/web/src/app/billing/organizations/organization-plans.component.html b/apps/web/src/app/billing/organizations/organization-plans.component.html index e1b74abea71..d37f95e3aa2 100644 --- a/apps/web/src/app/billing/organizations/organization-plans.component.html +++ b/apps/web/src/app/billing/organizations/organization-plans.component.html @@ -335,7 +335,7 @@

    {{ "summary" | i18n }}

    >{{ "additionalUsers" | i18n }}: {{ "users" | i18n }}: - {{ formGroup.controls["additionalSeats"].value || 0 }} × + {{ formGroup.controls.additionalSeats.value || 0 }} × {{ (selectablePlan.isAnnual ? selectablePlan.PasswordManager.seatPrice / 12 @@ -355,7 +355,7 @@

    {{ "summary" | i18n }}

    *ngIf="selectablePlan.PasswordManager.hasAdditionalStorageOption" > {{ "additionalStorageGb" | i18n }}: - {{ formGroup.controls["additionalStorage"].value || 0 }} × + {{ formGroup.controls.additionalStorage.value || 0 }} × {{ (selectablePlan.isAnnual ? selectablePlan.PasswordManager.additionalStoragePricePerGb / 12 @@ -388,7 +388,7 @@

    {{ "summary" | i18n }}

    >{{ "additionalUsers" | i18n }}: {{ "users" | i18n }}: - {{ formGroup.controls["additionalSeats"].value || 0 }} × + {{ formGroup.controls.additionalSeats.value || 0 }} × {{ selectablePlan.PasswordManager.seatPrice | currency: "$" }} {{ "monthAbbr" | i18n }} = {{ @@ -403,7 +403,7 @@

    {{ "summary" | i18n }}

    *ngIf="selectablePlan.PasswordManager.hasAdditionalStorageOption" > {{ "additionalStorageGb" | i18n }}: - {{ formGroup.controls["additionalStorage"].value || 0 }} × + {{ formGroup.controls.additionalStorage.value || 0 }} × {{ selectablePlan.PasswordManager.additionalStoragePricePerGb | currency: "$" }} {{ "monthAbbr" | i18n }} = {{ additionalStorageTotal(selectablePlan) | currency: "$" }} /{{ "month" | i18n }} @@ -440,7 +440,12 @@

    - +
    {{ "passwordManagerPlanPrice" | i18n }}: {{ passwordManagerSubtotal | currency: "USD $" }} @@ -450,7 +455,7 @@


    - {{ "estimatedTax" | i18n }}: {{ taxCharges | currency: "USD $" }} + {{ "estimatedTax" | i18n }}: {{ estimatedTax | currency: "USD $" }}


    diff --git a/apps/web/src/app/billing/organizations/organization-plans.component.ts b/apps/web/src/app/billing/organizations/organization-plans.component.ts index e7a011792ae..4592f8de894 100644 --- a/apps/web/src/app/billing/organizations/organization-plans.component.ts +++ b/apps/web/src/app/billing/organizations/organization-plans.component.ts @@ -12,7 +12,9 @@ import { import { FormBuilder, Validators } from "@angular/forms"; import { Router } from "@angular/router"; import { Subject, takeUntil } from "rxjs"; +import { debounceTime } from "rxjs/operators"; +import { ManageTaxInformationComponent } from "@bitwarden/angular/billing/components"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; @@ -26,9 +28,12 @@ import { OrganizationUpgradeRequest } from "@bitwarden/common/admin-console/mode import { ProviderOrganizationCreateRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-organization-create.request"; import { ProviderResponse } from "@bitwarden/common/admin-console/models/response/provider/provider.response"; import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions"; +import { TaxServiceAbstraction } from "@bitwarden/common/billing/abstractions/tax.service.abstraction"; import { PaymentMethodType, PlanType, ProductTierType } from "@bitwarden/common/billing/enums"; +import { TaxInformation } from "@bitwarden/common/billing/models/domain"; import { ExpandedTaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/expanded-tax-info-update.request"; import { PaymentRequest } from "@bitwarden/common/billing/models/request/payment.request"; +import { PreviewOrganizationInvoiceRequest } from "@bitwarden/common/billing/models/request/preview-organization-invoice.request"; import { UpdatePaymentMethodRequest } from "@bitwarden/common/billing/models/request/update-payment-method.request"; import { BillingResponse } from "@bitwarden/common/billing/models/response/billing.response"; import { OrganizationSubscriptionResponse } from "@bitwarden/common/billing/models/response/organization-subscription.response"; @@ -50,7 +55,6 @@ import { OrganizationCreateModule } from "../../admin-console/organizations/crea import { BillingSharedModule, secretsManagerSubscribeFormFactory } from "../shared"; import { PaymentV2Component } from "../shared/payment/payment-v2.component"; import { PaymentComponent } from "../shared/payment/payment.component"; -import { TaxInfoComponent } from "../shared/tax-info.component"; interface OnSuccessArgs { organizationId: string; @@ -72,13 +76,14 @@ const Allowed2020PlansForLegacyProviders = [ export class OrganizationPlansComponent implements OnInit, OnDestroy { @ViewChild(PaymentComponent) paymentComponent: PaymentComponent; @ViewChild(PaymentV2Component) paymentV2Component: PaymentV2Component; - @ViewChild(TaxInfoComponent) taxComponent: TaxInfoComponent; + @ViewChild(ManageTaxInformationComponent) taxComponent: ManageTaxInformationComponent; - @Input() organizationId: string; + @Input() organizationId?: string; @Input() showFree = true; @Input() showCancel = false; @Input() acceptingSponsorship = false; @Input() currentPlan: PlanResponse; + selectedFile: File; @Input() @@ -93,6 +98,8 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { private _productTier = ProductTierType.Free; + protected taxInformation: TaxInformation; + @Input() get plan(): PlanType { return this._plan; @@ -149,7 +156,10 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { billing: BillingResponse; provider: ProviderResponse; - private destroy$ = new Subject(); + protected estimatedTax: number = 0; + protected total: number = 0; + + private destroy$: Subject = new Subject(); constructor( private apiService: ApiService, @@ -168,6 +178,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { private toastService: ToastService, private configService: ConfigService, private billingApiService: BillingApiServiceAbstraction, + private taxService: TaxServiceAbstraction, ) { this.selfHosted = this.platformUtilsService.isSelfHost(); } @@ -181,6 +192,9 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { this.organization = await this.organizationService.get(this.organizationId); this.billing = await this.organizationApiService.getBilling(this.organizationId); this.sub = await this.organizationApiService.getSubscription(this.organizationId); + this.taxInformation = await this.organizationApiService.getTaxInfo(this.organizationId); + } else { + this.taxInformation = await this.apiService.getTaxInfo(); } if (!this.selfHosted) { @@ -241,6 +255,16 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { } this.loading = false; + + this.formGroup.valueChanges.pipe(debounceTime(1000), takeUntil(this.destroy$)).subscribe(() => { + this.refreshSalesTax(); + }); + + this.secretsManagerForm.valueChanges + .pipe(debounceTime(1000), takeUntil(this.destroy$)) + .subscribe(() => { + this.refreshSalesTax(); + }); } ngOnDestroy() { @@ -438,17 +462,6 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { return this.selectedPlan.trialPeriodDays != null; } - get taxCharges() { - return this.taxComponent != null && this.taxComponent.taxRate != null - ? (this.taxComponent.taxRate / 100) * - (this.passwordManagerSubtotal + this.secretsManagerSubtotal) - : 0; - } - - get total() { - return this.passwordManagerSubtotal + this.secretsManagerSubtotal + this.taxCharges || 0; - } - get paymentDesc() { if (this.acceptingSponsorship) { return this.i18nService.t("paymentSponsored"); @@ -554,9 +567,9 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { this.changedProduct(); } - changedCountry() { + protected changedCountry(): void { if (this.deprecateStripeSourcesAPI) { - this.paymentV2Component.showBankAccount = this.taxComponent.country === "US"; + this.paymentV2Component.showBankAccount = this.taxInformation?.country === "US"; if ( !this.paymentV2Component.showBankAccount && this.paymentV2Component.selected === PaymentMethodType.BankAccount @@ -564,7 +577,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { this.paymentV2Component.select(PaymentMethodType.Card); } } else { - this.paymentComponent.hideBank = this.taxComponent.taxFormGroup?.value.country !== "US"; + this.paymentComponent.hideBank = this.taxInformation?.country !== "US"; if ( this.paymentComponent.hideBank && this.paymentComponent.method === PaymentMethodType.BankAccount @@ -575,28 +588,31 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { } } - cancel() { + protected onTaxInformationChanged(event: TaxInformation): void { + this.taxInformation = event; + this.changedCountry(); + this.refreshSalesTax(); + } + + protected cancel(): void { this.onCanceled.emit(); } - setSelectedFile(event: Event) { + protected setSelectedFile(event: Event): void { const fileInputEl = event.target; this.selectedFile = fileInputEl.files.length > 0 ? fileInputEl.files[0] : null; } submit = async () => { - if (this.taxComponent) { - if (!this.taxComponent?.taxFormGroup.valid) { - this.taxComponent?.taxFormGroup.markAllAsTouched(); - return; - } + if (this.taxComponent && !this.taxComponent.validate()) { + return; } if (this.singleOrgPolicyBlock) { return; } const doSubmit = async (): Promise => { - let orgId: string = null; + let orgId: string; if (this.createOrganization) { const orgKey = await this.keyService.makeOrgKey(); const key = orgKey[0].encryptedString; @@ -607,11 +623,9 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { const collectionCt = collection.encryptedString; const orgKeys = await this.keyService.makeKeyPair(orgKey[1]); - if (this.selfHosted) { - orgId = await this.createSelfHosted(key, collectionCt, orgKeys); - } else { - orgId = await this.createCloudHosted(key, collectionCt, orgKeys, orgKey[1]); - } + orgId = this.selfHosted + ? await this.createSelfHosted(key, collectionCt, orgKeys) + : await this.createCloudHosted(key, collectionCt, orgKeys, orgKey[1]); this.toastService.showToast({ variant: "success", @@ -619,7 +633,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { message: this.i18nService.t("organizationReadyToGo"), }); } else { - orgId = await this.updateOrganization(orgId); + orgId = await this.updateOrganization(); this.toastService.showToast({ variant: "success", title: null, @@ -653,7 +667,63 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { this.messagingService.send("organizationCreated", { organizationId }); }; - private async updateOrganization(orgId: string) { + protected get showTaxIdField(): boolean { + switch (this.formGroup.controls.productTier.value) { + case ProductTierType.Free: + case ProductTierType.Families: + return false; + default: + return true; + } + } + + private refreshSalesTax(): void { + if (this.formGroup.controls.plan.value == PlanType.Free) { + this.estimatedTax = 0; + return; + } + + if (!this.taxComponent.validate()) { + return; + } + + const request: PreviewOrganizationInvoiceRequest = { + organizationId: this.organizationId, + passwordManager: { + additionalStorage: this.formGroup.controls.additionalStorage.value, + plan: this.formGroup.controls.plan.value, + seats: this.formGroup.controls.additionalSeats.value, + }, + taxInformation: { + postalCode: this.taxInformation.postalCode, + country: this.taxInformation.country, + taxId: this.taxInformation.taxId, + }, + }; + + if (this.secretsManagerForm.controls.enabled.value === true) { + request.secretsManager = { + seats: this.secretsManagerForm.controls.userSeats.value, + additionalMachineAccounts: this.secretsManagerForm.controls.additionalServiceAccounts.value, + }; + } + + this.taxService + .previewOrganizationInvoice(request) + .then((invoice) => { + this.estimatedTax = invoice.taxAmount; + this.total = invoice.totalAmount; + }) + .catch((error) => { + this.toastService.showToast({ + title: "", + variant: "error", + message: this.i18nService.t(error.message), + }); + }); + } + + private async updateOrganization() { const request = new OrganizationUpgradeRequest(); request.additionalSeats = this.formGroup.controls.additionalSeats.value; request.additionalStorageGb = this.formGroup.controls.additionalStorage.value; @@ -661,8 +731,8 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { this.selectedPlan.PasswordManager.hasPremiumAccessOption && this.formGroup.controls.premiumAccessAddon.value; request.planType = this.selectedPlan.type; - request.billingAddressCountry = this.taxComponent.taxFormGroup?.value.country; - request.billingAddressPostalCode = this.taxComponent.taxFormGroup?.value.postalCode; + request.billingAddressCountry = this.taxInformation?.country; + request.billingAddressPostalCode = this.taxInformation?.postalCode; // Secrets Manager this.buildSecretsManagerRequest(request); @@ -671,10 +741,9 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { if (this.deprecateStripeSourcesAPI) { const updatePaymentMethodRequest = new UpdatePaymentMethodRequest(); updatePaymentMethodRequest.paymentSource = await this.paymentV2Component.tokenize(); - const expandedTaxInfoUpdateRequest = new ExpandedTaxInfoUpdateRequest(); - expandedTaxInfoUpdateRequest.country = this.taxComponent.country; - expandedTaxInfoUpdateRequest.postalCode = this.taxComponent.postalCode; - updatePaymentMethodRequest.taxInformation = expandedTaxInfoUpdateRequest; + updatePaymentMethodRequest.taxInformation = ExpandedTaxInfoUpdateRequest.From( + this.taxInformation, + ); await this.billingApiService.updateOrganizationPaymentMethod( this.organizationId, updatePaymentMethodRequest, @@ -684,8 +753,8 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { const paymentRequest = new PaymentRequest(); paymentRequest.paymentToken = paymentToken; paymentRequest.paymentMethodType = paymentMethodType; - paymentRequest.country = this.taxComponent.taxFormGroup?.value.country; - paymentRequest.postalCode = this.taxComponent.taxFormGroup?.value.postalCode; + paymentRequest.country = this.taxInformation?.country; + paymentRequest.postalCode = this.taxInformation?.postalCode; await this.organizationApiService.updatePayment(this.organizationId, paymentRequest); } } @@ -709,7 +778,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { collectionCt: string, orgKeys: [string, EncString], orgKey: SymmetricCryptoKey, - ) { + ): Promise { const request = new OrganizationCreateRequest(); request.key = key; request.collectionName = collectionCt; @@ -738,15 +807,13 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { this.selectedPlan.PasswordManager.hasPremiumAccessOption && this.formGroup.controls.premiumAccessAddon.value; request.planType = this.selectedPlan.type; - request.billingAddressPostalCode = this.taxComponent.taxFormGroup?.value.postalCode; - request.billingAddressCountry = this.taxComponent.taxFormGroup?.value.country; - if (this.taxComponent.taxFormGroup?.value.includeTaxId) { - request.taxIdNumber = this.taxComponent.taxFormGroup?.value.taxId; - request.billingAddressLine1 = this.taxComponent.taxFormGroup?.value.line1; - request.billingAddressLine2 = this.taxComponent.taxFormGroup?.value.line2; - request.billingAddressCity = this.taxComponent.taxFormGroup?.value.city; - request.billingAddressState = this.taxComponent.taxFormGroup?.value.state; - } + request.billingAddressPostalCode = this.taxInformation?.postalCode; + request.billingAddressCountry = this.taxInformation?.country; + request.taxIdNumber = this.taxInformation?.taxId; + request.billingAddressLine1 = this.taxInformation?.line1; + request.billingAddressLine2 = this.taxInformation?.line2; + request.billingAddressCity = this.taxInformation?.city; + request.billingAddressState = this.taxInformation?.state; } // Secrets Manager diff --git a/apps/web/src/app/billing/organizations/payment-method/organization-payment-method.component.html b/apps/web/src/app/billing/organizations/payment-method/organization-payment-method.component.html index 78f9955d31a..2c2dba938bc 100644 --- a/apps/web/src/app/billing/organizations/payment-method/organization-payment-method.component.html +++ b/apps/web/src/app/billing/organizations/payment-method/organization-payment-method.component.html @@ -63,20 +63,5 @@

    {{ "paymentMethod" | i18n }}

    {{ "paymentChargedWithUnpaidSubscription" | i18n }}

    - - -

    {{ "taxInformation" | i18n }}

    -

    {{ "taxInformationDesc" | i18n }}

    - - -
    diff --git a/apps/web/src/app/billing/organizations/payment-method/organization-payment-method.component.ts b/apps/web/src/app/billing/organizations/payment-method/organization-payment-method.component.ts index 4ed35461c72..270ba54f70d 100644 --- a/apps/web/src/app/billing/organizations/payment-method/organization-payment-method.component.ts +++ b/apps/web/src/app/billing/organizations/payment-method/organization-payment-method.component.ts @@ -1,7 +1,7 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { Location } from "@angular/common"; -import { Component, OnDestroy, ViewChild } from "@angular/core"; +import { Component, OnDestroy } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { ActivatedRoute, Router } from "@angular/router"; import { from, lastValueFrom, switchMap } from "rxjs"; @@ -11,7 +11,6 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions"; import { PaymentMethodType } from "@bitwarden/common/billing/enums"; -import { ExpandedTaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/expanded-tax-info-update.request"; import { VerifyBankAccountRequest } from "@bitwarden/common/billing/models/request/verify-bank-account.request"; import { OrganizationSubscriptionResponse } from "@bitwarden/common/billing/models/response/organization-subscription.response"; import { PaymentSourceResponse } from "@bitwarden/common/billing/models/response/payment-source.response"; @@ -22,7 +21,6 @@ import { DialogService, ToastService } from "@bitwarden/components"; import { FreeTrial } from "../../../core/types/free-trial"; import { TrialFlowService } from "../../services/trial-flow.service"; -import { TaxInfoComponent } from "../../shared"; import { AddCreditDialogResult, openAddCreditDialog, @@ -36,8 +34,6 @@ import { templateUrl: "./organization-payment-method.component.html", }) export class OrganizationPaymentMethodComponent implements OnDestroy { - @ViewChild(TaxInfoComponent) taxInfoComponent: TaxInfoComponent; - organizationId: string; isUnpaid = false; accountCredit: number; @@ -155,6 +151,7 @@ export class OrganizationPaymentMethodComponent implements OnDestroy { data: { initialPaymentMethod: this.paymentSource?.type, organizationId: this.organizationId, + productTier: this.organization?.productTierType, }, }); @@ -170,6 +167,7 @@ export class OrganizationPaymentMethodComponent implements OnDestroy { data: { initialPaymentMethod: this.paymentSource?.type, organizationId: this.organizationId, + productTier: this.organization?.productTierType, }, }); const result = await lastValueFrom(dialogRef.closed); @@ -183,32 +181,6 @@ export class OrganizationPaymentMethodComponent implements OnDestroy { } }; - protected updateTaxInformation = async (): Promise => { - this.taxInfoComponent.taxFormGroup.updateValueAndValidity(); - this.taxInfoComponent.taxFormGroup.markAllAsTouched(); - - if (this.taxInfoComponent.taxFormGroup.invalid) { - return; - } - - const request = new ExpandedTaxInfoUpdateRequest(); - request.country = this.taxInfoComponent.country; - request.postalCode = this.taxInfoComponent.postalCode; - request.taxId = this.taxInfoComponent.taxId; - request.line1 = this.taxInfoComponent.line1; - request.line2 = this.taxInfoComponent.line2; - request.city = this.taxInfoComponent.city; - request.state = this.taxInfoComponent.state; - - await this.billingApiService.updateOrganizationTaxInformation(this.organizationId, request); - - this.toastService.showToast({ - variant: "success", - title: null, - message: this.i18nService.t("taxInfoUpdated"), - }); - }; - protected verifyBankAccount = async (request: VerifyBankAccountRequest): Promise => { await this.billingApiService.verifyOrganizationBankAccount(this.organizationId, request); this.toastService.showToast({ diff --git a/apps/web/src/app/billing/shared/adjust-payment-dialog/adjust-payment-dialog-v2.component.html b/apps/web/src/app/billing/shared/adjust-payment-dialog/adjust-payment-dialog-v2.component.html index e41d3d961cd..bb06f87ca03 100644 --- a/apps/web/src/app/billing/shared/adjust-payment-dialog/adjust-payment-dialog-v2.component.html +++ b/apps/web/src/app/billing/shared/adjust-payment-dialog/adjust-payment-dialog-v2.component.html @@ -5,7 +5,12 @@ [showBankAccount]="!!organizationId" [initialPaymentMethod]="initialPaymentMethod" > - + - - diff --git a/apps/web/src/app/billing/shared/payment-method.component.ts b/apps/web/src/app/billing/shared/payment-method.component.ts index 298573f0852..149b4adf520 100644 --- a/apps/web/src/app/billing/shared/payment-method.component.ts +++ b/apps/web/src/app/billing/shared/payment-method.component.ts @@ -1,7 +1,7 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { Location } from "@angular/common"; -import { Component, OnDestroy, OnInit, ViewChild } from "@angular/core"; +import { Component, OnDestroy, OnInit } from "@angular/core"; import { FormBuilder, FormControl, Validators } from "@angular/forms"; import { ActivatedRoute, Router } from "@angular/router"; import { lastValueFrom } from "rxjs"; @@ -16,7 +16,6 @@ import { OrganizationSubscriptionResponse } from "@bitwarden/common/billing/mode import { SubscriptionResponse } from "@bitwarden/common/billing/models/response/subscription.response"; import { VerifyBankRequest } from "@bitwarden/common/models/request/verify-bank.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 { SyncService } from "@bitwarden/common/platform/sync"; import { DialogService, ToastService } from "@bitwarden/components"; @@ -29,15 +28,12 @@ import { AdjustPaymentDialogResult, openAdjustPaymentDialog, } from "./adjust-payment-dialog/adjust-payment-dialog.component"; -import { TaxInfoComponent } from "./tax-info.component"; @Component({ templateUrl: "payment-method.component.html", }) // eslint-disable-next-line rxjs-angular/prefer-takeuntil export class PaymentMethodComponent implements OnInit, OnDestroy { - @ViewChild(TaxInfoComponent) taxInfo: TaxInfoComponent; - loading = false; firstLoaded = false; billing: BillingPaymentResponse; @@ -61,7 +57,6 @@ export class PaymentMethodComponent implements OnInit, OnDestroy { ]), }); - taxForm = this.formBuilder.group({}); launchPaymentModalAutomatically = false; protected freeTrialData: FreeTrial; @@ -72,7 +67,6 @@ export class PaymentMethodComponent implements OnInit, OnDestroy { protected platformUtilsService: PlatformUtilsService, private router: Router, private location: Location, - private logService: LogService, private route: ActivatedRoute, private formBuilder: FormBuilder, private dialogService: DialogService, @@ -198,15 +192,6 @@ export class PaymentMethodComponent implements OnInit, OnDestroy { await this.load(); }; - submitTaxInfo = async () => { - await this.taxInfo.submitTaxInfo(); - this.toastService.showToast({ - variant: "success", - title: null, - message: this.i18nService.t("taxInfoUpdated"), - }); - }; - determineOrgsWithUpcomingPaymentIssues() { this.freeTrialData = this.trialFlowService.checkForOrgsWithUpcomingPaymentIssues( this.organization, @@ -231,10 +216,6 @@ export class PaymentMethodComponent implements OnInit, OnDestroy { return this.organizationId != null; } - get headerClass() { - return this.forOrganization ? ["page-header"] : ["tabbed-header"]; - } - get paymentSourceClasses() { if (this.paymentSource == null) { return []; diff --git a/apps/web/src/app/billing/shared/tax-info.component.html b/apps/web/src/app/billing/shared/tax-info.component.html index 82d5104a53a..4a42c0c1109 100644 --- a/apps/web/src/app/billing/shared/tax-info.component.html +++ b/apps/web/src/app/billing/shared/tax-info.component.html @@ -13,51 +13,41 @@
    -
    +
    {{ "zipPostalCode" | i18n }}
    -
    - - - {{ "includeVAT" | i18n }} - -
    -
    -
    -
    - - {{ "taxIdNumber" | i18n }} - - -
    -
    -
    -
    +
    {{ "address1" | i18n }}
    -
    +
    {{ "address2" | i18n }}
    -
    +
    {{ "cityTown" | i18n }}
    -
    +
    {{ "stateProvince" | i18n }}
    +
    + + {{ "taxIdNumber" | i18n }} + + +
    diff --git a/apps/web/src/app/billing/shared/tax-info.component.ts b/apps/web/src/app/billing/shared/tax-info.component.ts index 8ebec5e1dfe..214364e4cf2 100644 --- a/apps/web/src/app/billing/shared/tax-info.component.ts +++ b/apps/web/src/app/billing/shared/tax-info.component.ts @@ -1,31 +1,20 @@ // 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 { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from "@angular/core"; import { FormControl, FormGroup, Validators } from "@angular/forms"; import { ActivatedRoute } from "@angular/router"; import { Subject, takeUntil } from "rxjs"; +import { debounceTime } from "rxjs/operators"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; +import { TaxServiceAbstraction } from "@bitwarden/common/billing/abstractions/tax.service.abstraction"; +import { CountryListItem } from "@bitwarden/common/billing/models/domain"; import { ExpandedTaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/expanded-tax-info-update.request"; -import { TaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/tax-info-update.request"; -import { TaxInfoResponse } from "@bitwarden/common/billing/models/response/tax-info.response"; -import { TaxRateResponse } from "@bitwarden/common/billing/models/response/tax-rate.response"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { SharedModule } from "../../shared"; -type TaxInfoView = Omit & { - includeTaxId: boolean; - [key: string]: unknown; -}; - -type CountryList = { - name: string; - value: string; - disabled: boolean; -}; - @Component({ selector: "app-tax-info", templateUrl: "tax-info.component.html", @@ -33,359 +22,68 @@ type CountryList = { imports: [SharedModule], }) // eslint-disable-next-line rxjs-angular/prefer-takeuntil -export class TaxInfoComponent implements OnInit { - @Input() trialFlow = false; - @Output() onCountryChanged = new EventEmitter(); +export class TaxInfoComponent implements OnInit, OnDestroy { private destroy$ = new Subject(); + @Input() trialFlow = false; + @Output() countryChanged = new EventEmitter(); + @Output() taxInformationChanged: EventEmitter = new EventEmitter(); + taxFormGroup = new FormGroup({ - country: new FormControl(null, [Validators.required]), - postalCode: new FormControl(null), - includeTaxId: new FormControl(null), - taxId: new FormControl(null), - line1: new FormControl(null), - line2: new FormControl(null), - city: new FormControl(null), - state: new FormControl(null), + country: new FormControl(null, [Validators.required]), + postalCode: new FormControl(null, [Validators.required]), + taxId: new FormControl(null), + line1: new FormControl(null), + line2: new FormControl(null), + city: new FormControl(null), + state: new FormControl(null), }); + protected isTaxSupported: boolean; + loading = true; organizationId: string; providerId: string; - taxInfo: TaxInfoView = { - taxId: null, - line1: null, - line2: null, - city: null, - state: null, - postalCode: null, - country: "US", - includeTaxId: false, - }; - countryList: CountryList[] = [ - { name: "-- Select --", value: "", disabled: false }, - { name: "United States", value: "US", disabled: false }, - { name: "China", value: "CN", disabled: false }, - { name: "France", value: "FR", disabled: false }, - { name: "Germany", value: "DE", disabled: false }, - { name: "Canada", value: "CA", disabled: false }, - { name: "United Kingdom", value: "GB", disabled: false }, - { name: "Australia", value: "AU", disabled: false }, - { name: "India", value: "IN", disabled: false }, - { name: "", value: "-", disabled: true }, - { name: "Afghanistan", value: "AF", disabled: false }, - { name: "Åland Islands", value: "AX", disabled: false }, - { name: "Albania", value: "AL", disabled: false }, - { name: "Algeria", value: "DZ", disabled: false }, - { name: "American Samoa", value: "AS", disabled: false }, - { name: "Andorra", value: "AD", disabled: false }, - { name: "Angola", value: "AO", disabled: false }, - { name: "Anguilla", value: "AI", disabled: false }, - { name: "Antarctica", value: "AQ", disabled: false }, - { name: "Antigua and Barbuda", value: "AG", disabled: false }, - { name: "Argentina", value: "AR", disabled: false }, - { name: "Armenia", value: "AM", disabled: false }, - { name: "Aruba", value: "AW", disabled: false }, - { name: "Austria", value: "AT", disabled: false }, - { name: "Azerbaijan", value: "AZ", disabled: false }, - { name: "Bahamas", value: "BS", disabled: false }, - { name: "Bahrain", value: "BH", disabled: false }, - { name: "Bangladesh", value: "BD", disabled: false }, - { name: "Barbados", value: "BB", disabled: false }, - { name: "Belarus", value: "BY", disabled: false }, - { name: "Belgium", value: "BE", disabled: false }, - { name: "Belize", value: "BZ", disabled: false }, - { name: "Benin", value: "BJ", disabled: false }, - { name: "Bermuda", value: "BM", disabled: false }, - { name: "Bhutan", value: "BT", disabled: false }, - { name: "Bolivia, Plurinational State of", value: "BO", disabled: false }, - { name: "Bonaire, Sint Eustatius and Saba", value: "BQ", disabled: false }, - { name: "Bosnia and Herzegovina", value: "BA", disabled: false }, - { name: "Botswana", value: "BW", disabled: false }, - { name: "Bouvet Island", value: "BV", disabled: false }, - { name: "Brazil", value: "BR", disabled: false }, - { name: "British Indian Ocean Territory", value: "IO", disabled: false }, - { name: "Brunei Darussalam", value: "BN", disabled: false }, - { name: "Bulgaria", value: "BG", disabled: false }, - { name: "Burkina Faso", value: "BF", disabled: false }, - { name: "Burundi", value: "BI", disabled: false }, - { name: "Cambodia", value: "KH", disabled: false }, - { name: "Cameroon", value: "CM", disabled: false }, - { name: "Cape Verde", value: "CV", disabled: false }, - { name: "Cayman Islands", value: "KY", disabled: false }, - { name: "Central African Republic", value: "CF", disabled: false }, - { name: "Chad", value: "TD", disabled: false }, - { name: "Chile", value: "CL", disabled: false }, - { name: "Christmas Island", value: "CX", disabled: false }, - { name: "Cocos (Keeling) Islands", value: "CC", disabled: false }, - { name: "Colombia", value: "CO", disabled: false }, - { name: "Comoros", value: "KM", disabled: false }, - { name: "Congo", value: "CG", disabled: false }, - { name: "Congo, the Democratic Republic of the", value: "CD", disabled: false }, - { name: "Cook Islands", value: "CK", disabled: false }, - { name: "Costa Rica", value: "CR", disabled: false }, - { name: "Côte d'Ivoire", value: "CI", disabled: false }, - { name: "Croatia", value: "HR", disabled: false }, - { name: "Cuba", value: "CU", disabled: false }, - { name: "Curaçao", value: "CW", disabled: false }, - { name: "Cyprus", value: "CY", disabled: false }, - { name: "Czech Republic", value: "CZ", disabled: false }, - { name: "Denmark", value: "DK", disabled: false }, - { name: "Djibouti", value: "DJ", disabled: false }, - { name: "Dominica", value: "DM", disabled: false }, - { name: "Dominican Republic", value: "DO", disabled: false }, - { name: "Ecuador", value: "EC", disabled: false }, - { name: "Egypt", value: "EG", disabled: false }, - { name: "El Salvador", value: "SV", disabled: false }, - { name: "Equatorial Guinea", value: "GQ", disabled: false }, - { name: "Eritrea", value: "ER", disabled: false }, - { name: "Estonia", value: "EE", disabled: false }, - { name: "Ethiopia", value: "ET", disabled: false }, - { name: "Falkland Islands (Malvinas)", value: "FK", disabled: false }, - { name: "Faroe Islands", value: "FO", disabled: false }, - { name: "Fiji", value: "FJ", disabled: false }, - { name: "Finland", value: "FI", disabled: false }, - { name: "French Guiana", value: "GF", disabled: false }, - { name: "French Polynesia", value: "PF", disabled: false }, - { name: "French Southern Territories", value: "TF", disabled: false }, - { name: "Gabon", value: "GA", disabled: false }, - { name: "Gambia", value: "GM", disabled: false }, - { name: "Georgia", value: "GE", disabled: false }, - { name: "Ghana", value: "GH", disabled: false }, - { name: "Gibraltar", value: "GI", disabled: false }, - { name: "Greece", value: "GR", disabled: false }, - { name: "Greenland", value: "GL", disabled: false }, - { name: "Grenada", value: "GD", disabled: false }, - { name: "Guadeloupe", value: "GP", disabled: false }, - { name: "Guam", value: "GU", disabled: false }, - { name: "Guatemala", value: "GT", disabled: false }, - { name: "Guernsey", value: "GG", disabled: false }, - { name: "Guinea", value: "GN", disabled: false }, - { name: "Guinea-Bissau", value: "GW", disabled: false }, - { name: "Guyana", value: "GY", disabled: false }, - { name: "Haiti", value: "HT", disabled: false }, - { name: "Heard Island and McDonald Islands", value: "HM", disabled: false }, - { name: "Holy See (Vatican City State)", value: "VA", disabled: false }, - { name: "Honduras", value: "HN", disabled: false }, - { name: "Hong Kong", value: "HK", disabled: false }, - { name: "Hungary", value: "HU", disabled: false }, - { name: "Iceland", value: "IS", disabled: false }, - { name: "Indonesia", value: "ID", disabled: false }, - { name: "Iran, Islamic Republic of", value: "IR", disabled: false }, - { name: "Iraq", value: "IQ", disabled: false }, - { name: "Ireland", value: "IE", disabled: false }, - { name: "Isle of Man", value: "IM", disabled: false }, - { name: "Israel", value: "IL", disabled: false }, - { name: "Italy", value: "IT", disabled: false }, - { name: "Jamaica", value: "JM", disabled: false }, - { name: "Japan", value: "JP", disabled: false }, - { name: "Jersey", value: "JE", disabled: false }, - { name: "Jordan", value: "JO", disabled: false }, - { name: "Kazakhstan", value: "KZ", disabled: false }, - { name: "Kenya", value: "KE", disabled: false }, - { name: "Kiribati", value: "KI", disabled: false }, - { name: "Korea, Democratic People's Republic of", value: "KP", disabled: false }, - { name: "Korea, Republic of", value: "KR", disabled: false }, - { name: "Kuwait", value: "KW", disabled: false }, - { name: "Kyrgyzstan", value: "KG", disabled: false }, - { name: "Lao People's Democratic Republic", value: "LA", disabled: false }, - { name: "Latvia", value: "LV", disabled: false }, - { name: "Lebanon", value: "LB", disabled: false }, - { name: "Lesotho", value: "LS", disabled: false }, - { name: "Liberia", value: "LR", disabled: false }, - { name: "Libya", value: "LY", disabled: false }, - { name: "Liechtenstein", value: "LI", disabled: false }, - { name: "Lithuania", value: "LT", disabled: false }, - { name: "Luxembourg", value: "LU", disabled: false }, - { name: "Macao", value: "MO", disabled: false }, - { name: "Macedonia, the former Yugoslav Republic of", value: "MK", disabled: false }, - { name: "Madagascar", value: "MG", disabled: false }, - { name: "Malawi", value: "MW", disabled: false }, - { name: "Malaysia", value: "MY", disabled: false }, - { name: "Maldives", value: "MV", disabled: false }, - { name: "Mali", value: "ML", disabled: false }, - { name: "Malta", value: "MT", disabled: false }, - { name: "Marshall Islands", value: "MH", disabled: false }, - { name: "Martinique", value: "MQ", disabled: false }, - { name: "Mauritania", value: "MR", disabled: false }, - { name: "Mauritius", value: "MU", disabled: false }, - { name: "Mayotte", value: "YT", disabled: false }, - { name: "Mexico", value: "MX", disabled: false }, - { name: "Micronesia, Federated States of", value: "FM", disabled: false }, - { name: "Moldova, Republic of", value: "MD", disabled: false }, - { name: "Monaco", value: "MC", disabled: false }, - { name: "Mongolia", value: "MN", disabled: false }, - { name: "Montenegro", value: "ME", disabled: false }, - { name: "Montserrat", value: "MS", disabled: false }, - { name: "Morocco", value: "MA", disabled: false }, - { name: "Mozambique", value: "MZ", disabled: false }, - { name: "Myanmar", value: "MM", disabled: false }, - { name: "Namibia", value: "NA", disabled: false }, - { name: "Nauru", value: "NR", disabled: false }, - { name: "Nepal", value: "NP", disabled: false }, - { name: "Netherlands", value: "NL", disabled: false }, - { name: "New Caledonia", value: "NC", disabled: false }, - { name: "New Zealand", value: "NZ", disabled: false }, - { name: "Nicaragua", value: "NI", disabled: false }, - { name: "Niger", value: "NE", disabled: false }, - { name: "Nigeria", value: "NG", disabled: false }, - { name: "Niue", value: "NU", disabled: false }, - { name: "Norfolk Island", value: "NF", disabled: false }, - { name: "Northern Mariana Islands", value: "MP", disabled: false }, - { name: "Norway", value: "NO", disabled: false }, - { name: "Oman", value: "OM", disabled: false }, - { name: "Pakistan", value: "PK", disabled: false }, - { name: "Palau", value: "PW", disabled: false }, - { name: "Palestinian Territory, Occupied", value: "PS", disabled: false }, - { name: "Panama", value: "PA", disabled: false }, - { name: "Papua New Guinea", value: "PG", disabled: false }, - { name: "Paraguay", value: "PY", disabled: false }, - { name: "Peru", value: "PE", disabled: false }, - { name: "Philippines", value: "PH", disabled: false }, - { name: "Pitcairn", value: "PN", disabled: false }, - { name: "Poland", value: "PL", disabled: false }, - { name: "Portugal", value: "PT", disabled: false }, - { name: "Puerto Rico", value: "PR", disabled: false }, - { name: "Qatar", value: "QA", disabled: false }, - { name: "Réunion", value: "RE", disabled: false }, - { name: "Romania", value: "RO", disabled: false }, - { name: "Russian Federation", value: "RU", disabled: false }, - { name: "Rwanda", value: "RW", disabled: false }, - { name: "Saint Barthélemy", value: "BL", disabled: false }, - { name: "Saint Helena, Ascension and Tristan da Cunha", value: "SH", disabled: false }, - { name: "Saint Kitts and Nevis", value: "KN", disabled: false }, - { name: "Saint Lucia", value: "LC", disabled: false }, - { name: "Saint Martin (French part)", value: "MF", disabled: false }, - { name: "Saint Pierre and Miquelon", value: "PM", disabled: false }, - { name: "Saint Vincent and the Grenadines", value: "VC", disabled: false }, - { name: "Samoa", value: "WS", disabled: false }, - { name: "San Marino", value: "SM", disabled: false }, - { name: "Sao Tome and Principe", value: "ST", disabled: false }, - { name: "Saudi Arabia", value: "SA", disabled: false }, - { name: "Senegal", value: "SN", disabled: false }, - { name: "Serbia", value: "RS", disabled: false }, - { name: "Seychelles", value: "SC", disabled: false }, - { name: "Sierra Leone", value: "SL", disabled: false }, - { name: "Singapore", value: "SG", disabled: false }, - { name: "Sint Maarten (Dutch part)", value: "SX", disabled: false }, - { name: "Slovakia", value: "SK", disabled: false }, - { name: "Slovenia", value: "SI", disabled: false }, - { name: "Solomon Islands", value: "SB", disabled: false }, - { name: "Somalia", value: "SO", disabled: false }, - { name: "South Africa", value: "ZA", disabled: false }, - { name: "South Georgia and the South Sandwich Islands", value: "GS", disabled: false }, - { name: "South Sudan", value: "SS", disabled: false }, - { name: "Spain", value: "ES", disabled: false }, - { name: "Sri Lanka", value: "LK", disabled: false }, - { name: "Sudan", value: "SD", disabled: false }, - { name: "Suriname", value: "SR", disabled: false }, - { name: "Svalbard and Jan Mayen", value: "SJ", disabled: false }, - { name: "Swaziland", value: "SZ", disabled: false }, - { name: "Sweden", value: "SE", disabled: false }, - { name: "Switzerland", value: "CH", disabled: false }, - { name: "Syrian Arab Republic", value: "SY", disabled: false }, - { name: "Taiwan", value: "TW", disabled: false }, - { name: "Tajikistan", value: "TJ", disabled: false }, - { name: "Tanzania, United Republic of", value: "TZ", disabled: false }, - { name: "Thailand", value: "TH", disabled: false }, - { name: "Timor-Leste", value: "TL", disabled: false }, - { name: "Togo", value: "TG", disabled: false }, - { name: "Tokelau", value: "TK", disabled: false }, - { name: "Tonga", value: "TO", disabled: false }, - { name: "Trinidad and Tobago", value: "TT", disabled: false }, - { name: "Tunisia", value: "TN", disabled: false }, - { name: "Turkey", value: "TR", disabled: false }, - { name: "Turkmenistan", value: "TM", disabled: false }, - { name: "Turks and Caicos Islands", value: "TC", disabled: false }, - { name: "Tuvalu", value: "TV", disabled: false }, - { name: "Uganda", value: "UG", disabled: false }, - { name: "Ukraine", value: "UA", disabled: false }, - { name: "United Arab Emirates", value: "AE", disabled: false }, - { name: "United States Minor Outlying Islands", value: "UM", disabled: false }, - { name: "Uruguay", value: "UY", disabled: false }, - { name: "Uzbekistan", value: "UZ", disabled: false }, - { name: "Vanuatu", value: "VU", disabled: false }, - { name: "Venezuela, Bolivarian Republic of", value: "VE", disabled: false }, - { name: "Viet Nam", value: "VN", disabled: false }, - { name: "Virgin Islands, British", value: "VG", disabled: false }, - { name: "Virgin Islands, U.S.", value: "VI", disabled: false }, - { name: "Wallis and Futuna", value: "WF", disabled: false }, - { name: "Western Sahara", value: "EH", disabled: false }, - { name: "Yemen", value: "YE", disabled: false }, - { name: "Zambia", value: "ZM", disabled: false }, - { name: "Zimbabwe", value: "ZW", disabled: false }, - ]; - taxRates: TaxRateResponse[]; + countryList: CountryListItem[] = this.taxService.getCountries(); constructor( private apiService: ApiService, private route: ActivatedRoute, private logService: LogService, private organizationApiService: OrganizationApiServiceAbstraction, + private taxService: TaxServiceAbstraction, ) {} get country(): string { - return this.taxFormGroup.get("country").value; - } - - set country(country: string) { - this.taxFormGroup.get("country").setValue(country); + return this.taxFormGroup.controls.country.value; } get postalCode(): string { - return this.taxFormGroup.get("postalCode").value; - } - - set postalCode(postalCode: string) { - this.taxFormGroup.get("postalCode").setValue(postalCode); - } - - get includeTaxId(): boolean { - return this.taxFormGroup.get("includeTaxId").value; - } - - set includeTaxId(includeTaxId: boolean) { - this.taxFormGroup.get("includeTaxId").setValue(includeTaxId); + return this.taxFormGroup.controls.postalCode.value; } get taxId(): string { - return this.taxFormGroup.get("taxId").value; - } - - set taxId(taxId: string) { - this.taxFormGroup.get("taxId").setValue(taxId); + return this.taxFormGroup.controls.taxId.value; } get line1(): string { - return this.taxFormGroup.get("line1").value; - } - - set line1(line1: string) { - this.taxFormGroup.get("line1").setValue(line1); + return this.taxFormGroup.controls.line1.value; } get line2(): string { - return this.taxFormGroup.get("line2").value; - } - - set line2(line2: string) { - this.taxFormGroup.get("line2").setValue(line2); + return this.taxFormGroup.controls.line2.value; } get city(): string { - return this.taxFormGroup.get("city").value; - } - - set city(city: string) { - this.taxFormGroup.get("city").setValue(city); + return this.taxFormGroup.controls.city.value; } get state(): string { - return this.taxFormGroup.get("state").value; + return this.taxFormGroup.controls.state.value; } - set state(state: string) { - this.taxFormGroup.get("state").setValue(state); + get showTaxIdField(): boolean { + return !!this.organizationId; } async ngOnInit() { @@ -402,22 +100,13 @@ export class TaxInfoComponent implements OnInit { try { const taxInfo = await this.organizationApiService.getTaxInfo(this.organizationId); if (taxInfo) { - this.taxId = taxInfo.taxId; - this.state = taxInfo.state; - this.line1 = taxInfo.line1; - this.line2 = taxInfo.line2; - this.city = taxInfo.city; - this.state = taxInfo.state; - this.postalCode = taxInfo.postalCode; - this.country = taxInfo.country || "US"; - this.includeTaxId = - this.countrySupportsTax(this.country) && - (!!taxInfo.taxId || - !!taxInfo.line1 || - !!taxInfo.line2 || - !!taxInfo.city || - !!taxInfo.state); - this.setTaxInfoObject(); + this.taxFormGroup.controls.taxId.setValue(taxInfo.taxId); + this.taxFormGroup.controls.state.setValue(taxInfo.state); + this.taxFormGroup.controls.line1.setValue(taxInfo.line1); + this.taxFormGroup.controls.line2.setValue(taxInfo.line2); + this.taxFormGroup.controls.city.setValue(taxInfo.city); + this.taxFormGroup.controls.postalCode.setValue(taxInfo.postalCode); + this.taxFormGroup.controls.country.setValue(taxInfo.country); } } catch (e) { this.logService.error(e); @@ -426,119 +115,79 @@ export class TaxInfoComponent implements OnInit { try { const taxInfo = await this.apiService.getTaxInfo(); if (taxInfo) { - this.postalCode = taxInfo.postalCode; - this.country = taxInfo.country || "US"; + this.taxFormGroup.controls.postalCode.setValue(taxInfo.postalCode); + this.taxFormGroup.controls.country.setValue(taxInfo.country); } - this.setTaxInfoObject(); } catch (e) { this.logService.error(e); } } - if (this.country === "US") { - this.taxFormGroup.get("postalCode").setValidators([Validators.required]); - this.taxFormGroup.get("postalCode").updateValueAndValidity(); - } + this.isTaxSupported = await this.taxService.isCountrySupported( + this.taxFormGroup.controls.country.value, + ); - if (this.country !== "US") { - this.onCountryChanged.emit(); - } + this.countryChanged.emit(); }); - this.taxFormGroup - .get("country") - .valueChanges.pipe(takeUntil(this.destroy$)) + this.taxFormGroup.controls.country.valueChanges + .pipe(debounceTime(1000), takeUntil(this.destroy$)) .subscribe((value) => { - if (value === "US") { - this.taxFormGroup.get("postalCode").setValidators([Validators.required]); - } else { - this.taxFormGroup.get("postalCode").clearValidators(); - } - this.taxFormGroup.get("postalCode").updateValueAndValidity(); - this.setTaxInfoObject(); - this.changeCountry(); + this.taxService + .isCountrySupported(this.taxFormGroup.controls.country.value) + .then((isSupported) => { + this.isTaxSupported = isSupported; + }) + .catch(() => { + this.isTaxSupported = false; + }) + .finally(() => { + if (!this.isTaxSupported) { + this.taxFormGroup.controls.taxId.setValue(null); + this.taxFormGroup.controls.line1.setValue(null); + this.taxFormGroup.controls.line2.setValue(null); + this.taxFormGroup.controls.city.setValue(null); + this.taxFormGroup.controls.state.setValue(null); + } + + this.countryChanged.emit(); + }); + this.taxInformationChanged.emit(); }); - try { - const taxRates = await this.apiService.getTaxRates(); - if (taxRates) { - this.taxRates = taxRates.data; - } - } catch (e) { - this.logService.error(e); - } finally { - this.loading = false; - } - } - - get taxRate() { - if (this.taxRates != null) { - const localTaxRate = this.taxRates.find( - (x) => x.country === this.country && x.postalCode === this.postalCode, - ); - return localTaxRate?.rate ?? null; - } - } - - setTaxInfoObject() { - this.taxInfo.country = this.country; - this.taxInfo.postalCode = this.postalCode; - this.taxInfo.includeTaxId = this.includeTaxId; - this.taxInfo.taxId = this.taxId; - this.taxInfo.line1 = this.line1; - this.taxInfo.line2 = this.line2; - this.taxInfo.city = this.city; - this.taxInfo.state = this.state; - } + this.taxFormGroup.controls.postalCode.valueChanges + .pipe(debounceTime(1000), takeUntil(this.destroy$)) + .subscribe(() => { + this.taxInformationChanged.emit(); + }); - get showTaxIdCheckbox() { - return ( - (this.organizationId || this.providerId) && - this.country !== "US" && - this.countrySupportsTax(this.taxInfo.country) - ); - } + this.taxFormGroup.controls.taxId.valueChanges + .pipe(debounceTime(1000), takeUntil(this.destroy$)) + .subscribe(() => { + this.taxInformationChanged.emit(); + }); - get showTaxIdFields() { - return ( - (this.organizationId || this.providerId) && - this.includeTaxId && - this.countrySupportsTax(this.country) - ); + this.loading = false; } - getTaxInfoRequest(): TaxInfoUpdateRequest { - if (this.organizationId || this.providerId) { - const request = new ExpandedTaxInfoUpdateRequest(); - request.country = this.country; - request.postalCode = this.postalCode; - - if (this.includeTaxId) { - request.taxId = this.taxId; - request.line1 = this.line1; - request.line2 = this.line2; - request.city = this.city; - request.state = this.state; - } else { - request.taxId = null; - request.line1 = null; - request.line2 = null; - request.city = null; - request.state = null; - } - return request; - } else { - const request = new TaxInfoUpdateRequest(); - request.postalCode = this.postalCode; - request.country = this.country; - return request; - } + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); } submitTaxInfo(): Promise { this.taxFormGroup.updateValueAndValidity(); this.taxFormGroup.markAllAsTouched(); - const request = this.getTaxInfoRequest(); + + const request = new ExpandedTaxInfoUpdateRequest(); + request.country = this.country; + request.postalCode = this.postalCode; + request.taxId = this.taxId; + request.line1 = this.line1; + request.line2 = this.line2; + request.city = this.city; + request.state = this.state; + return this.organizationId ? this.organizationApiService.updateTaxInfo( this.organizationId, @@ -546,97 +195,4 @@ export class TaxInfoComponent implements OnInit { ) : this.apiService.putTaxInfo(request); } - - changeCountry() { - if (!this.countrySupportsTax(this.country)) { - this.includeTaxId = false; - this.taxId = null; - this.line1 = null; - this.line2 = null; - this.city = null; - this.state = null; - this.setTaxInfoObject(); - } - this.onCountryChanged.emit(); - } - - countrySupportsTax(countryCode: string) { - return this.taxSupportedCountryCodes.includes(countryCode); - } - - private taxSupportedCountryCodes: string[] = [ - "CN", - "FR", - "DE", - "CA", - "GB", - "AU", - "IN", - "AD", - "AR", - "AT", - "BE", - "BO", - "BR", - "BG", - "CL", - "CO", - "CR", - "HR", - "CY", - "CZ", - "DK", - "DO", - "EC", - "EG", - "SV", - "EE", - "FI", - "GE", - "GR", - "HK", - "HU", - "IS", - "ID", - "IQ", - "IE", - "IL", - "IT", - "JP", - "KE", - "KR", - "LV", - "LI", - "LT", - "LU", - "MY", - "MT", - "MX", - "NL", - "NZ", - "NO", - "PE", - "PH", - "PL", - "PT", - "RO", - "RU", - "SA", - "RS", - "SG", - "SK", - "SI", - "ZA", - "ES", - "SE", - "CH", - "TW", - "TH", - "TR", - "UA", - "AE", - "UY", - "VE", - "VN", - ]; } diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 7c338fc6e97..08e08ccad15 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -9275,6 +9275,18 @@ "updatedTaxInformation": { "message": "Updated tax information" }, + "billingInvalidTaxIdError": { + "message": "Invalid tax ID, if you believe this is an error please contact support." + }, + "billingTaxIdTypeInferenceError": { + "message": "We were unable to validate your tax ID, if you believe this is an error please contact support." + }, + "billingPreviewInvalidTaxIdError": { + "message": "Invalid tax ID, if you believe this is an error please contact support." + }, + "billingPreviewInvoiceError": { + "message": "An error occurred while previewing the invoice. Please try again later." + }, "unverified": { "message": "Unverified" }, 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 33a20444c2b..74aa468c42e 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 @@ -29,7 +29,7 @@

    {{ "generalInformation" | 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 46fd6989681..f773db6c11c 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 @@ -111,9 +111,7 @@ export class SetupComponent implements OnInit, OnDestroy { try { this.formGroup.markAllAsTouched(); - const formIsValid = this.formGroup.valid && this.manageTaxInformationComponent.touch(); - - if (!formIsValid) { + if (!this.manageTaxInformationComponent.validate() || !this.formGroup.valid) { return; } @@ -131,14 +129,11 @@ export class SetupComponent implements OnInit, OnDestroy { request.taxInfo.country = taxInformation.country; request.taxInfo.postalCode = taxInformation.postalCode; - - if (taxInformation.includeTaxId) { - request.taxInfo.taxId = taxInformation.taxId; - request.taxInfo.line1 = taxInformation.line1; - request.taxInfo.line2 = taxInformation.line2; - request.taxInfo.city = taxInformation.city; - request.taxInfo.state = taxInformation.state; - } + request.taxInfo.taxId = taxInformation.taxId; + request.taxInfo.line1 = taxInformation.line1; + request.taxInfo.line2 = taxInformation.line2; + request.taxInfo.city = taxInformation.city; + request.taxInfo.state = taxInformation.state; const provider = await this.providerApiService.postProviderSetup(this.providerId, request); diff --git a/libs/angular/src/billing/components/manage-tax-information/manage-tax-information.component.html b/libs/angular/src/billing/components/manage-tax-information/manage-tax-information.component.html index 0b041bd4c06..3f635656fb7 100644 --- a/libs/angular/src/billing/components/manage-tax-information/manage-tax-information.component.html +++ b/libs/angular/src/billing/components/manage-tax-information/manage-tax-information.component.html @@ -1,7 +1,7 @@
    - + {{ "country" | i18n }}
    - + {{ "zipPostalCode" | i18n }}
    -
    - - - {{ "includeVAT" | i18n }} - -
    -
    -
    -
    - - {{ "taxIdNumber" | i18n }} - - -
    -
    -
    -
    - - {{ "address1" | i18n }} - - -
    -
    - - {{ "address2" | i18n }} - - -
    -
    - - {{ "cityTown" | i18n }} - - -
    -
    - - {{ "stateProvince" | i18n }} - - + +
    + + {{ "address1" | i18n }} + + +
    +
    + + {{ "address2" | i18n }} + + +
    +
    + + {{ "cityTown" | i18n }} + + +
    +
    + + {{ "stateProvince" | i18n }} + + +
    +
    + + {{ "taxIdNumber" | i18n }} + + +
    +
    +
    +
    - diff --git a/libs/angular/src/billing/components/manage-tax-information/manage-tax-information.component.ts b/libs/angular/src/billing/components/manage-tax-information/manage-tax-information.component.ts index a3564b1ebc9..13a6d2d0cc3 100644 --- a/libs/angular/src/billing/components/manage-tax-information/manage-tax-information.component.ts +++ b/libs/angular/src/billing/components/manage-tax-information/manage-tax-information.component.ts @@ -3,14 +3,10 @@ import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from "@angular/core"; import { FormBuilder, Validators } from "@angular/forms"; import { Subject, takeUntil } from "rxjs"; +import { debounceTime } from "rxjs/operators"; -import { TaxInformation } from "@bitwarden/common/billing/models/domain"; - -type Country = { - name: string; - value: string; - disabled: boolean; -}; +import { TaxServiceAbstraction } from "@bitwarden/common/billing/abstractions/tax.service.abstraction"; +import { CountryListItem, TaxInformation } from "@bitwarden/common/billing/models/domain"; @Component({ selector: "app-manage-tax-information", @@ -19,12 +15,23 @@ type Country = { export class ManageTaxInformationComponent implements OnInit, OnDestroy { @Input() startWith: TaxInformation; @Input() onSubmit?: (taxInformation: TaxInformation) => Promise; + @Input() showTaxIdField: boolean = true; + + /** + * Emits when the tax information has changed. + */ + @Output() taxInformationChanged = new EventEmitter(); + + /** + * Emits when the tax information has been updated. + */ @Output() taxInformationUpdated = new EventEmitter(); + private taxInformation: TaxInformation; + protected formGroup = this.formBuilder.group({ country: ["", Validators.required], postalCode: ["", Validators.required], - includeTaxId: false, taxId: "", line1: "", line2: "", @@ -32,16 +39,20 @@ export class ManageTaxInformationComponent implements OnInit, OnDestroy { state: "", }); + protected isTaxSupported: boolean; + private destroy$ = new Subject(); - private taxInformation: TaxInformation; + protected readonly countries: CountryListItem[] = this.taxService.getCountries(); - constructor(private formBuilder: FormBuilder) {} + constructor( + private formBuilder: FormBuilder, + private taxService: TaxServiceAbstraction, + ) {} - getTaxInformation = (): TaxInformation & { includeTaxId: boolean } => ({ - ...this.taxInformation, - includeTaxId: this.formGroup.value.includeTaxId, - }); + getTaxInformation(): TaxInformation { + return this.taxInformation; + } submit = async () => { this.formGroup.markAllAsTouched(); @@ -52,23 +63,32 @@ export class ManageTaxInformationComponent implements OnInit, OnDestroy { this.taxInformationUpdated.emit(); }; - touch = (): boolean => { - this.formGroup.markAllAsTouched(); - return this.formGroup.valid; - }; + validate(): boolean { + if (this.formGroup.dirty) { + this.formGroup.markAllAsTouched(); + return this.formGroup.valid; + } else { + return this.formGroup.valid; + } + } async ngOnInit() { if (this.startWith) { - this.formGroup.patchValue({ - ...this.startWith, - includeTaxId: - this.countrySupportsTax(this.startWith.country) && - (!!this.startWith.taxId || - !!this.startWith.line1 || - !!this.startWith.line2 || - !!this.startWith.city || - !!this.startWith.state), - }); + this.formGroup.controls.country.setValue(this.startWith.country); + this.formGroup.controls.postalCode.setValue(this.startWith.postalCode); + + this.isTaxSupported = + this.startWith && this.startWith.country + ? await this.taxService.isCountrySupported(this.startWith.country) + : false; + + if (this.isTaxSupported) { + this.formGroup.controls.taxId.setValue(this.startWith.taxId); + this.formGroup.controls.line1.setValue(this.startWith.line1); + this.formGroup.controls.line2.setValue(this.startWith.line2); + this.formGroup.controls.city.setValue(this.startWith.city); + this.formGroup.controls.state.setValue(this.startWith.state); + } } this.formGroup.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((values) => { @@ -82,354 +102,47 @@ export class ManageTaxInformationComponent implements OnInit, OnDestroy { state: values.state, }; }); - } - ngOnDestroy() { - this.destroy$.next(); - this.destroy$.complete(); - } + this.formGroup.controls.country.valueChanges + .pipe(debounceTime(1000), takeUntil(this.destroy$)) + .subscribe((country: string) => { + this.taxService + .isCountrySupported(country) + .then((isSupported) => (this.isTaxSupported = isSupported)) + .catch(() => (this.isTaxSupported = false)) + .finally(() => { + if (!this.isTaxSupported) { + this.formGroup.controls.taxId.setValue(null); + this.formGroup.controls.line1.setValue(null); + this.formGroup.controls.line2.setValue(null); + this.formGroup.controls.city.setValue(null); + this.formGroup.controls.state.setValue(null); + } + if (this.taxInformationChanged) { + this.taxInformationChanged.emit(this.taxInformation); + } + }); + }); - protected countrySupportsTax(countryCode: string) { - return this.taxSupportedCountryCodes.includes(countryCode); - } + this.formGroup.controls.postalCode.valueChanges + .pipe(debounceTime(1000), takeUntil(this.destroy$)) + .subscribe(() => { + if (this.taxInformationChanged) { + this.taxInformationChanged.emit(this.taxInformation); + } + }); - protected get includeTaxIdIsSelected() { - return this.formGroup.value.includeTaxId; + this.formGroup.controls.taxId.valueChanges + .pipe(debounceTime(1000), takeUntil(this.destroy$)) + .subscribe(() => { + if (this.taxInformationChanged) { + this.taxInformationChanged.emit(this.taxInformation); + } + }); } - protected get selectionSupportsAdditionalOptions() { - return ( - this.formGroup.value.country !== "US" && this.countrySupportsTax(this.formGroup.value.country) - ); + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); } - - protected countries: Country[] = [ - { name: "-- Select --", value: "", disabled: false }, - { name: "United States", value: "US", disabled: false }, - { name: "China", value: "CN", disabled: false }, - { name: "France", value: "FR", disabled: false }, - { name: "Germany", value: "DE", disabled: false }, - { name: "Canada", value: "CA", disabled: false }, - { name: "United Kingdom", value: "GB", disabled: false }, - { name: "Australia", value: "AU", disabled: false }, - { name: "India", value: "IN", disabled: false }, - { name: "", value: "-", disabled: true }, - { name: "Afghanistan", value: "AF", disabled: false }, - { name: "Åland Islands", value: "AX", disabled: false }, - { name: "Albania", value: "AL", disabled: false }, - { name: "Algeria", value: "DZ", disabled: false }, - { name: "American Samoa", value: "AS", disabled: false }, - { name: "Andorra", value: "AD", disabled: false }, - { name: "Angola", value: "AO", disabled: false }, - { name: "Anguilla", value: "AI", disabled: false }, - { name: "Antarctica", value: "AQ", disabled: false }, - { name: "Antigua and Barbuda", value: "AG", disabled: false }, - { name: "Argentina", value: "AR", disabled: false }, - { name: "Armenia", value: "AM", disabled: false }, - { name: "Aruba", value: "AW", disabled: false }, - { name: "Austria", value: "AT", disabled: false }, - { name: "Azerbaijan", value: "AZ", disabled: false }, - { name: "Bahamas", value: "BS", disabled: false }, - { name: "Bahrain", value: "BH", disabled: false }, - { name: "Bangladesh", value: "BD", disabled: false }, - { name: "Barbados", value: "BB", disabled: false }, - { name: "Belarus", value: "BY", disabled: false }, - { name: "Belgium", value: "BE", disabled: false }, - { name: "Belize", value: "BZ", disabled: false }, - { name: "Benin", value: "BJ", disabled: false }, - { name: "Bermuda", value: "BM", disabled: false }, - { name: "Bhutan", value: "BT", disabled: false }, - { name: "Bolivia, Plurinational State of", value: "BO", disabled: false }, - { name: "Bonaire, Sint Eustatius and Saba", value: "BQ", disabled: false }, - { name: "Bosnia and Herzegovina", value: "BA", disabled: false }, - { name: "Botswana", value: "BW", disabled: false }, - { name: "Bouvet Island", value: "BV", disabled: false }, - { name: "Brazil", value: "BR", disabled: false }, - { name: "British Indian Ocean Territory", value: "IO", disabled: false }, - { name: "Brunei Darussalam", value: "BN", disabled: false }, - { name: "Bulgaria", value: "BG", disabled: false }, - { name: "Burkina Faso", value: "BF", disabled: false }, - { name: "Burundi", value: "BI", disabled: false }, - { name: "Cambodia", value: "KH", disabled: false }, - { name: "Cameroon", value: "CM", disabled: false }, - { name: "Cape Verde", value: "CV", disabled: false }, - { name: "Cayman Islands", value: "KY", disabled: false }, - { name: "Central African Republic", value: "CF", disabled: false }, - { name: "Chad", value: "TD", disabled: false }, - { name: "Chile", value: "CL", disabled: false }, - { name: "Christmas Island", value: "CX", disabled: false }, - { name: "Cocos (Keeling) Islands", value: "CC", disabled: false }, - { name: "Colombia", value: "CO", disabled: false }, - { name: "Comoros", value: "KM", disabled: false }, - { name: "Congo", value: "CG", disabled: false }, - { name: "Congo, the Democratic Republic of the", value: "CD", disabled: false }, - { name: "Cook Islands", value: "CK", disabled: false }, - { name: "Costa Rica", value: "CR", disabled: false }, - { name: "Côte d'Ivoire", value: "CI", disabled: false }, - { name: "Croatia", value: "HR", disabled: false }, - { name: "Cuba", value: "CU", disabled: false }, - { name: "Curaçao", value: "CW", disabled: false }, - { name: "Cyprus", value: "CY", disabled: false }, - { name: "Czech Republic", value: "CZ", disabled: false }, - { name: "Denmark", value: "DK", disabled: false }, - { name: "Djibouti", value: "DJ", disabled: false }, - { name: "Dominica", value: "DM", disabled: false }, - { name: "Dominican Republic", value: "DO", disabled: false }, - { name: "Ecuador", value: "EC", disabled: false }, - { name: "Egypt", value: "EG", disabled: false }, - { name: "El Salvador", value: "SV", disabled: false }, - { name: "Equatorial Guinea", value: "GQ", disabled: false }, - { name: "Eritrea", value: "ER", disabled: false }, - { name: "Estonia", value: "EE", disabled: false }, - { name: "Ethiopia", value: "ET", disabled: false }, - { name: "Falkland Islands (Malvinas)", value: "FK", disabled: false }, - { name: "Faroe Islands", value: "FO", disabled: false }, - { name: "Fiji", value: "FJ", disabled: false }, - { name: "Finland", value: "FI", disabled: false }, - { name: "French Guiana", value: "GF", disabled: false }, - { name: "French Polynesia", value: "PF", disabled: false }, - { name: "French Southern Territories", value: "TF", disabled: false }, - { name: "Gabon", value: "GA", disabled: false }, - { name: "Gambia", value: "GM", disabled: false }, - { name: "Georgia", value: "GE", disabled: false }, - { name: "Ghana", value: "GH", disabled: false }, - { name: "Gibraltar", value: "GI", disabled: false }, - { name: "Greece", value: "GR", disabled: false }, - { name: "Greenland", value: "GL", disabled: false }, - { name: "Grenada", value: "GD", disabled: false }, - { name: "Guadeloupe", value: "GP", disabled: false }, - { name: "Guam", value: "GU", disabled: false }, - { name: "Guatemala", value: "GT", disabled: false }, - { name: "Guernsey", value: "GG", disabled: false }, - { name: "Guinea", value: "GN", disabled: false }, - { name: "Guinea-Bissau", value: "GW", disabled: false }, - { name: "Guyana", value: "GY", disabled: false }, - { name: "Haiti", value: "HT", disabled: false }, - { name: "Heard Island and McDonald Islands", value: "HM", disabled: false }, - { name: "Holy See (Vatican City State)", value: "VA", disabled: false }, - { name: "Honduras", value: "HN", disabled: false }, - { name: "Hong Kong", value: "HK", disabled: false }, - { name: "Hungary", value: "HU", disabled: false }, - { name: "Iceland", value: "IS", disabled: false }, - { name: "Indonesia", value: "ID", disabled: false }, - { name: "Iran, Islamic Republic of", value: "IR", disabled: false }, - { name: "Iraq", value: "IQ", disabled: false }, - { name: "Ireland", value: "IE", disabled: false }, - { name: "Isle of Man", value: "IM", disabled: false }, - { name: "Israel", value: "IL", disabled: false }, - { name: "Italy", value: "IT", disabled: false }, - { name: "Jamaica", value: "JM", disabled: false }, - { name: "Japan", value: "JP", disabled: false }, - { name: "Jersey", value: "JE", disabled: false }, - { name: "Jordan", value: "JO", disabled: false }, - { name: "Kazakhstan", value: "KZ", disabled: false }, - { name: "Kenya", value: "KE", disabled: false }, - { name: "Kiribati", value: "KI", disabled: false }, - { name: "Korea, Democratic People's Republic of", value: "KP", disabled: false }, - { name: "Korea, Republic of", value: "KR", disabled: false }, - { name: "Kuwait", value: "KW", disabled: false }, - { name: "Kyrgyzstan", value: "KG", disabled: false }, - { name: "Lao People's Democratic Republic", value: "LA", disabled: false }, - { name: "Latvia", value: "LV", disabled: false }, - { name: "Lebanon", value: "LB", disabled: false }, - { name: "Lesotho", value: "LS", disabled: false }, - { name: "Liberia", value: "LR", disabled: false }, - { name: "Libya", value: "LY", disabled: false }, - { name: "Liechtenstein", value: "LI", disabled: false }, - { name: "Lithuania", value: "LT", disabled: false }, - { name: "Luxembourg", value: "LU", disabled: false }, - { name: "Macao", value: "MO", disabled: false }, - { name: "Macedonia, the former Yugoslav Republic of", value: "MK", disabled: false }, - { name: "Madagascar", value: "MG", disabled: false }, - { name: "Malawi", value: "MW", disabled: false }, - { name: "Malaysia", value: "MY", disabled: false }, - { name: "Maldives", value: "MV", disabled: false }, - { name: "Mali", value: "ML", disabled: false }, - { name: "Malta", value: "MT", disabled: false }, - { name: "Marshall Islands", value: "MH", disabled: false }, - { name: "Martinique", value: "MQ", disabled: false }, - { name: "Mauritania", value: "MR", disabled: false }, - { name: "Mauritius", value: "MU", disabled: false }, - { name: "Mayotte", value: "YT", disabled: false }, - { name: "Mexico", value: "MX", disabled: false }, - { name: "Micronesia, Federated States of", value: "FM", disabled: false }, - { name: "Moldova, Republic of", value: "MD", disabled: false }, - { name: "Monaco", value: "MC", disabled: false }, - { name: "Mongolia", value: "MN", disabled: false }, - { name: "Montenegro", value: "ME", disabled: false }, - { name: "Montserrat", value: "MS", disabled: false }, - { name: "Morocco", value: "MA", disabled: false }, - { name: "Mozambique", value: "MZ", disabled: false }, - { name: "Myanmar", value: "MM", disabled: false }, - { name: "Namibia", value: "NA", disabled: false }, - { name: "Nauru", value: "NR", disabled: false }, - { name: "Nepal", value: "NP", disabled: false }, - { name: "Netherlands", value: "NL", disabled: false }, - { name: "New Caledonia", value: "NC", disabled: false }, - { name: "New Zealand", value: "NZ", disabled: false }, - { name: "Nicaragua", value: "NI", disabled: false }, - { name: "Niger", value: "NE", disabled: false }, - { name: "Nigeria", value: "NG", disabled: false }, - { name: "Niue", value: "NU", disabled: false }, - { name: "Norfolk Island", value: "NF", disabled: false }, - { name: "Northern Mariana Islands", value: "MP", disabled: false }, - { name: "Norway", value: "NO", disabled: false }, - { name: "Oman", value: "OM", disabled: false }, - { name: "Pakistan", value: "PK", disabled: false }, - { name: "Palau", value: "PW", disabled: false }, - { name: "Palestinian Territory, Occupied", value: "PS", disabled: false }, - { name: "Panama", value: "PA", disabled: false }, - { name: "Papua New Guinea", value: "PG", disabled: false }, - { name: "Paraguay", value: "PY", disabled: false }, - { name: "Peru", value: "PE", disabled: false }, - { name: "Philippines", value: "PH", disabled: false }, - { name: "Pitcairn", value: "PN", disabled: false }, - { name: "Poland", value: "PL", disabled: false }, - { name: "Portugal", value: "PT", disabled: false }, - { name: "Puerto Rico", value: "PR", disabled: false }, - { name: "Qatar", value: "QA", disabled: false }, - { name: "Réunion", value: "RE", disabled: false }, - { name: "Romania", value: "RO", disabled: false }, - { name: "Russian Federation", value: "RU", disabled: false }, - { name: "Rwanda", value: "RW", disabled: false }, - { name: "Saint Barthélemy", value: "BL", disabled: false }, - { name: "Saint Helena, Ascension and Tristan da Cunha", value: "SH", disabled: false }, - { name: "Saint Kitts and Nevis", value: "KN", disabled: false }, - { name: "Saint Lucia", value: "LC", disabled: false }, - { name: "Saint Martin (French part)", value: "MF", disabled: false }, - { name: "Saint Pierre and Miquelon", value: "PM", disabled: false }, - { name: "Saint Vincent and the Grenadines", value: "VC", disabled: false }, - { name: "Samoa", value: "WS", disabled: false }, - { name: "San Marino", value: "SM", disabled: false }, - { name: "Sao Tome and Principe", value: "ST", disabled: false }, - { name: "Saudi Arabia", value: "SA", disabled: false }, - { name: "Senegal", value: "SN", disabled: false }, - { name: "Serbia", value: "RS", disabled: false }, - { name: "Seychelles", value: "SC", disabled: false }, - { name: "Sierra Leone", value: "SL", disabled: false }, - { name: "Singapore", value: "SG", disabled: false }, - { name: "Sint Maarten (Dutch part)", value: "SX", disabled: false }, - { name: "Slovakia", value: "SK", disabled: false }, - { name: "Slovenia", value: "SI", disabled: false }, - { name: "Solomon Islands", value: "SB", disabled: false }, - { name: "Somalia", value: "SO", disabled: false }, - { name: "South Africa", value: "ZA", disabled: false }, - { name: "South Georgia and the South Sandwich Islands", value: "GS", disabled: false }, - { name: "South Sudan", value: "SS", disabled: false }, - { name: "Spain", value: "ES", disabled: false }, - { name: "Sri Lanka", value: "LK", disabled: false }, - { name: "Sudan", value: "SD", disabled: false }, - { name: "Suriname", value: "SR", disabled: false }, - { name: "Svalbard and Jan Mayen", value: "SJ", disabled: false }, - { name: "Swaziland", value: "SZ", disabled: false }, - { name: "Sweden", value: "SE", disabled: false }, - { name: "Switzerland", value: "CH", disabled: false }, - { name: "Syrian Arab Republic", value: "SY", disabled: false }, - { name: "Taiwan", value: "TW", disabled: false }, - { name: "Tajikistan", value: "TJ", disabled: false }, - { name: "Tanzania, United Republic of", value: "TZ", disabled: false }, - { name: "Thailand", value: "TH", disabled: false }, - { name: "Timor-Leste", value: "TL", disabled: false }, - { name: "Togo", value: "TG", disabled: false }, - { name: "Tokelau", value: "TK", disabled: false }, - { name: "Tonga", value: "TO", disabled: false }, - { name: "Trinidad and Tobago", value: "TT", disabled: false }, - { name: "Tunisia", value: "TN", disabled: false }, - { name: "Turkey", value: "TR", disabled: false }, - { name: "Turkmenistan", value: "TM", disabled: false }, - { name: "Turks and Caicos Islands", value: "TC", disabled: false }, - { name: "Tuvalu", value: "TV", disabled: false }, - { name: "Uganda", value: "UG", disabled: false }, - { name: "Ukraine", value: "UA", disabled: false }, - { name: "United Arab Emirates", value: "AE", disabled: false }, - { name: "United States Minor Outlying Islands", value: "UM", disabled: false }, - { name: "Uruguay", value: "UY", disabled: false }, - { name: "Uzbekistan", value: "UZ", disabled: false }, - { name: "Vanuatu", value: "VU", disabled: false }, - { name: "Venezuela, Bolivarian Republic of", value: "VE", disabled: false }, - { name: "Viet Nam", value: "VN", disabled: false }, - { name: "Virgin Islands, British", value: "VG", disabled: false }, - { name: "Virgin Islands, U.S.", value: "VI", disabled: false }, - { name: "Wallis and Futuna", value: "WF", disabled: false }, - { name: "Western Sahara", value: "EH", disabled: false }, - { name: "Yemen", value: "YE", disabled: false }, - { name: "Zambia", value: "ZM", disabled: false }, - { name: "Zimbabwe", value: "ZW", disabled: false }, - ]; - - private taxSupportedCountryCodes: string[] = [ - "CN", - "FR", - "DE", - "CA", - "GB", - "AU", - "IN", - "AD", - "AR", - "AT", - "BE", - "BO", - "BR", - "BG", - "CL", - "CO", - "CR", - "HR", - "CY", - "CZ", - "DK", - "DO", - "EC", - "EG", - "SV", - "EE", - "FI", - "GE", - "GR", - "HK", - "HU", - "IS", - "ID", - "IQ", - "IE", - "IL", - "IT", - "JP", - "KE", - "KR", - "LV", - "LI", - "LT", - "LU", - "MY", - "MT", - "MX", - "NL", - "NZ", - "NO", - "PE", - "PH", - "PL", - "PT", - "RO", - "RU", - "SA", - "RS", - "SG", - "SK", - "SI", - "ZA", - "ES", - "SE", - "CH", - "TW", - "TH", - "TR", - "UA", - "AE", - "UY", - "VE", - "VN", - ]; } diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 688507099de..149d553696e 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -138,11 +138,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 { 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 { OrganizationBillingService } from "@bitwarden/common/billing/services/organization-billing.service"; +import { TaxService } from "@bitwarden/common/billing/services/tax.service"; import { AppIdService as AppIdServiceAbstraction } from "@bitwarden/common/platform/abstractions/app-id.service"; import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; import { BulkEncryptService } from "@bitwarden/common/platform/abstractions/bulk-encrypt.service"; @@ -1271,6 +1273,11 @@ const safeProviders: SafeProvider[] = [ useClass: BillingApiService, deps: [ApiServiceAbstraction, LogService, ToastService], }), + safeProvider({ + provide: TaxServiceAbstraction, + useClass: TaxService, + deps: [ApiServiceAbstraction], + }), safeProvider({ provide: BillingAccountProfileStateService, useClass: DefaultBillingAccountProfileStateService, diff --git a/libs/common/src/billing/abstractions/tax.service.abstraction.ts b/libs/common/src/billing/abstractions/tax.service.abstraction.ts new file mode 100644 index 00000000000..438d3f394e0 --- /dev/null +++ b/libs/common/src/billing/abstractions/tax.service.abstraction.ts @@ -0,0 +1,18 @@ +import { CountryListItem } from "@bitwarden/common/billing/models/domain"; +import { PreviewIndividualInvoiceRequest } from "@bitwarden/common/billing/models/request/preview-individual-invoice.request"; +import { PreviewOrganizationInvoiceRequest } from "@bitwarden/common/billing/models/request/preview-organization-invoice.request"; +import { PreviewInvoiceResponse } from "@bitwarden/common/billing/models/response/preview-invoice.response"; + +export abstract class TaxServiceAbstraction { + abstract getCountries(): CountryListItem[]; + + abstract isCountrySupported(country: string): Promise; + + abstract previewIndividualInvoice( + request: PreviewIndividualInvoiceRequest, + ): Promise; + + abstract previewOrganizationInvoice( + request: PreviewOrganizationInvoiceRequest, + ): Promise; +} diff --git a/libs/common/src/billing/models/domain/country-list-item.ts b/libs/common/src/billing/models/domain/country-list-item.ts new file mode 100644 index 00000000000..79abb96871c --- /dev/null +++ b/libs/common/src/billing/models/domain/country-list-item.ts @@ -0,0 +1,5 @@ +export type CountryListItem = { + name: string; + value: string; + disabled: boolean; +}; diff --git a/libs/common/src/billing/models/domain/index.ts b/libs/common/src/billing/models/domain/index.ts index 0f53c3e116c..057f6dc4e84 100644 --- a/libs/common/src/billing/models/domain/index.ts +++ b/libs/common/src/billing/models/domain/index.ts @@ -1,2 +1,3 @@ export * from "./bank-account"; +export * from "./country-list-item"; export * from "./tax-information"; diff --git a/libs/common/src/billing/models/request/expanded-tax-info-update.request.ts b/libs/common/src/billing/models/request/expanded-tax-info-update.request.ts index 838249e2d8c..784d2691629 100644 --- a/libs/common/src/billing/models/request/expanded-tax-info-update.request.ts +++ b/libs/common/src/billing/models/request/expanded-tax-info-update.request.ts @@ -12,6 +12,10 @@ export class ExpandedTaxInfoUpdateRequest extends TaxInfoUpdateRequest { state: string; static From(taxInformation: TaxInformation): ExpandedTaxInfoUpdateRequest { + if (!taxInformation) { + return null; + } + const request = new ExpandedTaxInfoUpdateRequest(); request.country = taxInformation.country; request.postalCode = taxInformation.postalCode; diff --git a/libs/common/src/billing/models/request/preview-individual-invoice.request.ts b/libs/common/src/billing/models/request/preview-individual-invoice.request.ts new file mode 100644 index 00000000000..f817398c629 --- /dev/null +++ b/libs/common/src/billing/models/request/preview-individual-invoice.request.ts @@ -0,0 +1,28 @@ +// @ts-strict-ignore +export class PreviewIndividualInvoiceRequest { + passwordManager: PasswordManager; + taxInformation: TaxInformation; + + constructor(passwordManager: PasswordManager, taxInformation: TaxInformation) { + this.passwordManager = passwordManager; + this.taxInformation = taxInformation; + } +} + +class PasswordManager { + additionalStorage: number; + + constructor(additionalStorage: number) { + this.additionalStorage = additionalStorage; + } +} + +class TaxInformation { + postalCode: string; + country: string; + + constructor(postalCode: string, country: string) { + this.postalCode = postalCode; + this.country = country; + } +} diff --git a/libs/common/src/billing/models/request/preview-organization-invoice.request.ts b/libs/common/src/billing/models/request/preview-organization-invoice.request.ts new file mode 100644 index 00000000000..365dff5c110 --- /dev/null +++ b/libs/common/src/billing/models/request/preview-organization-invoice.request.ts @@ -0,0 +1,54 @@ +import { PlanType } from "@bitwarden/common/billing/enums"; + +export class PreviewOrganizationInvoiceRequest { + organizationId?: string; + passwordManager: PasswordManager; + secretsManager?: SecretsManager; + taxInformation: TaxInformation; + + constructor( + passwordManager: PasswordManager, + taxInformation: TaxInformation, + organizationId?: string, + secretsManager?: SecretsManager, + ) { + this.organizationId = organizationId; + this.passwordManager = passwordManager; + this.secretsManager = secretsManager; + this.taxInformation = taxInformation; + } +} + +class PasswordManager { + plan: PlanType; + seats: number; + additionalStorage: number; + + constructor(plan: PlanType, seats: number, additionalStorage: number) { + this.plan = plan; + this.seats = seats; + this.additionalStorage = additionalStorage; + } +} + +class SecretsManager { + seats: number; + additionalMachineAccounts: number; + + constructor(seats: number, additionalMachineAccounts: number) { + this.seats = seats; + this.additionalMachineAccounts = additionalMachineAccounts; + } +} + +class TaxInformation { + postalCode: string; + country: string; + taxId: string; + + constructor(postalCode: string, country: string, taxId: string) { + this.postalCode = postalCode; + this.country = country; + this.taxId = taxId; + } +} diff --git a/libs/common/src/billing/models/response/preview-invoice.response.ts b/libs/common/src/billing/models/response/preview-invoice.response.ts new file mode 100644 index 00000000000..c822a569bb3 --- /dev/null +++ b/libs/common/src/billing/models/response/preview-invoice.response.ts @@ -0,0 +1,16 @@ +import { BaseResponse } from "@bitwarden/common/models/response/base.response"; + +export class PreviewInvoiceResponse extends BaseResponse { + effectiveTaxRate: number; + taxableBaseAmount: number; + taxAmount: number; + totalAmount: number; + + constructor(response: any) { + super(response); + this.effectiveTaxRate = this.getResponseProperty("EffectiveTaxRate"); + this.taxableBaseAmount = this.getResponseProperty("TaxableBaseAmount"); + this.taxAmount = this.getResponseProperty("TaxAmount"); + this.totalAmount = this.getResponseProperty("TotalAmount"); + } +} diff --git a/libs/common/src/billing/models/response/tax-id-types.response.ts b/libs/common/src/billing/models/response/tax-id-types.response.ts new file mode 100644 index 00000000000..0d5cce46c8c --- /dev/null +++ b/libs/common/src/billing/models/response/tax-id-types.response.ts @@ -0,0 +1,28 @@ +import { BaseResponse } from "@bitwarden/common/models/response/base.response"; + +export class TaxIdTypesResponse extends BaseResponse { + taxIdTypes: TaxIdTypeResponse[] = []; + + constructor(response: any) { + super(response); + const taxIdTypes = this.getResponseProperty("TaxIdTypes"); + if (taxIdTypes && taxIdTypes.length) { + this.taxIdTypes = taxIdTypes.map((t: any) => new TaxIdTypeResponse(t)); + } + } +} + +export class TaxIdTypeResponse extends BaseResponse { + code: string; + country: string; + description: string; + example: string; + + constructor(response: any) { + super(response); + this.code = this.getResponseProperty("Code"); + this.country = this.getResponseProperty("Country"); + this.description = this.getResponseProperty("Description"); + this.example = this.getResponseProperty("Example"); + } +} diff --git a/libs/common/src/billing/services/tax.service.ts b/libs/common/src/billing/services/tax.service.ts new file mode 100644 index 00000000000..45e57267ec0 --- /dev/null +++ b/libs/common/src/billing/services/tax.service.ts @@ -0,0 +1,303 @@ +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { TaxServiceAbstraction } from "@bitwarden/common/billing/abstractions/tax.service.abstraction"; +import { CountryListItem } from "@bitwarden/common/billing/models/domain"; +import { PreviewIndividualInvoiceRequest } from "@bitwarden/common/billing/models/request/preview-individual-invoice.request"; +import { PreviewOrganizationInvoiceRequest } from "@bitwarden/common/billing/models/request/preview-organization-invoice.request"; +import { PreviewInvoiceResponse } from "@bitwarden/common/billing/models/response/preview-invoice.response"; + +export class TaxService implements TaxServiceAbstraction { + constructor(private apiService: ApiService) {} + + getCountries(): CountryListItem[] { + return [ + { name: "-- Select --", value: "", disabled: false }, + { name: "United States", value: "US", disabled: false }, + { name: "China", value: "CN", disabled: false }, + { name: "France", value: "FR", disabled: false }, + { name: "Germany", value: "DE", disabled: false }, + { name: "Canada", value: "CA", disabled: false }, + { name: "United Kingdom", value: "GB", disabled: false }, + { name: "Australia", value: "AU", disabled: false }, + { name: "India", value: "IN", disabled: false }, + { name: "", value: "-", disabled: true }, + { name: "Afghanistan", value: "AF", disabled: false }, + { name: "Åland Islands", value: "AX", disabled: false }, + { name: "Albania", value: "AL", disabled: false }, + { name: "Algeria", value: "DZ", disabled: false }, + { name: "American Samoa", value: "AS", disabled: false }, + { name: "Andorra", value: "AD", disabled: false }, + { name: "Angola", value: "AO", disabled: false }, + { name: "Anguilla", value: "AI", disabled: false }, + { name: "Antarctica", value: "AQ", disabled: false }, + { name: "Antigua and Barbuda", value: "AG", disabled: false }, + { name: "Argentina", value: "AR", disabled: false }, + { name: "Armenia", value: "AM", disabled: false }, + { name: "Aruba", value: "AW", disabled: false }, + { name: "Austria", value: "AT", disabled: false }, + { name: "Azerbaijan", value: "AZ", disabled: false }, + { name: "Bahamas", value: "BS", disabled: false }, + { name: "Bahrain", value: "BH", disabled: false }, + { name: "Bangladesh", value: "BD", disabled: false }, + { name: "Barbados", value: "BB", disabled: false }, + { name: "Belarus", value: "BY", disabled: false }, + { name: "Belgium", value: "BE", disabled: false }, + { name: "Belize", value: "BZ", disabled: false }, + { name: "Benin", value: "BJ", disabled: false }, + { name: "Bermuda", value: "BM", disabled: false }, + { name: "Bhutan", value: "BT", disabled: false }, + { name: "Bolivia, Plurinational State of", value: "BO", disabled: false }, + { name: "Bonaire, Sint Eustatius and Saba", value: "BQ", disabled: false }, + { name: "Bosnia and Herzegovina", value: "BA", disabled: false }, + { name: "Botswana", value: "BW", disabled: false }, + { name: "Bouvet Island", value: "BV", disabled: false }, + { name: "Brazil", value: "BR", disabled: false }, + { name: "British Indian Ocean Territory", value: "IO", disabled: false }, + { name: "Brunei Darussalam", value: "BN", disabled: false }, + { name: "Bulgaria", value: "BG", disabled: false }, + { name: "Burkina Faso", value: "BF", disabled: false }, + { name: "Burundi", value: "BI", disabled: false }, + { name: "Cambodia", value: "KH", disabled: false }, + { name: "Cameroon", value: "CM", disabled: false }, + { name: "Cape Verde", value: "CV", disabled: false }, + { name: "Cayman Islands", value: "KY", disabled: false }, + { name: "Central African Republic", value: "CF", disabled: false }, + { name: "Chad", value: "TD", disabled: false }, + { name: "Chile", value: "CL", disabled: false }, + { name: "Christmas Island", value: "CX", disabled: false }, + { name: "Cocos (Keeling) Islands", value: "CC", disabled: false }, + { name: "Colombia", value: "CO", disabled: false }, + { name: "Comoros", value: "KM", disabled: false }, + { name: "Congo", value: "CG", disabled: false }, + { name: "Congo, the Democratic Republic of the", value: "CD", disabled: false }, + { name: "Cook Islands", value: "CK", disabled: false }, + { name: "Costa Rica", value: "CR", disabled: false }, + { name: "Côte d'Ivoire", value: "CI", disabled: false }, + { name: "Croatia", value: "HR", disabled: false }, + { name: "Cuba", value: "CU", disabled: false }, + { name: "Curaçao", value: "CW", disabled: false }, + { name: "Cyprus", value: "CY", disabled: false }, + { name: "Czech Republic", value: "CZ", disabled: false }, + { name: "Denmark", value: "DK", disabled: false }, + { name: "Djibouti", value: "DJ", disabled: false }, + { name: "Dominica", value: "DM", disabled: false }, + { name: "Dominican Republic", value: "DO", disabled: false }, + { name: "Ecuador", value: "EC", disabled: false }, + { name: "Egypt", value: "EG", disabled: false }, + { name: "El Salvador", value: "SV", disabled: false }, + { name: "Equatorial Guinea", value: "GQ", disabled: false }, + { name: "Eritrea", value: "ER", disabled: false }, + { name: "Estonia", value: "EE", disabled: false }, + { name: "Ethiopia", value: "ET", disabled: false }, + { name: "Falkland Islands (Malvinas)", value: "FK", disabled: false }, + { name: "Faroe Islands", value: "FO", disabled: false }, + { name: "Fiji", value: "FJ", disabled: false }, + { name: "Finland", value: "FI", disabled: false }, + { name: "French Guiana", value: "GF", disabled: false }, + { name: "French Polynesia", value: "PF", disabled: false }, + { name: "French Southern Territories", value: "TF", disabled: false }, + { name: "Gabon", value: "GA", disabled: false }, + { name: "Gambia", value: "GM", disabled: false }, + { name: "Georgia", value: "GE", disabled: false }, + { name: "Ghana", value: "GH", disabled: false }, + { name: "Gibraltar", value: "GI", disabled: false }, + { name: "Greece", value: "GR", disabled: false }, + { name: "Greenland", value: "GL", disabled: false }, + { name: "Grenada", value: "GD", disabled: false }, + { name: "Guadeloupe", value: "GP", disabled: false }, + { name: "Guam", value: "GU", disabled: false }, + { name: "Guatemala", value: "GT", disabled: false }, + { name: "Guernsey", value: "GG", disabled: false }, + { name: "Guinea", value: "GN", disabled: false }, + { name: "Guinea-Bissau", value: "GW", disabled: false }, + { name: "Guyana", value: "GY", disabled: false }, + { name: "Haiti", value: "HT", disabled: false }, + { name: "Heard Island and McDonald Islands", value: "HM", disabled: false }, + { name: "Holy See (Vatican City State)", value: "VA", disabled: false }, + { name: "Honduras", value: "HN", disabled: false }, + { name: "Hong Kong", value: "HK", disabled: false }, + { name: "Hungary", value: "HU", disabled: false }, + { name: "Iceland", value: "IS", disabled: false }, + { name: "Indonesia", value: "ID", disabled: false }, + { name: "Iran, Islamic Republic of", value: "IR", disabled: false }, + { name: "Iraq", value: "IQ", disabled: false }, + { name: "Ireland", value: "IE", disabled: false }, + { name: "Isle of Man", value: "IM", disabled: false }, + { name: "Israel", value: "IL", disabled: false }, + { name: "Italy", value: "IT", disabled: false }, + { name: "Jamaica", value: "JM", disabled: false }, + { name: "Japan", value: "JP", disabled: false }, + { name: "Jersey", value: "JE", disabled: false }, + { name: "Jordan", value: "JO", disabled: false }, + { name: "Kazakhstan", value: "KZ", disabled: false }, + { name: "Kenya", value: "KE", disabled: false }, + { name: "Kiribati", value: "KI", disabled: false }, + { name: "Korea, Democratic People's Republic of", value: "KP", disabled: false }, + { name: "Korea, Republic of", value: "KR", disabled: false }, + { name: "Kuwait", value: "KW", disabled: false }, + { name: "Kyrgyzstan", value: "KG", disabled: false }, + { name: "Lao People's Democratic Republic", value: "LA", disabled: false }, + { name: "Latvia", value: "LV", disabled: false }, + { name: "Lebanon", value: "LB", disabled: false }, + { name: "Lesotho", value: "LS", disabled: false }, + { name: "Liberia", value: "LR", disabled: false }, + { name: "Libya", value: "LY", disabled: false }, + { name: "Liechtenstein", value: "LI", disabled: false }, + { name: "Lithuania", value: "LT", disabled: false }, + { name: "Luxembourg", value: "LU", disabled: false }, + { name: "Macao", value: "MO", disabled: false }, + { name: "Macedonia, the former Yugoslav Republic of", value: "MK", disabled: false }, + { name: "Madagascar", value: "MG", disabled: false }, + { name: "Malawi", value: "MW", disabled: false }, + { name: "Malaysia", value: "MY", disabled: false }, + { name: "Maldives", value: "MV", disabled: false }, + { name: "Mali", value: "ML", disabled: false }, + { name: "Malta", value: "MT", disabled: false }, + { name: "Marshall Islands", value: "MH", disabled: false }, + { name: "Martinique", value: "MQ", disabled: false }, + { name: "Mauritania", value: "MR", disabled: false }, + { name: "Mauritius", value: "MU", disabled: false }, + { name: "Mayotte", value: "YT", disabled: false }, + { name: "Mexico", value: "MX", disabled: false }, + { name: "Micronesia, Federated States of", value: "FM", disabled: false }, + { name: "Moldova, Republic of", value: "MD", disabled: false }, + { name: "Monaco", value: "MC", disabled: false }, + { name: "Mongolia", value: "MN", disabled: false }, + { name: "Montenegro", value: "ME", disabled: false }, + { name: "Montserrat", value: "MS", disabled: false }, + { name: "Morocco", value: "MA", disabled: false }, + { name: "Mozambique", value: "MZ", disabled: false }, + { name: "Myanmar", value: "MM", disabled: false }, + { name: "Namibia", value: "NA", disabled: false }, + { name: "Nauru", value: "NR", disabled: false }, + { name: "Nepal", value: "NP", disabled: false }, + { name: "Netherlands", value: "NL", disabled: false }, + { name: "New Caledonia", value: "NC", disabled: false }, + { name: "New Zealand", value: "NZ", disabled: false }, + { name: "Nicaragua", value: "NI", disabled: false }, + { name: "Niger", value: "NE", disabled: false }, + { name: "Nigeria", value: "NG", disabled: false }, + { name: "Niue", value: "NU", disabled: false }, + { name: "Norfolk Island", value: "NF", disabled: false }, + { name: "Northern Mariana Islands", value: "MP", disabled: false }, + { name: "Norway", value: "NO", disabled: false }, + { name: "Oman", value: "OM", disabled: false }, + { name: "Pakistan", value: "PK", disabled: false }, + { name: "Palau", value: "PW", disabled: false }, + { name: "Palestinian Territory, Occupied", value: "PS", disabled: false }, + { name: "Panama", value: "PA", disabled: false }, + { name: "Papua New Guinea", value: "PG", disabled: false }, + { name: "Paraguay", value: "PY", disabled: false }, + { name: "Peru", value: "PE", disabled: false }, + { name: "Philippines", value: "PH", disabled: false }, + { name: "Pitcairn", value: "PN", disabled: false }, + { name: "Poland", value: "PL", disabled: false }, + { name: "Portugal", value: "PT", disabled: false }, + { name: "Puerto Rico", value: "PR", disabled: false }, + { name: "Qatar", value: "QA", disabled: false }, + { name: "Réunion", value: "RE", disabled: false }, + { name: "Romania", value: "RO", disabled: false }, + { name: "Russian Federation", value: "RU", disabled: false }, + { name: "Rwanda", value: "RW", disabled: false }, + { name: "Saint Barthélemy", value: "BL", disabled: false }, + { name: "Saint Helena, Ascension and Tristan da Cunha", value: "SH", disabled: false }, + { name: "Saint Kitts and Nevis", value: "KN", disabled: false }, + { name: "Saint Lucia", value: "LC", disabled: false }, + { name: "Saint Martin (French part)", value: "MF", disabled: false }, + { name: "Saint Pierre and Miquelon", value: "PM", disabled: false }, + { name: "Saint Vincent and the Grenadines", value: "VC", disabled: false }, + { name: "Samoa", value: "WS", disabled: false }, + { name: "San Marino", value: "SM", disabled: false }, + { name: "Sao Tome and Principe", value: "ST", disabled: false }, + { name: "Saudi Arabia", value: "SA", disabled: false }, + { name: "Senegal", value: "SN", disabled: false }, + { name: "Serbia", value: "RS", disabled: false }, + { name: "Seychelles", value: "SC", disabled: false }, + { name: "Sierra Leone", value: "SL", disabled: false }, + { name: "Singapore", value: "SG", disabled: false }, + { name: "Sint Maarten (Dutch part)", value: "SX", disabled: false }, + { name: "Slovakia", value: "SK", disabled: false }, + { name: "Slovenia", value: "SI", disabled: false }, + { name: "Solomon Islands", value: "SB", disabled: false }, + { name: "Somalia", value: "SO", disabled: false }, + { name: "South Africa", value: "ZA", disabled: false }, + { name: "South Georgia and the South Sandwich Islands", value: "GS", disabled: false }, + { name: "South Sudan", value: "SS", disabled: false }, + { name: "Spain", value: "ES", disabled: false }, + { name: "Sri Lanka", value: "LK", disabled: false }, + { name: "Sudan", value: "SD", disabled: false }, + { name: "Suriname", value: "SR", disabled: false }, + { name: "Svalbard and Jan Mayen", value: "SJ", disabled: false }, + { name: "Swaziland", value: "SZ", disabled: false }, + { name: "Sweden", value: "SE", disabled: false }, + { name: "Switzerland", value: "CH", disabled: false }, + { name: "Syrian Arab Republic", value: "SY", disabled: false }, + { name: "Taiwan", value: "TW", disabled: false }, + { name: "Tajikistan", value: "TJ", disabled: false }, + { name: "Tanzania, United Republic of", value: "TZ", disabled: false }, + { name: "Thailand", value: "TH", disabled: false }, + { name: "Timor-Leste", value: "TL", disabled: false }, + { name: "Togo", value: "TG", disabled: false }, + { name: "Tokelau", value: "TK", disabled: false }, + { name: "Tonga", value: "TO", disabled: false }, + { name: "Trinidad and Tobago", value: "TT", disabled: false }, + { name: "Tunisia", value: "TN", disabled: false }, + { name: "Turkey", value: "TR", disabled: false }, + { name: "Turkmenistan", value: "TM", disabled: false }, + { name: "Turks and Caicos Islands", value: "TC", disabled: false }, + { name: "Tuvalu", value: "TV", disabled: false }, + { name: "Uganda", value: "UG", disabled: false }, + { name: "Ukraine", value: "UA", disabled: false }, + { name: "United Arab Emirates", value: "AE", disabled: false }, + { name: "United States Minor Outlying Islands", value: "UM", disabled: false }, + { name: "Uruguay", value: "UY", disabled: false }, + { name: "Uzbekistan", value: "UZ", disabled: false }, + { name: "Vanuatu", value: "VU", disabled: false }, + { name: "Venezuela, Bolivarian Republic of", value: "VE", disabled: false }, + { name: "Viet Nam", value: "VN", disabled: false }, + { name: "Virgin Islands, British", value: "VG", disabled: false }, + { name: "Virgin Islands, U.S.", value: "VI", disabled: false }, + { name: "Wallis and Futuna", value: "WF", disabled: false }, + { name: "Western Sahara", value: "EH", disabled: false }, + { name: "Yemen", value: "YE", disabled: false }, + { name: "Zambia", value: "ZM", disabled: false }, + { name: "Zimbabwe", value: "ZW", disabled: false }, + ]; + } + + async isCountrySupported(country: string): Promise { + const response = await this.apiService.send( + "GET", + "/tax/is-country-supported?country=" + country, + null, + true, + true, + ); + return response; + } + + async previewIndividualInvoice( + request: PreviewIndividualInvoiceRequest, + ): Promise { + const response = await this.apiService.send( + "POST", + "/accounts/billing/preview-invoice", + request, + true, + true, + ); + return new PreviewInvoiceResponse(response); + } + + async previewOrganizationInvoice( + request: PreviewOrganizationInvoiceRequest, + ): Promise { + const response = await this.apiService.send( + "POST", + `/invoices/preview-organization`, + request, + true, + true, + ); + return new PreviewInvoiceResponse(response); + } +} From 5a3681655b04b811f67d0c3b0993b1676425a058 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 2 Jan 2025 14:28:57 -0500 Subject: [PATCH 61/75] [deps] Platform: Update Rust crate libc to v0.2.169 (#12131) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- apps/desktop/desktop_native/Cargo.lock | 4 ++-- apps/desktop/desktop_native/core/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 e77e66a697c..86c33d34843 100644 --- a/apps/desktop/desktop_native/Cargo.lock +++ b/apps/desktop/desktop_native/Cargo.lock @@ -1508,9 +1508,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.162" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libloading" diff --git a/apps/desktop/desktop_native/core/Cargo.toml b/apps/desktop/desktop_native/core/Cargo.toml index 64ed009ceb5..b4fff01e6b0 100644 --- a/apps/desktop/desktop_native/core/Cargo.toml +++ b/apps/desktop/desktop_native/core/Cargo.toml @@ -31,7 +31,7 @@ base64 = "=0.22.1" byteorder = "=1.5.0" cbc = { version = "=0.1.2", features = ["alloc"] } homedir = "=0.3.4" -libc = "=0.2.162" +libc = "=0.2.169" pin-project = "=1.1.7" dirs = "=5.0.1" futures = "=0.3.31" From 0ae10f0d9bd40470b8e3a0124f65e1122e412238 Mon Sep 17 00:00:00 2001 From: Addison Beck Date: Thu, 2 Jan 2025 14:46:31 -0500 Subject: [PATCH 62/75] fix(safari): make sure to return from bespoke reload clause (#12661) On https://github.com/bitwarden/clients/pull/12326 I refactored and extended a bit of special case logic for process reloads on Safari. This appears to have caused a regression, and I think it's because I didn't return from the function when Safari reloads. This makes it still call `chrome.runtime.reload()`, which makes Safari send an "install" event, which leads to our extension opening it's Getting Started page. This commit returns after the location reload in Safari. This is tricky to test locally as the issue does not appear with sideloaded apps. I'll test it with Testflight! --- apps/browser/src/platform/browser/browser-api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/browser/src/platform/browser/browser-api.ts b/apps/browser/src/platform/browser/browser-api.ts index dc71d50d4b1..1d8ff65c17d 100644 --- a/apps/browser/src/platform/browser/browser-api.ts +++ b/apps/browser/src/platform/browser/browser-api.ts @@ -458,7 +458,7 @@ export class BrowserApi { // and that prompts us to show a new tab, this apparently doesn't happen on sideloaded // extensions and only shows itself production scenarios. See: https://bitwarden.atlassian.net/browse/PM-12298 if (this.isSafariApi) { - self.location.reload(); + return self.location.reload(); } return chrome.runtime.reload(); } From af4311fa21128af1b2c11ae413db8cdab8d0a61f Mon Sep 17 00:00:00 2001 From: Vince Grassia <593223+vgrassia@users.noreply.github.com> Date: Thu, 2 Jan 2025 14:58:34 -0500 Subject: [PATCH 63/75] BRE-534 - Update workflow to parallelize extension builds (#12640) --- .github/workflows/build-browser.yml | 160 +++++++++++++++------------- 1 file changed, 83 insertions(+), 77 deletions(-) diff --git a/.github/workflows/build-browser.yml b/.github/workflows/build-browser.yml index 7740e418e7b..68d1f10a51a 100644 --- a/.github/workflows/build-browser.yml +++ b/.github/workflows/build-browser.yml @@ -114,8 +114,8 @@ jobs: fi - build: - name: Build + build-source: + name: Build browser source runs-on: ubuntu-22.04 needs: - setup @@ -127,7 +127,7 @@ jobs: - name: Check out repo uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: - ref: ${{ github.event.pull_request.head.sha }} + ref: ${{ github.event.pull_request.head.sha }} - name: Set up Node uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0 @@ -175,15 +175,85 @@ jobs: path: browser-source.zip if-no-files-found: error + + build: + name: Build + runs-on: ubuntu-22.04 + needs: + - setup + - locales-test + - build-source + env: + _BUILD_NUMBER: ${{ needs.setup.outputs.adj_build_number }} + _NODE_VERSION: ${{ needs.setup.outputs.node_version }} + strategy: + matrix: + include: + - name: "chrome" + npm_command: "dist:chrome" + archive_name: "dist-chrome.zip" + artifact_name: "dist-chrome-MV3" + - name: "edge" + npm_command: "dist:edge" + archive_name: "dist-edge.zip" + artifact_name: "dist-edge" + - name: "edge-mv3" + npm_command: "dist:edge:mv3" + archive_name: "dist-edge.zip" + artifact_name: "DO-NOT-USE-FOR-PROD-dist-edge-MV3" + - name: "firefox" + npm_command: "dist:firefox" + archive_name: "dist-firefox.zip" + artifact_name: "dist-firefox" + - name: "firefox-mv3" + npm_command: "dist:firefox:mv3" + archive_name: "dist-firefox.zip" + artifact_name: "DO-NOT-USE-FOR-PROD-dist-firefox-MV3" + - name: "opera" + npm_command: "dist:opera" + archive_name: "dist-opera.zip" + artifact_name: "dist-opera" + - name: "opera-mv3" + npm_command: "dist:opera:mv3" + archive_name: "dist-opera.zip" + artifact_name: "DO-NOT-USE-FOR-PROD-dist-opera-MV3" + steps: + - name: Check out repo + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ github.event.pull_request.head.sha }} + + - name: Set up Node + uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0 + with: + cache: 'npm' + cache-dependency-path: '**/package-lock.json' + node-version: ${{ env._NODE_VERSION }} + + - name: Print environment + run: | + node --version + npm --version + + - name: Download browser source + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 + with: + name: browser-source-${{ env._BUILD_NUMBER }}.zip + + - name: Unzip browser source artifact + run: | + unzip browser-source.zip + rm browser-source.zip + - name: NPM setup run: npm ci working-directory: browser-source/ - - name: Download SDK Artifacts + - name: Download SDK artifacts if: ${{ inputs.sdk_branch != '' }} uses: bitwarden/gh-actions/download-artifacts@main with: - github_token: ${{secrets.GITHUB_TOKEN}} + github_token: ${{ secrets.GITHUB_TOKEN }} workflow: build-wasm-internal.yml workflow_conclusion: success branch: ${{ inputs.sdk_branch }} @@ -195,85 +265,19 @@ jobs: - name: Override SDK if: ${{ inputs.sdk_branch != '' }} working-directory: browser-source/ - run: | - npm link ../sdk-internal - - - name: Build Chrome - run: npm run dist:chrome - working-directory: browser-source/apps/browser - - - name: Upload Chrome MV3 artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 - with: - name: dist-chrome-MV3-${{ env._BUILD_NUMBER }}.zip - path: browser-source/apps/browser/dist/dist-chrome.zip - if-no-files-found: error - - - name: Build Edge - run: npm run dist:edge - working-directory: browser-source/apps/browser - - - name: Upload Edge artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 - with: - name: dist-edge-${{ env._BUILD_NUMBER }}.zip - path: browser-source/apps/browser/dist/dist-edge.zip - if-no-files-found: error - - - name: Build Edge (MV3) - run: npm run dist:edge:mv3 - working-directory: browser-source/apps/browser - - - name: Upload Edge MV3 artifact (DO NOT USE FOR PROD) - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 - with: - name: DO-NOT-USE-FOR-PROD-dist-edge-MV3-${{ env._BUILD_NUMBER }}.zip - path: browser-source/apps/browser/dist/dist-edge.zip - if-no-files-found: error - - - name: Build Firefox - run: npm run dist:firefox - working-directory: browser-source/apps/browser - - - name: Upload Firefox artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 - with: - name: dist-firefox-${{ env._BUILD_NUMBER }}.zip - path: browser-source/apps/browser/dist/dist-firefox.zip - if-no-files-found: error + run: npm link ../sdk-internal - - name: Build Firefox (MV3) - run: npm run dist:firefox:mv3 + - name: Build extension + run: npm run ${{ matrix.npm_command }} working-directory: browser-source/apps/browser - - name: Upload Firefox MV3 artifact (DO NOT USE FOR PROD) + - name: Upload extension artifact uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: - name: DO-NOT-USE-FOR-PROD-dist-firefox-MV3-${{ env._BUILD_NUMBER }}.zip - path: browser-source/apps/browser/dist/dist-firefox.zip + name: ${{ matrix.artifact_name }}-${{ env._BUILD_NUMBER }}.zip + path: browser-source/apps/browser/dist/${{ matrix.archive_name }} if-no-files-found: error - - name: Build Opera - run: npm run dist:opera - working-directory: browser-source/apps/browser - - - name: Upload Opera artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 - with: - name: dist-opera-${{ env._BUILD_NUMBER }}.zip - path: browser-source/apps/browser/dist/dist-opera.zip - if-no-files-found: error - - - name: Build Opera (MV3) - run: npm run dist:opera:mv3 - working-directory: browser-source/apps/browser - - - name: Upload Opera MV3 artifact (DO NOT USE FOR PROD) - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 - with: - name: DO-NOT-USE-FOR-PROD-dist-opera-MV3-${{ env._BUILD_NUMBER }}.zip - path: browser-source/apps/browser/dist/dist-opera.zip - if-no-files-found: error build-safari: name: Build Safari @@ -448,6 +452,7 @@ jobs: upload_sources: true upload_translations: false + check-failures: name: Check for failures if: always() @@ -455,6 +460,7 @@ jobs: needs: - setup - locales-test + - build-source - build - build-safari - crowdin-push From 15cc4ff1eb936a6da5ba4a7470f6fa07104970bc Mon Sep 17 00:00:00 2001 From: Alex Morask <144709477+amorask-bitwarden@users.noreply.github.com> Date: Thu, 2 Jan 2025 14:37:09 -0600 Subject: [PATCH 64/75] [PM-14461] Update organization state after subscription update (#12222) * Update organization state after subscription update * QA: Fix SM trial seat adjustment --- .../adjust-subscription.component.ts | 18 +++++++++++++++++- .../sm-adjust-subscription.component.ts | 14 +++++++++++++- .../organization-api.service.abstraction.ts | 4 ++-- .../organization/organization-api.service.ts | 14 ++++++++------ 4 files changed, 40 insertions(+), 10 deletions(-) diff --git a/apps/web/src/app/billing/organizations/adjust-subscription.component.ts b/apps/web/src/app/billing/organizations/adjust-subscription.component.ts index 73dbb0a0026..0166560007e 100644 --- a/apps/web/src/app/billing/organizations/adjust-subscription.component.ts +++ b/apps/web/src/app/billing/organizations/adjust-subscription.component.ts @@ -5,6 +5,8 @@ import { FormBuilder, Validators } from "@angular/forms"; import { Subject, takeUntil } from "rxjs"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; +import { InternalOrganizationServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { OrganizationData } from "@bitwarden/common/admin-console/models/data/organization.data"; import { OrganizationSubscriptionUpdateRequest } from "@bitwarden/common/billing/models/request/organization-subscription-update.request"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { ToastService } from "@bitwarden/components"; @@ -34,6 +36,7 @@ export class AdjustSubscription implements OnInit, OnDestroy { private organizationApiService: OrganizationApiServiceAbstraction, private formBuilder: FormBuilder, private toastService: ToastService, + private internalOrganizationService: InternalOrganizationServiceAbstraction, ) {} ngOnInit() { @@ -64,7 +67,20 @@ export class AdjustSubscription implements OnInit, OnDestroy { this.additionalSeatCount, this.adjustSubscriptionForm.value.newMaxSeats, ); - await this.organizationApiService.updatePasswordManagerSeats(this.organizationId, request); + + const response = await this.organizationApiService.updatePasswordManagerSeats( + this.organizationId, + request, + ); + + const organization = await this.internalOrganizationService.get(this.organizationId); + + const organizationData = new OrganizationData(response, { + isMember: organization.isMember, + isProviderUser: organization.isProviderUser, + }); + + await this.internalOrganizationService.upsert(organizationData); this.toastService.showToast({ variant: "success", diff --git a/apps/web/src/app/billing/organizations/sm-adjust-subscription.component.ts b/apps/web/src/app/billing/organizations/sm-adjust-subscription.component.ts index 4a4f309c68b..fc7a188f967 100644 --- a/apps/web/src/app/billing/organizations/sm-adjust-subscription.component.ts +++ b/apps/web/src/app/billing/organizations/sm-adjust-subscription.component.ts @@ -5,6 +5,8 @@ import { FormBuilder, Validators } from "@angular/forms"; import { Subject, takeUntil } from "rxjs"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; +import { InternalOrganizationServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { OrganizationData } from "@bitwarden/common/admin-console/models/data/organization.data"; import { OrganizationSmSubscriptionUpdateRequest } from "@bitwarden/common/billing/models/request/organization-sm-subscription-update.request"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; @@ -104,6 +106,7 @@ export class SecretsManagerAdjustSubscriptionComponent implements OnInit, OnDest private i18nService: I18nService, private platformUtilsService: PlatformUtilsService, private toastService: ToastService, + private internalOrganizationService: InternalOrganizationServiceAbstraction, ) {} ngOnInit() { @@ -157,11 +160,20 @@ export class SecretsManagerAdjustSubscriptionComponent implements OnInit, OnDest ? this.formGroup.value.maxAutoscaleServiceAccounts : null; - await this.organizationApiService.updateSecretsManagerSubscription( + const response = await this.organizationApiService.updateSecretsManagerSubscription( this.organizationId, request, ); + const organization = await this.internalOrganizationService.get(this.organizationId); + + const organizationData = new OrganizationData(response, { + isMember: organization.isMember, + isProviderUser: organization.isProviderUser, + }); + + await this.internalOrganizationService.upsert(organizationData); + this.toastService.showToast({ variant: "success", title: null, diff --git a/libs/common/src/admin-console/abstractions/organization/organization-api.service.abstraction.ts b/libs/common/src/admin-console/abstractions/organization/organization-api.service.abstraction.ts index 9fd3b358568..0c45c919e95 100644 --- a/libs/common/src/admin-console/abstractions/organization/organization-api.service.abstraction.ts +++ b/libs/common/src/admin-console/abstractions/organization/organization-api.service.abstraction.ts @@ -53,11 +53,11 @@ export class OrganizationApiServiceAbstraction { updatePasswordManagerSeats: ( id: string, request: OrganizationSubscriptionUpdateRequest, - ) => Promise; + ) => Promise; updateSecretsManagerSubscription: ( id: string, request: OrganizationSmSubscriptionUpdateRequest, - ) => Promise; + ) => Promise; updateSeats: (id: string, request: SeatRequest) => Promise; updateStorage: (id: string, request: StorageRequest) => Promise; verifyBank: (id: string, request: VerifyBankRequest) => Promise; diff --git a/libs/common/src/admin-console/services/organization/organization-api.service.ts b/libs/common/src/admin-console/services/organization/organization-api.service.ts index d62fd49a6a4..b3fd11982b8 100644 --- a/libs/common/src/admin-console/services/organization/organization-api.service.ts +++ b/libs/common/src/admin-console/services/organization/organization-api.service.ts @@ -161,27 +161,29 @@ export class OrganizationApiService implements OrganizationApiServiceAbstraction async updatePasswordManagerSeats( id: string, request: OrganizationSubscriptionUpdateRequest, - ): Promise { - return this.apiService.send( + ): Promise { + const r = await this.apiService.send( "POST", "/organizations/" + id + "/subscription", request, true, - false, + true, ); + return new ProfileOrganizationResponse(r); } async updateSecretsManagerSubscription( id: string, request: OrganizationSmSubscriptionUpdateRequest, - ): Promise { - return this.apiService.send( + ): Promise { + const r = await this.apiService.send( "POST", "/organizations/" + id + "/sm-subscription", request, true, - false, + true, ); + return new ProfileOrganizationResponse(r); } async updateSeats(id: string, request: SeatRequest): Promise { From cf9bc7c4555ffe891335774e9d42d19f1c00388d Mon Sep 17 00:00:00 2001 From: Nick Krantz <125900171+nick-livefront@users.noreply.github.com> Date: Thu, 2 Jan 2025 15:37:48 -0600 Subject: [PATCH 65/75] Vault Strict Typing cleanup (#12512) * remove strict types from `NewDeviceVerificationNotice` - Add null default value for class properties - Enforce that the userId is passed - noticeState$ can return null * remove strict types from `CopyCipherFieldService` - refactor title to be a string rather than null * remove strict types from `PasswordRepromptComponent` - add guard to exit early on submit but also solves removing null/undefined from typing * use bang to ensure required input * remove strict types from `CopyCipherFieldDirective` - add bang for required types - add default values for null types * add bang for constant variables in cipher form stories * remove strict types from `DeleteAttachmentComponent` - add bang for required types - refactor title to be an empty string * fix tests --- libs/vault/src/cipher-form/cipher-form.stories.ts | 10 ++++------ .../delete-attachment.component.spec.ts | 2 +- .../delete-attachment/delete-attachment.component.ts | 8 +++----- .../src/components/copy-cipher-field.directive.ts | 10 ++++------ libs/vault/src/components/org-icon.directive.ts | 4 +--- .../src/components/password-reprompt.component.ts | 8 ++++++-- .../src/services/copy-cipher-field.service.spec.ts | 2 +- libs/vault/src/services/copy-cipher-field.service.ts | 4 +--- .../services/new-device-verification-notice.service.ts | 10 ++++------ 9 files changed, 25 insertions(+), 33 deletions(-) diff --git a/libs/vault/src/cipher-form/cipher-form.stories.ts b/libs/vault/src/cipher-form/cipher-form.stories.ts index 98ccdec360e..1d44e4542bc 100644 --- a/libs/vault/src/cipher-form/cipher-form.stories.ts +++ b/libs/vault/src/cipher-form/cipher-form.stories.ts @@ -1,5 +1,3 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { importProvidersFrom } from "@angular/core"; import { action } from "@storybook/addon-actions"; import { @@ -234,7 +232,7 @@ export const Edit: Story = { config: { ...defaultConfig, mode: "edit", - originalCipher: defaultConfig.originalCipher, + originalCipher: defaultConfig.originalCipher!, }, }, }; @@ -245,7 +243,7 @@ export const PartialEdit: Story = { config: { ...defaultConfig, mode: "partial-edit", - originalCipher: defaultConfig.originalCipher, + originalCipher: defaultConfig.originalCipher!, }, }, }; @@ -256,7 +254,7 @@ export const Clone: Story = { config: { ...defaultConfig, mode: "clone", - originalCipher: defaultConfig.originalCipher, + originalCipher: defaultConfig.originalCipher!, }, }, }; @@ -269,7 +267,7 @@ export const NoPersonalOwnership: Story = { mode: "add", allowPersonalOwnership: false, originalCipher: defaultConfig.originalCipher, - organizations: defaultConfig.organizations, + organizations: defaultConfig.organizations!, }, }, }; diff --git a/libs/vault/src/cipher-form/components/attachments/delete-attachment/delete-attachment.component.spec.ts b/libs/vault/src/cipher-form/components/attachments/delete-attachment/delete-attachment.component.spec.ts index 749093902d7..8e0d4f7a665 100644 --- a/libs/vault/src/cipher-form/components/attachments/delete-attachment/delete-attachment.component.spec.ts +++ b/libs/vault/src/cipher-form/components/attachments/delete-attachment/delete-attachment.component.spec.ts @@ -98,7 +98,7 @@ describe("DeleteAttachmentComponent", () => { expect(showToast).toHaveBeenCalledWith({ variant: "success", - title: null, + title: "", message: "deletedAttachment", }); }); diff --git a/libs/vault/src/cipher-form/components/attachments/delete-attachment/delete-attachment.component.ts b/libs/vault/src/cipher-form/components/attachments/delete-attachment/delete-attachment.component.ts index d5a7039ad70..b1ada907b1d 100644 --- a/libs/vault/src/cipher-form/components/attachments/delete-attachment/delete-attachment.component.ts +++ b/libs/vault/src/cipher-form/components/attachments/delete-attachment/delete-attachment.component.ts @@ -1,5 +1,3 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { CommonModule } from "@angular/common"; import { Component, EventEmitter, Input, Output } from "@angular/core"; @@ -24,10 +22,10 @@ import { }) export class DeleteAttachmentComponent { /** Id of the cipher associated with the attachment */ - @Input({ required: true }) cipherId: string; + @Input({ required: true }) cipherId!: string; /** The attachment that is can be deleted */ - @Input({ required: true }) attachment: AttachmentView; + @Input({ required: true }) attachment!: AttachmentView; /** Emits when the attachment is successfully deleted */ @Output() onDeletionSuccess = new EventEmitter(); @@ -56,7 +54,7 @@ export class DeleteAttachmentComponent { this.toastService.showToast({ variant: "success", - title: null, + title: "", message: this.i18nService.t("deletedAttachment"), }); diff --git a/libs/vault/src/components/copy-cipher-field.directive.ts b/libs/vault/src/components/copy-cipher-field.directive.ts index 19bf25a2e2e..1eb96a30449 100644 --- a/libs/vault/src/components/copy-cipher-field.directive.ts +++ b/libs/vault/src/components/copy-cipher-field.directive.ts @@ -1,5 +1,3 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { Directive, HostBinding, HostListener, Input, OnChanges, Optional } from "@angular/core"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; @@ -28,9 +26,9 @@ export class CopyCipherFieldDirective implements OnChanges { alias: "appCopyField", required: true, }) - action: Exclude; + action!: Exclude; - @Input({ required: true }) cipher: CipherView; + @Input({ required: true }) cipher!: CipherView; constructor( private copyCipherFieldService: CopyCipherFieldService, @@ -52,7 +50,7 @@ export class CopyCipherFieldDirective implements OnChanges { @HostListener("click") async copy() { const value = this.getValueToCopy(); - await this.copyCipherFieldService.copy(value, this.action, this.cipher); + await this.copyCipherFieldService.copy(value ?? "", this.action, this.cipher); } async ngOnChanges() { @@ -69,7 +67,7 @@ export class CopyCipherFieldDirective implements OnChanges { // 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; + this.menuItemDirective.disabled = this.disabled ?? false; } } diff --git a/libs/vault/src/components/org-icon.directive.ts b/libs/vault/src/components/org-icon.directive.ts index bcf42503760..69a19b46c65 100644 --- a/libs/vault/src/components/org-icon.directive.ts +++ b/libs/vault/src/components/org-icon.directive.ts @@ -1,5 +1,3 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { Directive, ElementRef, HostBinding, Input, Renderer2 } from "@angular/core"; import { ProductTierType } from "@bitwarden/common/billing/enums"; @@ -11,7 +9,7 @@ export type OrgIconSize = "default" | "small" | "large"; selector: "[appOrgIcon]", }) export class OrgIconDirective { - @Input({ required: true }) tierType: ProductTierType; + @Input({ required: true }) tierType!: ProductTierType; @Input() size?: OrgIconSize = "default"; constructor( diff --git a/libs/vault/src/components/password-reprompt.component.ts b/libs/vault/src/components/password-reprompt.component.ts index acedcd79b81..abefac58747 100644 --- a/libs/vault/src/components/password-reprompt.component.ts +++ b/libs/vault/src/components/password-reprompt.component.ts @@ -1,5 +1,3 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { DialogRef } from "@angular/cdk/dialog"; import { Component } from "@angular/core"; import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms"; @@ -51,6 +49,12 @@ export class PasswordRepromptComponent { ) {} submit = async () => { + // Exit early when a master password is not provided. + // The form field required error will be shown to users in these cases. + if (!this.formGroup.value.masterPassword) { + return; + } + const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.id))); if (userId == null) { diff --git a/libs/vault/src/services/copy-cipher-field.service.spec.ts b/libs/vault/src/services/copy-cipher-field.service.spec.ts index fa148b0e2e3..48510b2efd9 100644 --- a/libs/vault/src/services/copy-cipher-field.service.spec.ts +++ b/libs/vault/src/services/copy-cipher-field.service.spec.ts @@ -76,7 +76,7 @@ describe("CopyCipherFieldService", () => { expect(toastService.showToast).toHaveBeenCalledWith({ variant: "success", message: "Username copied", - title: null, + title: "", }); expect(i18nService.t).toHaveBeenCalledWith("username"); expect(i18nService.t).toHaveBeenCalledWith("valueCopied", "Username"); diff --git a/libs/vault/src/services/copy-cipher-field.service.ts b/libs/vault/src/services/copy-cipher-field.service.ts index 1b3bb6c0a8c..bfcf3495865 100644 --- a/libs/vault/src/services/copy-cipher-field.service.ts +++ b/libs/vault/src/services/copy-cipher-field.service.ts @@ -1,5 +1,3 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { Injectable } from "@angular/core"; import { firstValueFrom } from "rxjs"; @@ -131,7 +129,7 @@ export class CopyCipherFieldService { this.toastService.showToast({ variant: "success", message: this.i18nService.t("valueCopied", this.i18nService.t(action.typeI18nKey)), - title: null, + title: "", }); if (action.event !== undefined) { diff --git a/libs/vault/src/services/new-device-verification-notice.service.ts b/libs/vault/src/services/new-device-verification-notice.service.ts index bb096ff0c2c..a925cf09ec3 100644 --- a/libs/vault/src/services/new-device-verification-notice.service.ts +++ b/libs/vault/src/services/new-device-verification-notice.service.ts @@ -1,5 +1,3 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { Injectable } from "@angular/core"; import { Observable } from "rxjs"; import { Jsonify } from "type-fest"; @@ -17,8 +15,8 @@ import { UserId } from "@bitwarden/common/types/guid"; // If a user dismisses the notice, use "last_dismissal" to wait 7 days before re-prompting // permanent_dismissal will be checked if the user should never see the notice again export class NewDeviceVerificationNotice { - last_dismissal: Date; - permanent_dismissal: boolean; + last_dismissal: Date | null = null; + permanent_dismissal: boolean | null = null; constructor(obj: Partial) { if (obj == null) { @@ -52,12 +50,12 @@ export class NewDeviceVerificationNoticeService { return this.stateProvider.getUser(userId, NEW_DEVICE_VERIFICATION_NOTICE_KEY); } - noticeState$(userId: UserId): Observable { + noticeState$(userId: UserId): Observable { return this.noticeState(userId).state$; } async updateNewDeviceVerificationNoticeState( - userId: UserId | null, + userId: UserId, newState: NewDeviceVerificationNotice, ): Promise { await this.noticeState(userId).update(() => { From b9660194be665eb42d96252840148f89c232c056 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 2 Jan 2025 16:38:29 -0500 Subject: [PATCH 66/75] [deps] Platform: Update tsconfig-paths-webpack-plugin to v4.2.0 (#12136) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package-lock.json | 9 +++++---- package.json | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8ab4e6f1cab..197b7cb0093 100644 --- a/package-lock.json +++ b/package-lock.json @@ -171,7 +171,7 @@ "tailwindcss": "3.4.17", "ts-jest": "29.2.2", "ts-loader": "9.5.1", - "tsconfig-paths-webpack-plugin": "4.1.0", + "tsconfig-paths-webpack-plugin": "4.2.0", "type-fest": "2.19.0", "typescript": "5.4.2", "typescript-strict-plugin": "^2.4.4", @@ -30565,14 +30565,15 @@ } }, "node_modules/tsconfig-paths-webpack-plugin": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.1.0.tgz", - "integrity": "sha512-xWFISjviPydmtmgeUAuXp4N1fky+VCtfhOkDUFIv5ea7p4wuTomI4QTrXvFBX2S4jZsmyTSrStQl+E+4w+RzxA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.2.0.tgz", + "integrity": "sha512-zbem3rfRS8BgeNK50Zz5SIQgXzLafiHjOwUAvk/38/o1jHn/V5QAgVUcz884or7WYcPaH3N2CIfUc2u0ul7UcA==", "dev": true, "license": "MIT", "dependencies": { "chalk": "^4.1.0", "enhanced-resolve": "^5.7.0", + "tapable": "^2.2.1", "tsconfig-paths": "^4.1.2" }, "engines": { diff --git a/package.json b/package.json index 513c96c0a2e..907723c3a02 100644 --- a/package.json +++ b/package.json @@ -132,7 +132,7 @@ "tailwindcss": "3.4.17", "ts-jest": "29.2.2", "ts-loader": "9.5.1", - "tsconfig-paths-webpack-plugin": "4.1.0", + "tsconfig-paths-webpack-plugin": "4.2.0", "type-fest": "2.19.0", "typescript": "5.4.2", "typescript-strict-plugin": "^2.4.4", From 10c8a2101a7b973d9c92e509fe33cfd87c9f34d5 Mon Sep 17 00:00:00 2001 From: SmithThe4th Date: Thu, 2 Jan 2025 17:16:33 -0500 Subject: [PATCH 67/75] [PM-12049] Remove usage of ActiveUserState from folder service (#11880) * Migrated folder service from using active user state to single user state Added extra test cases for encrypted folder and decrypted folders Updated derived state to use decrypt with key * Update callers in the web * Update callers in the browser * Update callers in libs * Update callers in cli * Fixed test * Fixed folder state test * Fixed test * removed duplicate activeUserId * Added takewhile operator to only make calls when userId is present * Simplified to accept a single user id instead of an observable * Required userid to be passed from notification service * [PM-15635] Folders not working on desktop (#12333) * Added folders memory state definition * added decrypted folders state * Refactored service to remove derived state * removed combinedstate and added clear decrypted folders to methods * Fixed test * Fixed issue with editing folder on the desktop app * Fixed test * Changed state name * fixed ts strict issue * fixed ts strict issue * fixed ts strict issue * removed unnecessasry null encrypteed folder check * Handle null folderdata * [PM-16197] "Items with No Folder" shows as a folder to edit name and delete (#12470) * Force redcryption anytime encryption state changes * Fixed text file * revert changes * create new object with nofolder instead of modifying exisiting object * Fixed failing test * switched to use memory-large-object * Fixed ts sctrict issue --------- Co-authored-by: Matt Bishop Co-authored-by: bnagawiecki <107435978+bnagawiecki@users.noreply.github.com> --- .../notification.background.spec.ts | 15 +- .../background/notification.background.ts | 22 +- .../sync/foreground-sync.service.spec.ts | 5 +- .../add-edit-folder-dialog.component.spec.ts | 2 +- .../add-edit-folder-dialog.component.ts | 12 +- .../vault-popup-list-filters.service.spec.ts | 11 +- .../vault-popup-list-filters.service.ts | 105 +++++----- .../settings/folders-v2.component.spec.ts | 6 +- .../popup/settings/folders-v2.component.ts | 11 +- .../vault/popup/settings/folders.component.ts | 15 +- .../vault/services/vault-filter.service.ts | 3 +- apps/cli/src/commands/edit.command.ts | 20 +- apps/cli/src/commands/get.command.ts | 15 +- apps/cli/src/commands/list.command.ts | 9 +- apps/cli/src/oss-serve-configurator.ts | 2 + apps/cli/src/vault.program.ts | 2 + apps/cli/src/vault/create.command.ts | 18 +- apps/cli/src/vault/delete.command.ts | 11 +- .../emergency-view-dialog.component.spec.ts | 7 + .../migrate-legacy-encryption.component.ts | 2 +- .../bulk-move-dialog.component.ts | 9 +- .../folder-add-edit.component.ts | 6 +- .../services/vault-filter.service.spec.ts | 3 +- .../services/vault-filter.service.ts | 14 +- .../individual-vault/view.component.spec.ts | 4 + .../vault-filter/vault-filter.service.ts | 3 + .../vault/components/add-edit.component.ts | 8 +- .../components/folder-add-edit.component.ts | 19 +- .../src/vault/components/view.component.ts | 8 +- .../services/vault-filter.service.ts | 12 +- .../src/platform/state/state-definitions.ts | 3 + .../src/platform/sync/core-sync.service.ts | 24 ++- libs/common/src/platform/sync/sync.service.ts | 3 +- .../src/services/notifications.service.ts | 6 +- .../vault-timeout.service.spec.ts | 2 +- .../vault-timeout/vault-timeout.service.ts | 2 +- .../folder/folder-api.service.abstraction.ts | 8 +- .../folder/folder.service.abstraction.ts | 28 +-- .../services/folder/folder-api.service.ts | 14 +- .../services/folder/folder.service.spec.ts | 157 +++++++------- .../vault/services/folder/folder.service.ts | 193 +++++++++++++----- .../services/key-state/folder.state.spec.ts | 64 ++---- .../vault/services/key-state/folder.state.ts | 29 +-- .../src/components/import.component.ts | 10 +- .../individual-vault-export.service.spec.ts | 10 +- .../individual-vault-export.service.ts | 13 +- .../src/services/vault-export.service.spec.ts | 10 +- .../default-cipher-form-config.service.ts | 10 +- .../src/cipher-view/cipher-view.component.ts | 14 +- 49 files changed, 592 insertions(+), 387 deletions(-) diff --git a/apps/browser/src/autofill/background/notification.background.spec.ts b/apps/browser/src/autofill/background/notification.background.spec.ts index e043dbfdd2e..37c05a55a3a 100644 --- a/apps/browser/src/autofill/background/notification.background.spec.ts +++ b/apps/browser/src/autofill/background/notification.background.spec.ts @@ -60,10 +60,18 @@ describe("NotificationBackground", () => { const configService = mock(); const accountService = mock(); + const activeAccountSubject = new BehaviorSubject<{ id: UserId } & AccountInfo>({ + id: "testId" as UserId, + email: "test@example.com", + emailVerified: true, + name: "Test User", + }); + beforeEach(() => { activeAccountStatusMock$ = new BehaviorSubject(AuthenticationStatus.Locked); authService = mock(); authService.activeAccountStatus$ = activeAccountStatusMock$; + accountService.activeAccount$ = activeAccountSubject; notificationBackground = new NotificationBackground( autofillService, cipherService, @@ -683,13 +691,6 @@ describe("NotificationBackground", () => { }); describe("saveOrUpdateCredentials", () => { - const activeAccountSubject = new BehaviorSubject<{ id: UserId } & AccountInfo>({ - id: "testId" as UserId, - email: "test@example.com", - emailVerified: true, - name: "Test User", - }); - let getDecryptedCipherByIdSpy: jest.SpyInstance; let getAllDecryptedForUrlSpy: jest.SpyInstance; let updatePasswordSpy: jest.SpyInstance; diff --git a/apps/browser/src/autofill/background/notification.background.ts b/apps/browser/src/autofill/background/notification.background.ts index 0947ce1e1da..5c6ff3c2c8c 100644 --- a/apps/browser/src/autofill/background/notification.background.ts +++ b/apps/browser/src/autofill/background/notification.background.ts @@ -83,6 +83,8 @@ export default class NotificationBackground { getWebVaultUrlForNotification: () => this.getWebVaultUrl(), }; + private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id)); + constructor( private autofillService: AutofillService, private cipherService: CipherService, @@ -569,9 +571,7 @@ export default class NotificationBackground { return; } - const activeUserId = await firstValueFrom( - this.accountService.activeAccount$.pipe(map((a) => a?.id)), - ); + const activeUserId = await firstValueFrom(this.activeUserId$); const cipher = await this.cipherService.encrypt(newCipher, activeUserId); try { @@ -611,10 +611,7 @@ export default class NotificationBackground { return; } - const activeUserId = await firstValueFrom( - this.accountService.activeAccount$.pipe(map((a) => a?.id)), - ); - + const activeUserId = await firstValueFrom(this.activeUserId$); const cipher = await this.cipherService.encrypt(cipherView, activeUserId); try { // We've only updated the password, no need to broadcast editedCipher message @@ -647,17 +644,15 @@ export default class NotificationBackground { if (Utils.isNullOrWhitespace(folderId) || folderId === "null") { return false; } - - const folders = await firstValueFrom(this.folderService.folderViews$); + const activeUserId = await firstValueFrom(this.activeUserId$); + const folders = await firstValueFrom(this.folderService.folderViews$(activeUserId)); return folders.some((x) => x.id === folderId); } private async getDecryptedCipherById(cipherId: string) { const cipher = await this.cipherService.get(cipherId); if (cipher != null && cipher.type === CipherType.Login) { - const activeUserId = await firstValueFrom( - this.accountService.activeAccount$.pipe(map((a) => a?.id)), - ); + const activeUserId = await firstValueFrom(this.activeUserId$); return await cipher.decrypt( await this.cipherService.getKeyForCipherKeyDecryption(cipher, activeUserId), @@ -697,7 +692,8 @@ export default class NotificationBackground { * Returns the first value found from the folder service's folderViews$ observable. */ private async getFolderData() { - return await firstValueFrom(this.folderService.folderViews$); + const activeUserId = await firstValueFrom(this.activeUserId$); + return await firstValueFrom(this.folderService.folderViews$(activeUserId)); } private async getWebVaultUrl(): Promise { 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 e1e921cc3a3..f5daff93815 100644 --- a/apps/browser/src/platform/sync/foreground-sync.service.spec.ts +++ b/apps/browser/src/platform/sync/foreground-sync.service.spec.ts @@ -3,7 +3,6 @@ import { Subject } from "rxjs"; import { CollectionService } from "@bitwarden/admin-console/common"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; @@ -30,12 +29,12 @@ describe("ForegroundSyncService", () => { const cipherService = mock(); const collectionService = mock(); const apiService = mock(); - const accountService = mock(); + const accountService = mockAccountServiceWith(userId); const authService = mock(); const sendService = mock(); const sendApiService = mock(); const messageListener = mock(); - const stateProvider = new FakeStateProvider(mockAccountServiceWith(userId)); + const stateProvider = new FakeStateProvider(accountService); const sut = new ForegroundSyncService( stateService, diff --git a/apps/browser/src/vault/popup/components/vault-v2/add-edit-folder-dialog/add-edit-folder-dialog.component.spec.ts b/apps/browser/src/vault/popup/components/vault-v2/add-edit-folder-dialog/add-edit-folder-dialog.component.spec.ts index 4e222a554f7..cbec7903031 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/add-edit-folder-dialog/add-edit-folder-dialog.component.spec.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/add-edit-folder-dialog/add-edit-folder-dialog.component.spec.ts @@ -171,7 +171,7 @@ describe("AddEditFolderDialogComponent", () => { it("deletes the folder", async () => { await component.deleteFolder(); - expect(deleteFolder).toHaveBeenCalledWith(folderView.id); + expect(deleteFolder).toHaveBeenCalledWith(folderView.id, ""); expect(showToast).toHaveBeenCalledWith({ variant: "success", title: null, diff --git a/apps/browser/src/vault/popup/components/vault-v2/add-edit-folder-dialog/add-edit-folder-dialog.component.ts b/apps/browser/src/vault/popup/components/vault-v2/add-edit-folder-dialog/add-edit-folder-dialog.component.ts index 5fbd54d9d78..a50403cea2d 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/add-edit-folder-dialog/add-edit-folder-dialog.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/add-edit-folder-dialog/add-edit-folder-dialog.component.ts @@ -13,7 +13,7 @@ import { } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms"; -import { firstValueFrom } from "rxjs"; +import { firstValueFrom, map } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; @@ -67,6 +67,7 @@ export class AddEditFolderDialogComponent implements AfterViewInit, OnInit { name: ["", Validators.required], }); + private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id)); private destroyRef = inject(DestroyRef); constructor( @@ -114,10 +115,10 @@ export class AddEditFolderDialogComponent implements AfterViewInit, OnInit { this.folder.name = this.folderForm.controls.name.value; try { - const activeUserId = await firstValueFrom(this.accountService.activeAccount$); - const userKey = await this.keyService.getUserKeyWithLegacySupport(activeUserId.id); + const activeUserId = await firstValueFrom(this.activeUserId$); + const userKey = await this.keyService.getUserKeyWithLegacySupport(activeUserId); const folder = await this.folderService.encrypt(this.folder, userKey); - await this.folderApiService.save(folder); + await this.folderApiService.save(folder, activeUserId); this.toastService.showToast({ variant: "success", @@ -144,7 +145,8 @@ export class AddEditFolderDialogComponent implements AfterViewInit, OnInit { } try { - await this.folderApiService.delete(this.folder.id); + const activeUserId = await firstValueFrom(this.activeUserId$); + await this.folderApiService.delete(this.folder.id, activeUserId); this.toastService.showToast({ variant: "success", title: null, 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 580514de610..0eb91c6cbe2 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 @@ -7,9 +7,12 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { ProductTierType } from "@bitwarden/common/billing/enums"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { StateProvider } from "@bitwarden/common/platform/state"; +import { mockAccountServiceWith } from "@bitwarden/common/spec"; +import { 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"; @@ -32,7 +35,7 @@ describe("VaultPopupListFiltersService", () => { } as unknown as CollectionService; const folderService = { - folderViews$, + folderViews$: () => folderViews$, } as unknown as FolderService; const cipherService = { @@ -60,6 +63,8 @@ describe("VaultPopupListFiltersService", () => { policyAppliesToActiveUser$.next(false); policyService.policyAppliesToActiveUser$.mockClear(); + const accountService = mockAccountServiceWith("userId" as UserId); + collectionService.getAllNested = () => Promise.resolve([]); TestBed.configureTestingModule({ providers: [ @@ -92,6 +97,10 @@ describe("VaultPopupListFiltersService", () => { useValue: { getGlobal: () => ({ state$, update }) }, }, { provide: FormBuilder, useClass: FormBuilder }, + { + provide: AccountService, + useValue: accountService, + }, ], }); 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 0ab08d01e46..8455fd587d0 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 @@ -20,6 +20,7 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { ProductTierType } from "@bitwarden/common/billing/enums"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; @@ -102,6 +103,8 @@ export class VaultPopupListFiltersService { map((ciphers) => Object.values(ciphers)), ); + private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id)); + constructor( private folderService: FolderService, private cipherService: CipherService, @@ -111,6 +114,7 @@ export class VaultPopupListFiltersService { private formBuilder: FormBuilder, private policyService: PolicyService, private stateProvider: StateProvider, + private accountService: AccountService, ) { this.filterForm.controls.organization.valueChanges .pipe(takeUntilDestroyed()) @@ -264,60 +268,67 @@ export class VaultPopupListFiltersService { /** * Folder array structured to be directly passed to `ChipSelectComponent` */ - folders$: Observable[]> = combineLatest([ - this.filters$.pipe( - distinctUntilChanged( - (previousFilter, currentFilter) => - // Only update the collections when the organizationId filter changes - previousFilter.organization?.id === currentFilter.organization?.id, - ), - ), - this.folderService.folderViews$, - this.cipherViews$, - ]).pipe( - map(([filters, folders, cipherViews]): [PopupListFilter, FolderView[], CipherView[]] => { - if (folders.length === 1 && folders[0].id === null) { - // Do not display folder selections when only the "no folder" option is available. - return [filters, [], cipherViews]; - } + folders$: Observable[]> = this.activeUserId$.pipe( + switchMap((userId) => + combineLatest([ + this.filters$.pipe( + distinctUntilChanged( + (previousFilter, currentFilter) => + // Only update the collections when the organizationId filter changes + previousFilter.organization?.id === currentFilter.organization?.id, + ), + ), + this.folderService.folderViews$(userId), + this.cipherViews$, + ]).pipe( + map(([filters, folders, cipherViews]): [PopupListFilter, FolderView[], CipherView[]] => { + if (folders.length === 1 && folders[0].id === null) { + // Do not display folder selections when only the "no folder" option is available. + return [filters, [], cipherViews]; + } - // Sort folders by alphabetic name - folders.sort(Utils.getSortFunction(this.i18nService, "name")); - let arrangedFolders = folders; + // Sort folders by alphabetic name + folders.sort(Utils.getSortFunction(this.i18nService, "name")); + let arrangedFolders = folders; - const noFolder = folders.find((f) => f.id === null); + const noFolder = folders.find((f) => f.id === null); - if (noFolder) { - // Update `name` of the "no folder" option to "Items with no folder" - noFolder.name = this.i18nService.t("itemsWithNoFolder"); + if (noFolder) { + // Update `name` of the "no folder" option to "Items with no folder" + const updatedNoFolder = { + ...noFolder, + name: this.i18nService.t("itemsWithNoFolder"), + }; - // Move the "no folder" option to the end of the list - arrangedFolders = [...folders.filter((f) => f.id !== null), noFolder]; - } - return [filters, arrangedFolders, cipherViews]; - }), - map(([filters, folders, cipherViews]) => { - const organizationId = filters.organization?.id ?? null; + // Move the "no folder" option to the end of the list + arrangedFolders = [...folders.filter((f) => f.id !== null), updatedNoFolder]; + } + return [filters, arrangedFolders, cipherViews]; + }), + map(([filters, folders, cipherViews]) => { + const organizationId = filters.organization?.id ?? null; - // When no org or "My vault" is selected, return all folders - if (organizationId === null || organizationId === MY_VAULT_ID) { - return folders; - } + // When no org or "My vault" is selected, return all folders + if (organizationId === null || organizationId === MY_VAULT_ID) { + return folders; + } - const orgCiphers = cipherViews.filter((c) => c.organizationId === organizationId); + const orgCiphers = cipherViews.filter((c) => c.organizationId === organizationId); - // Return only the folders that have ciphers within the filtered organization - return folders.filter((f) => orgCiphers.some((oc) => oc.folderId === f.id)); - }), - map((folders) => { - const nestedFolders = this.getAllFoldersNested(folders); - return new DynamicTreeNode({ - fullList: folders, - nestedList: nestedFolders, - }); - }), - map((folders) => - folders.nestedList.map((f) => this.convertToChipSelectOption(f, "bwi-folder")), + // Return only the folders that have ciphers within the filtered organization + return folders.filter((f) => orgCiphers.some((oc) => oc.folderId === f.id)); + }), + map((folders) => { + const nestedFolders = this.getAllFoldersNested(folders); + return new DynamicTreeNode({ + fullList: folders, + nestedList: nestedFolders, + }); + }), + map((folders) => + folders.nestedList.map((f) => this.convertToChipSelectOption(f, "bwi-folder")), + ), + ), ), ); diff --git a/apps/browser/src/vault/popup/settings/folders-v2.component.spec.ts b/apps/browser/src/vault/popup/settings/folders-v2.component.spec.ts index eecad04613e..9c202e26fef 100644 --- a/apps/browser/src/vault/popup/settings/folders-v2.component.spec.ts +++ b/apps/browser/src/vault/popup/settings/folders-v2.component.spec.ts @@ -4,10 +4,13 @@ import { By } from "@angular/platform-browser"; import { mock } from "jest-mock-extended"; import { BehaviorSubject } from "rxjs"; +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 { mockAccountServiceWith } from "@bitwarden/common/spec"; +import { UserId } from "@bitwarden/common/types/guid"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; import { DialogService } from "@bitwarden/components"; @@ -52,8 +55,9 @@ describe("FoldersV2Component", () => { { provide: PlatformUtilsService, useValue: mock() }, { provide: ConfigService, useValue: mock() }, { provide: LogService, useValue: mock() }, - { provide: FolderService, useValue: { folderViews$ } }, + { provide: FolderService, useValue: { folderViews$: () => folderViews$ } }, { provide: I18nService, useValue: { t: (key: string) => key } }, + { provide: AccountService, useValue: mockAccountServiceWith("UserId" as UserId) }, ], }) .overrideComponent(FoldersV2Component, { diff --git a/apps/browser/src/vault/popup/settings/folders-v2.component.ts b/apps/browser/src/vault/popup/settings/folders-v2.component.ts index ce196132f88..b1db949f2ee 100644 --- a/apps/browser/src/vault/popup/settings/folders-v2.component.ts +++ b/apps/browser/src/vault/popup/settings/folders-v2.component.ts @@ -1,8 +1,10 @@ import { CommonModule } from "@angular/common"; import { Component } from "@angular/core"; -import { map, Observable } from "rxjs"; +import { filter, map, Observable, switchMap } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { UserId } from "@bitwarden/common/types/guid"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; import { @@ -45,18 +47,21 @@ export class FoldersV2Component { folders$: Observable; NoFoldersIcon = VaultIcons.NoFolders; + private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id)); constructor( private folderService: FolderService, private dialogService: DialogService, + private accountService: AccountService, ) { - this.folders$ = this.folderService.folderViews$.pipe( + this.folders$ = this.activeUserId$.pipe( + filter((userId): userId is UserId => userId !== null), + switchMap((userId) => this.folderService.folderViews$(userId)), map((folders) => { // Remove the last folder, which is the "no folder" option folder if (folders.length > 0) { return folders.slice(0, folders.length - 1); } - return folders; }), ); diff --git a/apps/browser/src/vault/popup/settings/folders.component.ts b/apps/browser/src/vault/popup/settings/folders.component.ts index edf7fe939e8..1e3f182b43d 100644 --- a/apps/browser/src/vault/popup/settings/folders.component.ts +++ b/apps/browser/src/vault/popup/settings/folders.component.ts @@ -1,7 +1,9 @@ import { Component } from "@angular/core"; import { Router } from "@angular/router"; -import { map, Observable } from "rxjs"; +import { filter, map, Observable, switchMap } from "rxjs"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { UserId } from "@bitwarden/common/types/guid"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; @@ -12,16 +14,21 @@ import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; export class FoldersComponent { folders$: Observable; + private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id)); + constructor( private folderService: FolderService, private router: Router, + private accountService: AccountService, ) { - this.folders$ = this.folderService.folderViews$.pipe( + this.folders$ = this.activeUserId$.pipe( + filter((userId): userId is UserId => userId != null), + switchMap((userId) => this.folderService.folderViews$(userId)), map((folders) => { + // Remove the last folder, which is the "no folder" option folder if (folders.length > 0) { - folders = folders.slice(0, folders.length - 1); + return folders.slice(0, folders.length - 1); } - return folders; }), ); diff --git a/apps/browser/src/vault/services/vault-filter.service.ts b/apps/browser/src/vault/services/vault-filter.service.ts index ba1d11f5d27..305c7de487b 100644 --- a/apps/browser/src/vault/services/vault-filter.service.ts +++ b/apps/browser/src/vault/services/vault-filter.service.ts @@ -24,7 +24,7 @@ export class VaultFilterService extends BaseVaultFilterService { collectionService: CollectionService, policyService: PolicyService, stateProvider: StateProvider, - private accountService: AccountService, + accountService: AccountService, ) { super( organizationService, @@ -33,6 +33,7 @@ export class VaultFilterService extends BaseVaultFilterService { collectionService, policyService, stateProvider, + accountService, ); this.vaultFilter.myVaultOnly = false; this.vaultFilter.selectedOrganizationId = null; diff --git a/apps/cli/src/commands/edit.command.ts b/apps/cli/src/commands/edit.command.ts index da7c98a35d6..dd99d03b086 100644 --- a/apps/cli/src/commands/edit.command.ts +++ b/apps/cli/src/commands/edit.command.ts @@ -24,6 +24,8 @@ import { CipherResponse } from "../vault/models/cipher.response"; import { FolderResponse } from "../vault/models/folder.response"; export class EditCommand { + private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id)); + constructor( private cipherService: CipherService, private folderService: FolderService, @@ -121,12 +123,12 @@ export class EditCommand { cipher.collectionIds = req; try { - const activeUserId = await firstValueFrom( - this.accountService.activeAccount$.pipe(map((a) => a?.id)), - ); const updatedCipher = await this.cipherService.saveCollectionsWithServer(cipher); const decCipher = await updatedCipher.decrypt( - await this.cipherService.getKeyForCipherKeyDecryption(updatedCipher, activeUserId), + await this.cipherService.getKeyForCipherKeyDecryption( + updatedCipher, + await firstValueFrom(this.activeUserId$), + ), ); const res = new CipherResponse(decCipher); return Response.success(res); @@ -136,7 +138,8 @@ export class EditCommand { } private async editFolder(id: string, req: FolderExport) { - const folder = await this.folderService.getFromState(id); + const activeUserId = await firstValueFrom(this.activeUserId$); + const folder = await this.folderService.getFromState(id, activeUserId); if (folder == null) { return Response.notFound(); } @@ -144,12 +147,11 @@ export class EditCommand { let folderView = await folder.decrypt(); folderView = FolderExport.toView(req, folderView); - const activeUserId = await firstValueFrom(this.accountService.activeAccount$); - const userKey = await this.keyService.getUserKeyWithLegacySupport(activeUserId.id); + const userKey = await this.keyService.getUserKeyWithLegacySupport(activeUserId); const encFolder = await this.folderService.encrypt(folderView, userKey); try { - await this.folderApiService.save(encFolder); - const updatedFolder = await this.folderService.get(folder.id); + await this.folderApiService.save(encFolder, activeUserId); + const updatedFolder = await this.folderService.get(folder.id, activeUserId); const decFolder = await updatedFolder.decrypt(); const res = new FolderResponse(decFolder); return Response.success(res); diff --git a/apps/cli/src/commands/get.command.ts b/apps/cli/src/commands/get.command.ts index 1a835befd01..7c3cc7caa9f 100644 --- a/apps/cli/src/commands/get.command.ts +++ b/apps/cli/src/commands/get.command.ts @@ -51,6 +51,8 @@ import { FolderResponse } from "../vault/models/folder.response"; import { DownloadCommand } from "./download.command"; export class GetCommand extends DownloadCommand { + private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id)); + constructor( private cipherService: CipherService, private folderService: FolderService, @@ -113,10 +115,8 @@ export class GetCommand extends DownloadCommand { let decCipher: CipherView = null; if (Utils.isGuid(id)) { const cipher = await this.cipherService.get(id); - const activeUserId = await firstValueFrom( - this.accountService.activeAccount$.pipe(map((a) => a?.id)), - ); if (cipher != null) { + const activeUserId = await firstValueFrom(this.activeUserId$); decCipher = await cipher.decrypt( await this.cipherService.getKeyForCipherKeyDecryption(cipher, activeUserId), ); @@ -383,13 +383,14 @@ export class GetCommand extends DownloadCommand { private async getFolder(id: string) { let decFolder: FolderView = null; + const activeUserId = await firstValueFrom(this.activeUserId$); if (Utils.isGuid(id)) { - const folder = await this.folderService.getFromState(id); + const folder = await this.folderService.getFromState(id, activeUserId); if (folder != null) { decFolder = await folder.decrypt(); } } else if (id.trim() !== "") { - let folders = await this.folderService.getAllDecryptedFromState(); + let folders = await this.folderService.getAllDecryptedFromState(activeUserId); folders = CliUtils.searchFolders(folders, id); if (folders.length > 1) { return Response.multipleResults(folders.map((f) => f.id)); @@ -551,9 +552,7 @@ export class GetCommand extends DownloadCommand { private async getFingerprint(id: string) { let fingerprint: string[] = null; if (id === "me") { - const activeUserId = await firstValueFrom( - this.accountService.activeAccount$.pipe(map((a) => a?.id)), - ); + const activeUserId = await firstValueFrom(this.activeUserId$); const publicKey = await firstValueFrom(this.keyService.userPublicKey$(activeUserId)); fingerprint = await this.keyService.getFingerprint(activeUserId, publicKey); } else if (Utils.isGuid(id)) { diff --git a/apps/cli/src/commands/list.command.ts b/apps/cli/src/commands/list.command.ts index 9cb36f71496..92da86b696a 100644 --- a/apps/cli/src/commands/list.command.ts +++ b/apps/cli/src/commands/list.command.ts @@ -1,4 +1,4 @@ -import { firstValueFrom } from "rxjs"; +import { firstValueFrom, map } from "rxjs"; import { OrganizationUserApiService, @@ -12,6 +12,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { EventType } from "@bitwarden/common/enums"; import { ListResponse as ApiListResponse } from "@bitwarden/common/models/response/list.response"; import { Utils } from "@bitwarden/common/platform/misc/utils"; @@ -38,6 +39,7 @@ export class ListCommand { private organizationUserApiService: OrganizationUserApiService, private apiService: ApiService, private eventCollectionService: EventCollectionService, + private accountService: AccountService, ) {} async run(object: string, cmdOptions: Record): Promise { @@ -135,7 +137,10 @@ export class ListCommand { } private async listFolders(options: Options) { - let folders = await this.folderService.getAllDecryptedFromState(); + const activeUserId = await firstValueFrom( + this.accountService.activeAccount$.pipe(map((a) => a?.id)), + ); + let folders = await this.folderService.getAllDecryptedFromState(activeUserId); if (options.search != null && options.search.trim() !== "") { folders = CliUtils.searchFolders(folders, options.search); diff --git a/apps/cli/src/oss-serve-configurator.ts b/apps/cli/src/oss-serve-configurator.ts index 71c372ef301..9bd3a2bee5f 100644 --- a/apps/cli/src/oss-serve-configurator.ts +++ b/apps/cli/src/oss-serve-configurator.ts @@ -76,6 +76,7 @@ export class OssServeConfigurator { this.serviceContainer.organizationUserApiService, this.serviceContainer.apiService, this.serviceContainer.eventCollectionService, + this.serviceContainer.accountService, ); this.createCommand = new CreateCommand( this.serviceContainer.cipherService, @@ -115,6 +116,7 @@ export class OssServeConfigurator { this.serviceContainer.folderApiService, this.serviceContainer.billingAccountProfileStateService, this.serviceContainer.cipherAuthorizationService, + this.serviceContainer.accountService, ); this.confirmCommand = new ConfirmCommand( this.serviceContainer.apiService, diff --git a/apps/cli/src/vault.program.ts b/apps/cli/src/vault.program.ts index 1cdf23bcd89..3eb0e68de09 100644 --- a/apps/cli/src/vault.program.ts +++ b/apps/cli/src/vault.program.ts @@ -113,6 +113,7 @@ export class VaultProgram extends BaseProgram { this.serviceContainer.organizationUserApiService, this.serviceContainer.apiService, this.serviceContainer.eventCollectionService, + this.serviceContainer.accountService, ); const response = await command.run(object, cmd); @@ -321,6 +322,7 @@ export class VaultProgram extends BaseProgram { this.serviceContainer.folderApiService, this.serviceContainer.billingAccountProfileStateService, this.serviceContainer.cipherAuthorizationService, + this.serviceContainer.accountService, ); const response = await command.run(object, id, cmd); this.processResponse(response); diff --git a/apps/cli/src/vault/create.command.ts b/apps/cli/src/vault/create.command.ts index aa01ec3c491..47e91cb55ff 100644 --- a/apps/cli/src/vault/create.command.ts +++ b/apps/cli/src/vault/create.command.ts @@ -30,6 +30,8 @@ import { CipherResponse } from "./models/cipher.response"; import { FolderResponse } from "./models/folder.response"; export class CreateCommand { + private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id)); + constructor( private cipherService: CipherService, private folderService: FolderService, @@ -86,9 +88,7 @@ export class CreateCommand { } private async createCipher(req: CipherExport) { - const activeUserId = await firstValueFrom( - this.accountService.activeAccount$.pipe(map((a) => a?.id)), - ); + const activeUserId = await firstValueFrom(this.activeUserId$); const cipher = await this.cipherService.encrypt(CipherExport.toView(req), activeUserId); try { const newCipher = await this.cipherService.createWithServer(cipher); @@ -152,9 +152,7 @@ export class CreateCommand { } try { - const activeUserId = await firstValueFrom( - this.accountService.activeAccount$.pipe(map((a) => a?.id)), - ); + const activeUserId = await firstValueFrom(this.activeUserId$); const updatedCipher = await this.cipherService.saveAttachmentRawWithServer( cipher, fileName, @@ -171,12 +169,12 @@ export class CreateCommand { } private async createFolder(req: FolderExport) { - const activeAccountId = await firstValueFrom(this.accountService.activeAccount$); - const userKey = await this.keyService.getUserKeyWithLegacySupport(activeAccountId.id); + const activeUserId = await firstValueFrom(this.activeUserId$); + const userKey = await this.keyService.getUserKeyWithLegacySupport(activeUserId); const folder = await this.folderService.encrypt(FolderExport.toView(req), userKey); try { - await this.folderApiService.save(folder); - const newFolder = await this.folderService.get(folder.id); + await this.folderApiService.save(folder, activeUserId); + const newFolder = await this.folderService.get(folder.id, activeUserId); const decFolder = await newFolder.decrypt(); const res = new FolderResponse(decFolder); return Response.success(res); diff --git a/apps/cli/src/vault/delete.command.ts b/apps/cli/src/vault/delete.command.ts index 7f79c89b17c..6b66b8bc7bb 100644 --- a/apps/cli/src/vault/delete.command.ts +++ b/apps/cli/src/vault/delete.command.ts @@ -1,6 +1,7 @@ -import { firstValueFrom } from "rxjs"; +import { firstValueFrom, map } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; @@ -19,6 +20,7 @@ export class DeleteCommand { private folderApiService: FolderApiServiceAbstraction, private accountProfileService: BillingAccountProfileStateService, private cipherAuthorizationService: CipherAuthorizationService, + private accountService: AccountService, ) {} async run(object: string, id: string, cmdOptions: Record): Promise { @@ -103,13 +105,16 @@ export class DeleteCommand { } private async deleteFolder(id: string) { - const folder = await this.folderService.getFromState(id); + const activeUserId = await firstValueFrom( + this.accountService.activeAccount$.pipe(map((a) => a?.id)), + ); + const folder = await this.folderService.getFromState(id, activeUserId); if (folder == null) { return Response.notFound(); } try { - await this.folderApiService.delete(id); + await this.folderApiService.delete(id, activeUserId); return Response.success(); } catch (e) { return Response.error(e); 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 341e44f643b..0ad7eef81be 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 @@ -6,7 +6,11 @@ import { mock } from "jest-mock-extended"; import { CollectionService } from "@bitwarden/admin-console/common"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec"; +import { UserId } from "@bitwarden/common/types/guid"; import { 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"; @@ -29,6 +33,8 @@ describe("EmergencyViewDialogComponent", () => { card: {}, } as CipherView; + const accountService: FakeAccountService = mockAccountServiceWith(Utils.newGuid() as UserId); + beforeEach(async () => { open.mockClear(); close.mockClear(); @@ -43,6 +49,7 @@ describe("EmergencyViewDialogComponent", () => { { provide: DialogService, useValue: { open } }, { provide: DialogRef, useValue: { close } }, { provide: DIALOG_DATA, useValue: { cipher: mockCipher } }, + { provide: AccountService, useValue: accountService }, ], }).compileComponents(); diff --git a/apps/web/src/app/key-management/migrate-encryption/migrate-legacy-encryption.component.ts b/apps/web/src/app/key-management/migrate-encryption/migrate-legacy-encryption.component.ts index bb5a1c511c6..2361293fe80 100644 --- a/apps/web/src/app/key-management/migrate-encryption/migrate-legacy-encryption.component.ts +++ b/apps/web/src/app/key-management/migrate-encryption/migrate-legacy-encryption.component.ts @@ -83,7 +83,7 @@ export class MigrateFromLegacyEncryptionComponent { }); if (deleteFolders) { - await this.folderApiService.deleteAll(); + await this.folderApiService.deleteAll(activeUser.id); await this.syncService.fullSync(true, true); await this.submit(); return; diff --git a/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-move-dialog/bulk-move-dialog.component.ts b/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-move-dialog/bulk-move-dialog.component.ts index d68e3b9d732..b7f99fb7b44 100644 --- a/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-move-dialog/bulk-move-dialog.component.ts +++ b/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-move-dialog/bulk-move-dialog.component.ts @@ -3,8 +3,9 @@ import { DialogConfig, DialogRef, DIALOG_DATA } from "@angular/cdk/dialog"; import { Component, Inject, OnInit } from "@angular/core"; import { FormBuilder, Validators } from "@angular/forms"; -import { firstValueFrom, Observable } from "rxjs"; +import { firstValueFrom, map, Observable } from "rxjs"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; @@ -47,6 +48,8 @@ export class BulkMoveDialogComponent implements OnInit { }); folders$: Observable; + private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id)); + constructor( @Inject(DIALOG_DATA) params: BulkMoveDialogParams, private dialogRef: DialogRef, @@ -55,12 +58,14 @@ export class BulkMoveDialogComponent implements OnInit { private i18nService: I18nService, private folderService: FolderService, private formBuilder: FormBuilder, + private accountService: AccountService, ) { this.cipherIds = params.cipherIds ?? []; } async ngOnInit() { - this.folders$ = this.folderService.folderViews$; + const activeUserId = await firstValueFrom(this.activeUserId$); + this.folders$ = this.folderService.folderViews$(activeUserId); this.formGroup.patchValue({ folderId: (await firstValueFrom(this.folders$))[0].id, }); diff --git a/apps/web/src/app/vault/individual-vault/folder-add-edit.component.ts b/apps/web/src/app/vault/individual-vault/folder-add-edit.component.ts index 4d181a0510d..88af1ef601b 100644 --- a/apps/web/src/app/vault/individual-vault/folder-add-edit.component.ts +++ b/apps/web/src/app/vault/individual-vault/folder-add-edit.component.ts @@ -61,7 +61,7 @@ export class FolderAddEditComponent extends BaseFolderAddEditComponent { } try { - await this.folderApiService.delete(this.folder.id); + await this.folderApiService.delete(this.folder.id, await firstValueFrom(this.activeUserId$)); this.toastService.showToast({ variant: "success", title: null, @@ -82,10 +82,10 @@ export class FolderAddEditComponent extends BaseFolderAddEditComponent { } try { - const activeAccountId = (await firstValueFrom(this.accountSerivce.activeAccount$)).id; + const activeAccountId = await firstValueFrom(this.activeUserId$); const userKey = await this.keyService.getUserKeyWithLegacySupport(activeAccountId); const folder = await this.folderService.encrypt(this.folder, userKey); - this.formPromise = this.folderApiService.save(folder); + this.formPromise = this.folderApiService.save(folder, activeAccountId); await this.formPromise; this.platformUtilsService.showToast( "success", diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/services/vault-filter.service.spec.ts b/apps/web/src/app/vault/individual-vault/vault-filter/services/vault-filter.service.spec.ts index 0386a20adbb..47003d51cae 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/services/vault-filter.service.spec.ts +++ b/apps/web/src/app/vault/individual-vault/vault-filter/services/vault-filter.service.spec.ts @@ -63,7 +63,7 @@ describe("vault filter service", () => { singleOrgPolicy = new ReplaySubject(1); organizationService.memberOrganizations$ = organizations; - folderService.folderViews$ = folderViews; + folderService.folderViews$.mockReturnValue(folderViews); collectionService.decryptedCollections$ = collectionViews; policyService.policyAppliesToActiveUser$ .calledWith(PolicyType.PersonalOwnership) @@ -81,6 +81,7 @@ describe("vault filter service", () => { i18nService, stateProvider, collectionService, + accountService, ); collapsedGroupingsState = stateProvider.activeUser.getFake(COLLAPSED_GROUPINGS); }); diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/services/vault-filter.service.ts b/apps/web/src/app/vault/individual-vault/vault-filter/services/vault-filter.service.ts index c4ac3dc2d70..97b44132e60 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/services/vault-filter.service.ts +++ b/apps/web/src/app/vault/individual-vault/vault-filter/services/vault-filter.service.ts @@ -21,6 +21,7 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { ActiveUserState, StateProvider } from "@bitwarden/common/platform/state"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; @@ -45,6 +46,8 @@ const NestingDelimiter = "/"; @Injectable() export class VaultFilterService implements VaultFilterServiceAbstraction { + private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id)); + organizationTree$: Observable> = combineLatest([ this.organizationService.memberOrganizations$, this.policyService.policyAppliesToActiveUser$(PolicyType.SingleOrg), @@ -57,8 +60,14 @@ export class VaultFilterService implements VaultFilterServiceAbstraction { protected _organizationFilter = new BehaviorSubject(null); - filteredFolders$: Observable = this.folderService.folderViews$.pipe( - combineLatestWith(this.cipherService.cipherViews$, this._organizationFilter), + filteredFolders$: Observable = this.activeUserId$.pipe( + switchMap((userId) => + combineLatest([ + this.folderService.folderViews$(userId), + this.cipherService.cipherViews$, + this._organizationFilter, + ]), + ), switchMap(([folders, ciphers, org]) => { return this.filterFolders(folders, ciphers, org); }), @@ -95,6 +104,7 @@ export class VaultFilterService implements VaultFilterServiceAbstraction { protected i18nService: I18nService, protected stateProvider: StateProvider, protected collectionService: CollectionService, + protected accountService: AccountService, ) {} async getCollectionNodeFromTree(id: string) { diff --git a/apps/web/src/app/vault/individual-vault/view.component.spec.ts b/apps/web/src/app/vault/individual-vault/view.component.spec.ts index b26c55d46e8..bde9f564c4a 100644 --- a/apps/web/src/app/vault/individual-vault/view.component.spec.ts +++ b/apps/web/src/app/vault/individual-vault/view.component.spec.ts @@ -5,11 +5,14 @@ import { mock } from "jest-mock-extended"; import { CollectionService } from "@bitwarden/admin-console/common"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.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 { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; +import { mockAccountServiceWith } from "@bitwarden/common/spec"; +import { 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 { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; @@ -63,6 +66,7 @@ describe("ViewComponent", () => { useValue: mock(), }, { provide: ConfigService, useValue: mock() }, + { provide: AccountService, useValue: mockAccountServiceWith("UserId" as UserId) }, { provide: CipherAuthorizationService, useValue: { diff --git a/apps/web/src/app/vault/org-vault/vault-filter/vault-filter.service.ts b/apps/web/src/app/vault/org-vault/vault-filter/vault-filter.service.ts index c4ac9d73df7..e2d713649f5 100644 --- a/apps/web/src/app/vault/org-vault/vault-filter/vault-filter.service.ts +++ b/apps/web/src/app/vault/org-vault/vault-filter/vault-filter.service.ts @@ -4,6 +4,7 @@ import { map, Observable, ReplaySubject, Subject } from "rxjs"; import { CollectionAdminView, CollectionService } from "@bitwarden/admin-console/common"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { StateProvider } from "@bitwarden/common/platform/state"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; @@ -32,6 +33,7 @@ export class VaultFilterService extends BaseVaultFilterService implements OnDest i18nService: I18nService, stateProvider: StateProvider, collectionService: CollectionService, + accountService: AccountService, ) { super( organizationService, @@ -41,6 +43,7 @@ export class VaultFilterService extends BaseVaultFilterService implements OnDest i18nService, stateProvider, collectionService, + accountService, ); } diff --git a/libs/angular/src/vault/components/add-edit.component.ts b/libs/angular/src/vault/components/add-edit.component.ts index 3a9d0289971..4bd3e9710fb 100644 --- a/libs/angular/src/vault/components/add-edit.component.ts +++ b/libs/angular/src/vault/components/add-edit.component.ts @@ -102,6 +102,8 @@ export class AddEditComponent implements OnInit, OnDestroy { private personalOwnershipPolicyAppliesToActiveUser: boolean; private previousCipherId: string; + private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id)); + get fido2CredentialCreationDateValue(): string { const dateCreated = this.i18nService.t("dateCreated"); const creationDate = this.datePipe.transform( @@ -259,12 +261,10 @@ export class AddEditComponent implements OnInit, OnDestroy { const loadedAddEditCipherInfo = await this.loadAddEditCipherInfo(); + const activeUserId = await firstValueFrom(this.activeUserId$); if (this.cipher == null) { if (this.editMode) { const cipher = await this.loadCipher(); - const activeUserId = await firstValueFrom( - this.accountService.activeAccount$.pipe(map((a) => a?.id)), - ); this.cipher = await cipher.decrypt( await this.cipherService.getKeyForCipherKeyDecryption(cipher, activeUserId), ); @@ -323,7 +323,7 @@ export class AddEditComponent implements OnInit, OnDestroy { this.cipher.login.fido2Credentials = null; } - this.folders$ = this.folderService.folderViews$; + this.folders$ = this.folderService.folderViews$(activeUserId); if (this.editMode && this.previousCipherId !== this.cipherId) { void this.eventCollectionService.collectMany(EventType.Cipher_ClientViewed, [this.cipher]); diff --git a/libs/angular/src/vault/components/folder-add-edit.component.ts b/libs/angular/src/vault/components/folder-add-edit.component.ts index 6d9ae53cc66..205733ba48d 100644 --- a/libs/angular/src/vault/components/folder-add-edit.component.ts +++ b/libs/angular/src/vault/components/folder-add-edit.component.ts @@ -2,7 +2,7 @@ // @ts-strict-ignore import { Directive, EventEmitter, Input, OnInit, Output } from "@angular/core"; import { Validators, FormBuilder } from "@angular/forms"; -import { firstValueFrom } from "rxjs"; +import { firstValueFrom, map } from "rxjs"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -27,6 +27,8 @@ export class FolderAddEditComponent implements OnInit { deletePromise: Promise; protected componentName = ""; + protected activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id)); + formGroup = this.formBuilder.group({ name: ["", [Validators.required]], }); @@ -59,10 +61,10 @@ export class FolderAddEditComponent implements OnInit { } try { - const activeAccountId = await firstValueFrom(this.accountService.activeAccount$); - const userKey = await this.keyService.getUserKeyWithLegacySupport(activeAccountId.id); + const activeUserId = await firstValueFrom(this.activeUserId$); + const userKey = await this.keyService.getUserKeyWithLegacySupport(activeUserId); const folder = await this.folderService.encrypt(this.folder, userKey); - this.formPromise = this.folderApiService.save(folder); + this.formPromise = this.folderApiService.save(folder, activeUserId); await this.formPromise; this.platformUtilsService.showToast( "success", @@ -90,7 +92,8 @@ export class FolderAddEditComponent implements OnInit { } try { - this.deletePromise = this.folderApiService.delete(this.folder.id); + const activeUserId = await firstValueFrom(this.activeUserId$); + this.deletePromise = this.folderApiService.delete(this.folder.id, activeUserId); await this.deletePromise; this.platformUtilsService.showToast("success", null, this.i18nService.t("deletedFolder")); this.onDeletedFolder.emit(this.folder); @@ -107,8 +110,10 @@ export class FolderAddEditComponent implements OnInit { if (this.editMode) { this.editMode = true; this.title = this.i18nService.t("editFolder"); - const folder = await this.folderService.get(this.folderId); - this.folder = await folder.decrypt(); + const activeUserId = await firstValueFrom(this.activeUserId$); + this.folder = await firstValueFrom( + this.folderService.getDecrypted$(this.folderId, activeUserId), + ); } else { this.title = this.i18nService.t("addFolder"); } diff --git a/libs/angular/src/vault/components/view.component.ts b/libs/angular/src/vault/components/view.component.ts index a6b9a571730..6bea4cd6150 100644 --- a/libs/angular/src/vault/components/view.component.ts +++ b/libs/angular/src/vault/components/view.component.ts @@ -79,6 +79,8 @@ export class ViewComponent implements OnDestroy, OnInit { private previousCipherId: string; private passwordReprompted = false; + private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id)); + get fido2CredentialCreationDateValue(): string { const dateCreated = this.i18nService.t("dateCreated"); const creationDate = this.datePipe.transform( @@ -141,9 +143,7 @@ export class ViewComponent implements OnDestroy, OnInit { this.cleanUp(); const cipher = await this.cipherService.get(this.cipherId); - const activeUserId = await firstValueFrom( - this.accountService.activeAccount$.pipe(map((a) => a?.id)), - ); + const activeUserId = await firstValueFrom(this.activeUserId$); this.cipher = await cipher.decrypt( await this.cipherService.getKeyForCipherKeyDecryption(cipher, activeUserId), ); @@ -158,7 +158,7 @@ export class ViewComponent implements OnDestroy, OnInit { if (this.cipher.folderId) { this.folder = await ( - await firstValueFrom(this.folderService.folderViews$) + await firstValueFrom(this.folderService.folderViews$(activeUserId)) ).find((f) => f.id == this.cipher.folderId); } diff --git a/libs/angular/src/vault/vault-filter/services/vault-filter.service.ts b/libs/angular/src/vault/vault-filter/services/vault-filter.service.ts index a2b624876be..dd0b49f356a 100644 --- a/libs/angular/src/vault/vault-filter/services/vault-filter.service.ts +++ b/libs/angular/src/vault/vault-filter/services/vault-filter.service.ts @@ -1,7 +1,7 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { Injectable } from "@angular/core"; -import { firstValueFrom, from, map, mergeMap, Observable } from "rxjs"; +import { firstValueFrom, from, map, mergeMap, Observable, switchMap } from "rxjs"; import { CollectionService, CollectionView } from "@bitwarden/admin-console/common"; import { @@ -11,6 +11,7 @@ import { import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { ActiveUserState, StateProvider } from "@bitwarden/common/platform/state"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; @@ -32,6 +33,8 @@ export class VaultFilterService implements DeprecatedVaultFilterServiceAbstracti private readonly collapsedGroupings$: Observable> = this.collapsedGroupingsState.state$.pipe(map((c) => new Set(c))); + private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id)); + constructor( protected organizationService: OrganizationService, protected folderService: FolderService, @@ -39,6 +42,7 @@ export class VaultFilterService implements DeprecatedVaultFilterServiceAbstracti protected collectionService: CollectionService, protected policyService: PolicyService, protected stateProvider: StateProvider, + protected accountService: AccountService, ) {} async storeCollapsedFilterNodes(collapsedFilterNodes: Set): Promise { @@ -81,7 +85,8 @@ export class VaultFilterService implements DeprecatedVaultFilterServiceAbstracti }); }; - return this.folderService.folderViews$.pipe( + return this.activeUserId$.pipe( + switchMap((userId) => this.folderService.folderViews$(userId)), mergeMap((folders) => from(transformation(folders))), ); } @@ -126,8 +131,9 @@ export class VaultFilterService implements DeprecatedVaultFilterServiceAbstracti } async getFolderNested(id: string): Promise> { + const activeUserId = await firstValueFrom(this.activeUserId$); const folders = await this.getAllFoldersNested( - await firstValueFrom(this.folderService.folderViews$), + await firstValueFrom(this.folderService.folderViews$(activeUserId)), ); return ServiceUtils.getTreeNodeObjectFromList(folders, id) as TreeNode; } diff --git a/libs/common/src/platform/state/state-definitions.ts b/libs/common/src/platform/state/state-definitions.ts index ce9eb5a15c0..1ed5227cb13 100644 --- a/libs/common/src/platform/state/state-definitions.ts +++ b/libs/common/src/platform/state/state-definitions.ts @@ -150,6 +150,9 @@ export const COLLECTION_DATA = new StateDefinition("collection", "disk", { web: "memory", }); export const FOLDER_DISK = new StateDefinition("folder", "disk", { web: "memory" }); +export const FOLDER_MEMORY = new StateDefinition("decryptedFolders", "memory", { + browser: "memory-large-object", +}); export const VAULT_FILTER_DISK = new StateDefinition("vaultFilter", "disk", { web: "disk-local", }); diff --git a/libs/common/src/platform/sync/core-sync.service.ts b/libs/common/src/platform/sync/core-sync.service.ts index 180767ccd16..cfa9030c9de 100644 --- a/libs/common/src/platform/sync/core-sync.service.ts +++ b/libs/common/src/platform/sync/core-sync.service.ts @@ -85,18 +85,25 @@ export abstract class CoreSyncService implements SyncService { await this.stateProvider.getUser(userId, LAST_SYNC_DATE).update(() => date); } - async syncUpsertFolder(notification: SyncFolderNotification, isEdit: boolean): Promise { + async syncUpsertFolder( + notification: SyncFolderNotification, + isEdit: boolean, + userId: UserId, + ): Promise { this.syncStarted(); - if (await this.stateService.getIsAuthenticated()) { + + const authStatus = await firstValueFrom(this.authService.authStatusFor$(userId)); + + if (authStatus >= AuthenticationStatus.Locked) { try { - const localFolder = await this.folderService.get(notification.id); + const localFolder = await this.folderService.get(notification.id, userId); if ( (!isEdit && localFolder == null) || (isEdit && localFolder != null && localFolder.revisionDate < notification.revisionDate) ) { const remoteFolder = await this.folderApiService.get(notification.id); if (remoteFolder != null) { - await this.folderService.upsert(new FolderData(remoteFolder)); + await this.folderService.upsert(new FolderData(remoteFolder), userId); this.messageSender.send("syncedUpsertedFolder", { folderId: notification.id }); return this.syncCompleted(true); } @@ -108,10 +115,13 @@ export abstract class CoreSyncService implements SyncService { return this.syncCompleted(false); } - async syncDeleteFolder(notification: SyncFolderNotification): Promise { + async syncDeleteFolder(notification: SyncFolderNotification, userId: UserId): Promise { this.syncStarted(); - if (await this.stateService.getIsAuthenticated()) { - await this.folderService.delete(notification.id); + + const authStatus = await firstValueFrom(this.authService.authStatusFor$(userId)); + + if (authStatus >= AuthenticationStatus.Locked) { + await this.folderService.delete(notification.id, userId); this.messageSender.send("syncedDeletedFolder", { folderId: notification.id }); this.syncCompleted(true); return true; diff --git a/libs/common/src/platform/sync/sync.service.ts b/libs/common/src/platform/sync/sync.service.ts index 733b7beaff5..6763e01cab7 100644 --- a/libs/common/src/platform/sync/sync.service.ts +++ b/libs/common/src/platform/sync/sync.service.ts @@ -56,8 +56,9 @@ export abstract class SyncService { abstract syncUpsertFolder( notification: SyncFolderNotification, isEdit: boolean, + userId: UserId, ): Promise; - abstract syncDeleteFolder(notification: SyncFolderNotification): Promise; + abstract syncDeleteFolder(notification: SyncFolderNotification, userId: UserId): Promise; abstract syncUpsertCipher( notification: SyncCipherNotification, isEdit: boolean, diff --git a/libs/common/src/services/notifications.service.ts b/libs/common/src/services/notifications.service.ts index 6f7c5c9f262..4a14332af8a 100644 --- a/libs/common/src/services/notifications.service.ts +++ b/libs/common/src/services/notifications.service.ts @@ -168,10 +168,14 @@ export class NotificationsService implements NotificationsServiceAbstraction { await this.syncService.syncUpsertFolder( notification.payload as SyncFolderNotification, notification.type === NotificationType.SyncFolderUpdate, + payloadUserId, ); break; case NotificationType.SyncFolderDelete: - await this.syncService.syncDeleteFolder(notification.payload as SyncFolderNotification); + await this.syncService.syncDeleteFolder( + notification.payload as SyncFolderNotification, + payloadUserId, + ); break; case NotificationType.SyncVault: case NotificationType.SyncCiphers: diff --git a/libs/common/src/services/vault-timeout/vault-timeout.service.spec.ts b/libs/common/src/services/vault-timeout/vault-timeout.service.spec.ts index 71341a98a62..1350010f849 100644 --- a/libs/common/src/services/vault-timeout/vault-timeout.service.spec.ts +++ b/libs/common/src/services/vault-timeout/vault-timeout.service.spec.ts @@ -334,7 +334,7 @@ describe("VaultTimeoutService", () => { // Active users should have additional steps ran expect(searchService.clearIndex).toHaveBeenCalled(); - expect(folderService.clearCache).toHaveBeenCalled(); + expect(folderService.clearDecryptedFolderState).toHaveBeenCalled(); expectUserToHaveLoggedOut("3"); // They have chosen logout as their action and it's available, log them out expectUserToHaveLoggedOut("4"); // They may have had lock as their chosen action but it's not available to them so logout diff --git a/libs/common/src/services/vault-timeout/vault-timeout.service.ts b/libs/common/src/services/vault-timeout/vault-timeout.service.ts index f6ad0f17e9a..f465174bf40 100644 --- a/libs/common/src/services/vault-timeout/vault-timeout.service.ts +++ b/libs/common/src/services/vault-timeout/vault-timeout.service.ts @@ -135,10 +135,10 @@ export class VaultTimeoutService implements VaultTimeoutServiceAbstraction { if (userId == null || userId === currentUserId) { await this.searchService.clearIndex(); - await this.folderService.clearCache(); await this.collectionService.clearActiveUserCache(); } + await this.folderService.clearDecryptedFolderState(userId); await this.masterPasswordService.clearMasterKey(lockingUserId); await this.stateService.setUserKeyAutoUnlock(null, { userId: lockingUserId }); diff --git a/libs/common/src/vault/abstractions/folder/folder-api.service.abstraction.ts b/libs/common/src/vault/abstractions/folder/folder-api.service.abstraction.ts index a732acfa991..859f2183edb 100644 --- a/libs/common/src/vault/abstractions/folder/folder-api.service.abstraction.ts +++ b/libs/common/src/vault/abstractions/folder/folder-api.service.abstraction.ts @@ -1,11 +1,13 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore +import { UserId } from "@bitwarden/common/types/guid"; + import { Folder } from "../../models/domain/folder"; import { FolderResponse } from "../../models/response/folder.response"; export class FolderApiServiceAbstraction { - save: (folder: Folder) => Promise; - delete: (id: string) => Promise; + save: (folder: Folder, userId: UserId) => Promise; + delete: (id: string, userId: UserId) => Promise; get: (id: string) => Promise; - deleteAll: () => Promise; + deleteAll: (userId: UserId) => Promise; } diff --git a/libs/common/src/vault/abstractions/folder/folder.service.abstraction.ts b/libs/common/src/vault/abstractions/folder/folder.service.abstraction.ts index c5722e0f57b..b7241e3ae37 100644 --- a/libs/common/src/vault/abstractions/folder/folder.service.abstraction.ts +++ b/libs/common/src/vault/abstractions/folder/folder.service.abstraction.ts @@ -13,23 +13,27 @@ import { FolderWithIdRequest } from "../../models/request/folder-with-id.request import { FolderView } from "../../models/view/folder.view"; export abstract class FolderService implements UserKeyRotationDataProvider { - folders$: Observable; - folderViews$: Observable; + folders$: (userId: UserId) => Observable; + folderViews$: (userId: UserId) => Observable; - clearCache: () => Promise; + clearDecryptedFolderState: (userId: UserId) => Promise; encrypt: (model: FolderView, key: SymmetricCryptoKey) => Promise; - get: (id: string) => Promise; - getDecrypted$: (id: string) => Observable; - getAllFromState: () => Promise; + get: (id: string, userId: UserId) => Promise; + getDecrypted$: (id: string, userId: UserId) => Observable; + /** + * @deprecated Use firstValueFrom(folders$) directly instead + * @param userId The user id + * @returns Promise of folders array + */ + getAllFromState: (userId: UserId) => Promise; /** * @deprecated Only use in CLI! */ - getFromState: (id: string) => Promise; + getFromState: (id: string, userId: UserId) => Promise; /** * @deprecated Only use in CLI! */ - getAllDecryptedFromState: () => Promise; - decryptFolders: (folders: Folder[]) => Promise; + getAllDecryptedFromState: (userId: UserId) => Promise; /** * Returns user folders re-encrypted with the new user key. * @param originalUserKey the original user key @@ -46,8 +50,8 @@ export abstract class FolderService implements UserKeyRotationDataProvider Promise; + upsert: (folder: FolderData | FolderData[], userId: UserId) => Promise; replace: (folders: { [id: string]: FolderData }, userId: UserId) => Promise; - clear: (userId?: string) => Promise; - delete: (id: string | string[]) => Promise; + clear: (userId: UserId) => Promise; + delete: (id: string | string[], userId: UserId) => Promise; } diff --git a/libs/common/src/vault/services/folder/folder-api.service.ts b/libs/common/src/vault/services/folder/folder-api.service.ts index e46df37c176..24831393668 100644 --- a/libs/common/src/vault/services/folder/folder-api.service.ts +++ b/libs/common/src/vault/services/folder/folder-api.service.ts @@ -1,3 +1,5 @@ +import { UserId } from "@bitwarden/common/types/guid"; + import { ApiService } from "../../../abstractions/api.service"; import { FolderApiServiceAbstraction } from "../../../vault/abstractions/folder/folder-api.service.abstraction"; import { InternalFolderService } from "../../../vault/abstractions/folder/folder.service.abstraction"; @@ -12,7 +14,7 @@ export class FolderApiService implements FolderApiServiceAbstraction { private apiService: ApiService, ) {} - async save(folder: Folder): Promise { + async save(folder: Folder, userId: UserId): Promise { const request = new FolderRequest(folder); let response: FolderResponse; @@ -24,17 +26,17 @@ export class FolderApiService implements FolderApiServiceAbstraction { } const data = new FolderData(response); - await this.folderService.upsert(data); + await this.folderService.upsert(data, userId); } - async delete(id: string): Promise { + async delete(id: string, userId: UserId): Promise { await this.deleteFolder(id); - await this.folderService.delete(id); + await this.folderService.delete(id, userId); } - async deleteAll(): Promise { + async deleteAll(userId: UserId): Promise { await this.apiService.send("DELETE", "/folders/all", null, true, false); - await this.folderService.clear(); + await this.folderService.clear(userId); } async get(id: string): Promise { diff --git a/libs/common/src/vault/services/folder/folder.service.spec.ts b/libs/common/src/vault/services/folder/folder.service.spec.ts index 193d0e85e61..612cd83d99b 100644 --- a/libs/common/src/vault/services/folder/folder.service.spec.ts +++ b/libs/common/src/vault/services/folder/folder.service.spec.ts @@ -1,10 +1,10 @@ import { mock, MockProxy } from "jest-mock-extended"; -import { firstValueFrom } from "rxjs"; +import { BehaviorSubject, firstValueFrom } from "rxjs"; import { KeyService } from "../../../../../key-management/src/abstractions/key.service"; -import { makeStaticByteArray } from "../../../../spec"; +import { makeEncString } from "../../../../spec"; import { FakeAccountService, mockAccountServiceWith } from "../../../../spec/fake-account-service"; -import { FakeActiveUserState } from "../../../../spec/fake-state"; +import { FakeSingleUserState } from "../../../../spec/fake-state"; import { FakeStateProvider } from "../../../../spec/fake-state-provider"; import { EncryptService } from "../../../platform/abstractions/encrypt.service"; import { I18nService } from "../../../platform/abstractions/i18n.service"; @@ -17,7 +17,7 @@ import { CipherService } from "../../abstractions/cipher.service"; import { FolderData } from "../../models/data/folder.data"; import { FolderView } from "../../models/view/folder.view"; import { FolderService } from "../../services/folder/folder.service"; -import { FOLDER_ENCRYPTED_FOLDERS } from "../key-state/folder.state"; +import { FOLDER_DECRYPTED_FOLDERS, FOLDER_ENCRYPTED_FOLDERS } from "../key-state/folder.state"; describe("Folder Service", () => { let folderService: FolderService; @@ -30,7 +30,7 @@ describe("Folder Service", () => { const mockUserId = Utils.newGuid() as UserId; let accountService: FakeAccountService; - let folderState: FakeActiveUserState>; + let folderState: FakeSingleUserState>; beforeEach(() => { keyService = mock(); @@ -42,11 +42,9 @@ describe("Folder Service", () => { stateProvider = new FakeStateProvider(accountService); i18nService.collator = new Intl.Collator("en"); + i18nService.t.mockReturnValue("No Folder"); - keyService.hasUserKey.mockResolvedValue(true); - keyService.getUserKeyWithLegacySupport.mockResolvedValue( - new SymmetricCryptoKey(makeStaticByteArray(32)) as UserKey, - ); + keyService.userKey$.mockReturnValue(new BehaviorSubject("mockOriginalUserKey" as any)); encryptService.decryptToUtf8.mockResolvedValue("DEC"); folderService = new FolderService( @@ -57,10 +55,53 @@ describe("Folder Service", () => { stateProvider, ); - folderState = stateProvider.activeUser.getFake(FOLDER_ENCRYPTED_FOLDERS); + folderState = stateProvider.singleUser.getFake(mockUserId, FOLDER_ENCRYPTED_FOLDERS); // Initial state - folderState.nextState({ "1": folderData("1", "test") }); + folderState.nextState({ "1": folderData("1") }); + }); + + describe("folders$", () => { + it("emits encrypted folders from state", async () => { + const folder1 = folderData("1"); + const folder2 = folderData("2"); + + await stateProvider.setUserState( + FOLDER_ENCRYPTED_FOLDERS, + Object.fromEntries([folder1, folder2].map((f) => [f.id, f])), + mockUserId, + ); + + const result = await firstValueFrom(folderService.folders$(mockUserId)); + + expect(result.length).toBe(2); + expect(result).toIncludeAllPartialMembers([ + { id: "1", name: makeEncString("ENC_STRING_1") }, + { id: "2", name: makeEncString("ENC_STRING_2") }, + ]); + }); + }); + + describe("folderView$", () => { + it("emits decrypted folders from state", async () => { + const folder1 = folderData("1"); + const folder2 = folderData("2"); + + await stateProvider.setUserState( + FOLDER_ENCRYPTED_FOLDERS, + Object.fromEntries([folder1, folder2].map((f) => [f.id, f])), + mockUserId, + ); + + const result = await firstValueFrom(folderService.folderViews$(mockUserId)); + + expect(result.length).toBe(3); + expect(result).toIncludeAllPartialMembers([ + { id: "1", name: "DEC" }, + { id: "2", name: "DEC" }, + { name: "No Folder" }, + ]); + }); }); it("encrypt", async () => { @@ -83,105 +124,83 @@ describe("Folder Service", () => { describe("get", () => { it("exists", async () => { - const result = await folderService.get("1"); + const result = await folderService.get("1", mockUserId); expect(result).toEqual({ id: "1", - name: { - encryptedString: "test", - encryptionType: 0, - }, + name: makeEncString("ENC_STRING_" + 1), revisionDate: null, }); }); it("not exists", async () => { - const result = await folderService.get("2"); + const result = await folderService.get("2", mockUserId); expect(result).toBe(undefined); }); }); it("upsert", async () => { - await folderService.upsert(folderData("2", "test 2")); + await folderService.upsert(folderData("2"), mockUserId); - expect(await firstValueFrom(folderService.folders$)).toEqual([ + expect(await firstValueFrom(folderService.folders$(mockUserId))).toEqual([ { id: "1", - name: { - encryptedString: "test", - encryptionType: 0, - }, + name: makeEncString("ENC_STRING_" + 1), revisionDate: null, }, { id: "2", - name: { - encryptedString: "test 2", - encryptionType: 0, - }, + name: makeEncString("ENC_STRING_" + 2), revisionDate: null, }, ]); }); it("replace", async () => { - await folderService.replace({ "2": folderData("2", "test 2") }, mockUserId); + await folderService.replace({ "4": folderData("4") }, mockUserId); - expect(await firstValueFrom(folderService.folders$)).toEqual([ + expect(await firstValueFrom(folderService.folders$(mockUserId))).toEqual([ { - id: "2", - name: { - encryptedString: "test 2", - encryptionType: 0, - }, + id: "4", + name: makeEncString("ENC_STRING_" + 4), revisionDate: null, }, ]); }); it("delete", async () => { - await folderService.delete("1"); + await folderService.delete("1", mockUserId); - expect((await firstValueFrom(folderService.folders$)).length).toBe(0); + expect((await firstValueFrom(folderService.folders$(mockUserId))).length).toBe(0); }); - it("clearCache", async () => { - await folderService.clearCache(); + describe("clearDecryptedFolderState", () => { + it("null userId", async () => { + await expect(folderService.clearDecryptedFolderState(null)).rejects.toThrow( + "User ID is required.", + ); + }); + + it("userId provided", async () => { + await folderService.clearDecryptedFolderState(mockUserId); - expect((await firstValueFrom(folderService.folders$)).length).toBe(1); - expect((await firstValueFrom(folderService.folderViews$)).length).toBe(0); + expect((await firstValueFrom(folderService.folders$(mockUserId))).length).toBe(1); + expect( + (await firstValueFrom(stateProvider.getUserState$(FOLDER_DECRYPTED_FOLDERS, mockUserId))) + .length, + ).toBe(0); + }); }); - describe("clear", () => { - it("null userId", async () => { - await folderService.clear(); + it("clear", async () => { + await folderService.clear(mockUserId); - expect((await firstValueFrom(folderService.folders$)).length).toBe(0); - expect((await firstValueFrom(folderService.folderViews$)).length).toBe(0); - }); + expect((await firstValueFrom(folderService.folders$(mockUserId))).length).toBe(0); - /** - * TODO: Fix this test to address the problem where the fakes for the active user state is not - * updated as expected - */ - // it("matching userId", async () => { - // stateService.getUserId.mockResolvedValue("1"); - // await folderService.clear("1" as UserId); - - // expect((await firstValueFrom(folderService.folders$)).length).toBe(0); - // }); - - /** - * TODO: Fix this test to address the problem where the fakes for the active user state is not - * updated as expected - */ - // it("mismatching userId", async () => { - // await folderService.clear("12" as UserId); - - // expect((await firstValueFrom(folderService.folders$)).length).toBe(1); - // expect((await firstValueFrom(folderService.folderViews$)).length).toBe(2); - // }); + const folderViews = await firstValueFrom(folderService.folderViews$(mockUserId)); + expect(folderViews.length).toBe(1); + expect(folderViews[0].id).toBeNull(); // Should be the "No Folder" folder }); describe("getRotatedData", () => { @@ -207,10 +226,10 @@ describe("Folder Service", () => { }); }); - function folderData(id: string, name: string) { + function folderData(id: string) { const data = new FolderData({} as any); data.id = id; - data.name = name; + data.name = makeEncString("ENC_STRING_" + data.id).encryptedString; return data; } diff --git a/libs/common/src/vault/services/folder/folder.service.ts b/libs/common/src/vault/services/folder/folder.service.ts index 07b162b4f14..3aac5374fcb 100644 --- a/libs/common/src/vault/services/folder/folder.service.ts +++ b/libs/common/src/vault/services/folder/folder.service.ts @@ -1,14 +1,14 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { Observable, firstValueFrom, map, shareReplay } from "rxjs"; +import { Observable, Subject, firstValueFrom, map, shareReplay, switchMap, merge } from "rxjs"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; import { KeyService } from "../../../../../key-management/src/abstractions/key.service"; import { I18nService } from "../../../platform/abstractions/i18n.service"; -import { Utils } from "../../../platform/misc/utils"; import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key"; -import { ActiveUserState, DerivedState, StateProvider } from "../../../platform/state"; +import { StateProvider } from "../../../platform/state"; import { UserId } from "../../../types/guid"; import { UserKey } from "../../../types/key"; import { CipherService } from "../../../vault/abstractions/cipher.service"; @@ -21,11 +21,18 @@ import { FolderWithIdRequest } from "../../models/request/folder-with-id.request import { FOLDER_DECRYPTED_FOLDERS, FOLDER_ENCRYPTED_FOLDERS } from "../key-state/folder.state"; export class FolderService implements InternalFolderServiceAbstraction { - folders$: Observable; - folderViews$: Observable; + /** + * Ensures we reuse the same observable stream for each userId rather than + * creating a new one on each folderViews$ call. + */ + private folderViewCache = new Map>(); - private encryptedFoldersState: ActiveUserState>; - private decryptedFoldersState: DerivedState; + /** + * Used to force the folderviews$ Observable to re-emit with a provided value. + * Required because shareReplay with refCount: false maintains last emission. + * Used during cleanup to force emit empty arrays, ensuring stale data isn't retained. + */ + private forceFolderViews: Record> = {}; constructor( private keyService: KeyService, @@ -33,23 +40,44 @@ export class FolderService implements InternalFolderServiceAbstraction { private i18nService: I18nService, private cipherService: CipherService, private stateProvider: StateProvider, - ) { - this.encryptedFoldersState = this.stateProvider.getActive(FOLDER_ENCRYPTED_FOLDERS); - this.decryptedFoldersState = this.stateProvider.getDerived( - this.encryptedFoldersState.state$, - FOLDER_DECRYPTED_FOLDERS, - { folderService: this, keyService: this.keyService }, - ); + ) {} - this.folders$ = this.encryptedFoldersState.state$.pipe( - map((folderData) => Object.values(folderData).map((f) => new Folder(f))), - ); + folders$(userId: UserId): Observable { + return this.encryptedFoldersState(userId).state$.pipe( + map((folders) => { + if (folders == null) { + return []; + } - this.folderViews$ = this.decryptedFoldersState.state$; + return Object.values(folders).map((f) => new Folder(f)); + }), + ); } - async clearCache(): Promise { - await this.decryptedFoldersState.forceValue([]); + /** + * Returns an Observable of decrypted folder views for the given userId. + * Uses folderViewCache to maintain a single Observable instance per user, + * combining normal folder state updates with forced updates. + */ + folderViews$(userId: UserId): Observable { + if (!this.folderViewCache.has(userId)) { + if (!this.forceFolderViews[userId]) { + this.forceFolderViews[userId] = new Subject(); + } + + const observable = merge( + this.forceFolderViews[userId], + this.encryptedFoldersState(userId).state$.pipe( + switchMap((folderData) => { + return this.decryptFolders(userId, folderData); + }), + ), + ).pipe(shareReplay({ refCount: false, bufferSize: 1 })); + + this.folderViewCache.set(userId, observable); + } + + return this.folderViewCache.get(userId); } // TODO: This should be moved to EncryptService or something @@ -60,29 +88,29 @@ export class FolderService implements InternalFolderServiceAbstraction { return folder; } - async get(id: string): Promise { - const folders = await firstValueFrom(this.folders$); + async get(id: string, userId: UserId): Promise { + const folders = await firstValueFrom(this.folders$(userId)); return folders.find((folder) => folder.id === id); } - getDecrypted$(id: string): Observable { - return this.folderViews$.pipe( + getDecrypted$(id: string, userId: UserId): Observable { + return this.folderViews$(userId).pipe( map((folders) => folders.find((folder) => folder.id === id)), shareReplay({ refCount: true, bufferSize: 1 }), ); } - async getAllFromState(): Promise { - return await firstValueFrom(this.folders$); + async getAllFromState(userId: UserId): Promise { + return await firstValueFrom(this.folders$(userId)); } /** * @deprecated For the CLI only * @param id id of the folder */ - async getFromState(id: string): Promise { - const folder = await this.get(id); + async getFromState(id: string, userId: UserId): Promise { + const folder = await this.get(id, userId); if (!folder) { return null; } @@ -93,12 +121,13 @@ export class FolderService implements InternalFolderServiceAbstraction { /** * @deprecated Only use in CLI! */ - async getAllDecryptedFromState(): Promise { - return await firstValueFrom(this.folderViews$); + async getAllDecryptedFromState(userId: UserId): Promise { + return await firstValueFrom(this.folderViews$(userId)); } - async upsert(folderData: FolderData | FolderData[]): Promise { - await this.encryptedFoldersState.update((folders) => { + async upsert(folderData: FolderData | FolderData[], userId: UserId): Promise { + await this.clearDecryptedFolderState(userId); + await this.encryptedFoldersState(userId).update((folders) => { if (folders == null) { folders = {}; } @@ -120,24 +149,31 @@ export class FolderService implements InternalFolderServiceAbstraction { if (!folders) { return; } - + await this.clearDecryptedFolderState(userId); await this.stateProvider.getUser(userId, FOLDER_ENCRYPTED_FOLDERS).update(() => { const newFolders: Record = { ...folders }; return newFolders; }); } - async clear(userId?: UserId): Promise { + async clearDecryptedFolderState(userId: UserId): Promise { if (userId == null) { - await this.encryptedFoldersState.update(() => ({})); - await this.decryptedFoldersState.forceValue([]); - } else { - await this.stateProvider.getUser(userId, FOLDER_ENCRYPTED_FOLDERS).update(() => ({})); + throw new Error("User ID is required."); } + + await this.setDecryptedFolders([], userId); + } + + async clear(userId: UserId): Promise { + this.forceFolderViews[userId]?.next([]); + + await this.encryptedFoldersState(userId).update(() => ({})); + await this.clearDecryptedFolderState(userId); } - async delete(id: string | string[]): Promise { - await this.encryptedFoldersState.update((folders) => { + async delete(id: string | string[], userId: UserId): Promise { + await this.clearDecryptedFolderState(userId); + await this.encryptedFoldersState(userId).update((folders) => { if (folders == null) { return; } @@ -164,25 +200,11 @@ export class FolderService implements InternalFolderServiceAbstraction { } } if (updates.length > 0) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.cipherService.upsert(updates.map((c) => c.toCipherData())); + await this.cipherService.upsert(updates.map((c) => c.toCipherData())); } } } - async decryptFolders(folders: Folder[]) { - const decryptFolderPromises = folders.map((f) => f.decrypt()); - const decryptedFolders = await Promise.all(decryptFolderPromises); - - decryptedFolders.sort(Utils.getSortFunction(this.i18nService, "name")); - - const noneFolder = new FolderView(); - noneFolder.name = this.i18nService.t("noneFolder"); - decryptedFolders.push(noneFolder); - return decryptedFolders; - } - async getRotatedData( originalUserKey: UserKey, newUserKey: UserKey, @@ -193,7 +215,7 @@ export class FolderService implements InternalFolderServiceAbstraction { } let encryptedFolders: FolderWithIdRequest[] = []; - const folders = await firstValueFrom(this.folderViews$); + const folders = await firstValueFrom(this.folderViews$(userId)); if (!folders) { return encryptedFolders; } @@ -205,4 +227,63 @@ export class FolderService implements InternalFolderServiceAbstraction { ); return encryptedFolders; } + + /** + * Decrypts the folders for a user. + * @param userId the user id + * @param folderData encrypted folders + * @returns a list of decrypted folders + */ + private async decryptFolders( + userId: UserId, + folderData: Record, + ): Promise { + // Check if the decrypted folders are already cached + const decrypted = await firstValueFrom( + this.stateProvider.getUser(userId, FOLDER_DECRYPTED_FOLDERS).state$, + ); + if (decrypted?.length) { + return decrypted; + } + + if (folderData == null) { + return []; + } + + const folders = Object.values(folderData).map((f) => new Folder(f)); + const userKey = await firstValueFrom(this.keyService.userKey$(userId)); + if (!userKey) { + return []; + } + + const decryptFolderPromises = folders.map((f) => + f.decryptWithKey(userKey, this.encryptService), + ); + const decryptedFolders = await Promise.all(decryptFolderPromises); + decryptedFolders.sort(Utils.getSortFunction(this.i18nService, "name")); + + const noneFolder = new FolderView(); + noneFolder.name = this.i18nService.t("noneFolder"); + decryptedFolders.push(noneFolder); + + // Cache the decrypted folders + await this.setDecryptedFolders(decryptedFolders, userId); + return decryptedFolders; + } + + /** + * @returns a SingleUserState for the encrypted folders. + */ + private encryptedFoldersState(userId: UserId) { + return this.stateProvider.getUser(userId, FOLDER_ENCRYPTED_FOLDERS); + } + + /** + * Sets the decrypted folders state for a user. + * @param folders the decrypted folders + * @param userId the user id + */ + private async setDecryptedFolders(folders: FolderView[], userId: UserId): Promise { + await this.stateProvider.setUserState(FOLDER_DECRYPTED_FOLDERS, folders, userId); + } } diff --git a/libs/common/src/vault/services/key-state/folder.state.spec.ts b/libs/common/src/vault/services/key-state/folder.state.spec.ts index ece66b5d451..217a200ea88 100644 --- a/libs/common/src/vault/services/key-state/folder.state.spec.ts +++ b/libs/common/src/vault/services/key-state/folder.state.spec.ts @@ -1,11 +1,3 @@ -import { mock } from "jest-mock-extended"; - -import { KeyService } from "../../../../../key-management/src/abstractions/key.service"; -import { FolderService } from "../../abstractions/folder/folder.service.abstraction"; -import { FolderData } from "../../models/data/folder.data"; -import { Folder } from "../../models/domain/folder"; -import { FolderView } from "../../models/view/folder.view"; - import { FOLDER_DECRYPTED_FOLDERS, FOLDER_ENCRYPTED_FOLDERS } from "./folder.state"; describe("encrypted folders", () => { @@ -31,48 +23,32 @@ describe("encrypted folders", () => { }); describe("derived decrypted folders", () => { - const keyService = mock(); - const folderService = mock(); const sut = FOLDER_DECRYPTED_FOLDERS; - let data: FolderData; - - beforeEach(() => { - data = { - id: "id", - name: "encName", - revisionDate: "2024-01-31T12:00:00.000Z", - }; - }); - afterEach(() => { - jest.resetAllMocks(); - }); - - it("should deserialize encrypted folders", async () => { - const inputObj = [data]; + it("should deserialize decrypted folders", async () => { + const inputObj = [ + { + id: "id", + name: "encName", + revisionDate: "2024-01-31T12:00:00.000Z", + }, + ]; - const expectedFolderView = { - id: "id", - name: "encName", - revisionDate: new Date("2024-01-31T12:00:00.000Z"), - }; + const expectedFolderView = [ + { + id: "id", + name: "encName", + revisionDate: new Date("2024-01-31T12:00:00.000Z"), + }, + ]; - const result = sut.deserialize(JSON.parse(JSON.stringify(inputObj))); + const result = sut.deserializer(JSON.parse(JSON.stringify(inputObj))); - expect(result).toEqual([expectedFolderView]); + expect(result).toEqual(expectedFolderView); }); - it("should derive encrypted folders", async () => { - const folderViewMock = new FolderView(new Folder(data)); - keyService.hasUserKey.mockResolvedValue(true); - folderService.decryptFolders.mockResolvedValue([folderViewMock]); - - const encryptedFoldersState = { id: data }; - const derivedStateResult = await sut.derive(encryptedFoldersState, { - folderService, - keyService, - }); - - expect(derivedStateResult).toEqual([folderViewMock]); + it("should handle null input", async () => { + const result = sut.deserializer(null); + expect(result).toEqual([]); }); }); diff --git a/libs/common/src/vault/services/key-state/folder.state.ts b/libs/common/src/vault/services/key-state/folder.state.ts index 7262d72d58e..99ad8e5ae35 100644 --- a/libs/common/src/vault/services/key-state/folder.state.ts +++ b/libs/common/src/vault/services/key-state/folder.state.ts @@ -1,34 +1,23 @@ import { Jsonify } from "type-fest"; -import { KeyService } from "../../../../../key-management/src/abstractions/key.service"; -import { DeriveDefinition, FOLDER_DISK, UserKeyDefinition } from "../../../platform/state"; -import { FolderService } from "../../abstractions/folder/folder.service.abstraction"; +import { FOLDER_DISK, FOLDER_MEMORY, UserKeyDefinition } from "../../../platform/state"; import { FolderData } from "../../models/data/folder.data"; -import { Folder } from "../../models/domain/folder"; import { FolderView } from "../../models/view/folder.view"; export const FOLDER_ENCRYPTED_FOLDERS = UserKeyDefinition.record( FOLDER_DISK, - "folders", + "folder", { deserializer: (obj: Jsonify) => FolderData.fromJSON(obj), clearOn: ["logout"], }, ); -export const FOLDER_DECRYPTED_FOLDERS = DeriveDefinition.from< - Record, - FolderView[], - { folderService: FolderService; keyService: KeyService } ->(FOLDER_ENCRYPTED_FOLDERS, { - deserializer: (obj) => obj.map((f) => FolderView.fromJSON(f)), - derive: async (from, { folderService, keyService }) => { - const folders = Object.values(from || {}).map((f) => new Folder(f)); - - if (await keyService.hasUserKey()) { - return await folderService.decryptFolders(folders); - } else { - return []; - } +export const FOLDER_DECRYPTED_FOLDERS = new UserKeyDefinition( + FOLDER_MEMORY, + "decryptedFolders", + { + deserializer: (obj: Jsonify) => obj?.map((f) => FolderView.fromJSON(f)) ?? [], + clearOn: ["logout", "lock"], }, -}); +); diff --git a/libs/importer/src/components/import.component.ts b/libs/importer/src/components/import.component.ts index 0035fbdf10d..f2bf7471d44 100644 --- a/libs/importer/src/components/import.component.ts +++ b/libs/importer/src/components/import.component.ts @@ -16,7 +16,7 @@ import { import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms"; import * as JSZip from "jszip"; import { concat, Observable, Subject, lastValueFrom, combineLatest, firstValueFrom } from "rxjs"; -import { filter, map, takeUntil } from "rxjs/operators"; +import { filter, map, switchMap, takeUntil } from "rxjs/operators"; import { CollectionService, CollectionView } from "@bitwarden/admin-console/common"; import { JslibModule } from "@bitwarden/angular/jslib.module"; @@ -153,6 +153,8 @@ export class ImportComponent implements OnInit, OnDestroy, AfterViewInit { private _importBlockedByPolicy = false; protected isFromAC = false; + private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id)); + formGroup = this.formBuilder.group({ vaultSelector: [ "myVault", @@ -206,6 +208,7 @@ export class ImportComponent implements OnInit, OnDestroy, AfterViewInit { @Optional() protected importCollectionService: ImportCollectionServiceAbstraction, protected toastService: ToastService, + protected accountService: AccountService, ) {} protected get importBlockedByPolicy(): boolean { @@ -257,7 +260,10 @@ export class ImportComponent implements OnInit, OnDestroy, AfterViewInit { private handleImportInit() { // Filter out the no folder-item from folderViews$ - this.folders$ = this.folderService.folderViews$.pipe( + this.folders$ = this.activeUserId$.pipe( + switchMap((userId) => { + return this.folderService.folderViews$(userId); + }), map((folders) => folders.filter((f) => f.id != null)), ); diff --git a/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.spec.ts b/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.spec.ts index 9d58bcbf559..069df8606bf 100644 --- a/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.spec.ts +++ b/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.spec.ts @@ -1,5 +1,5 @@ import { mock, MockProxy } from "jest-mock-extended"; -import { BehaviorSubject } from "rxjs"; +import { BehaviorSubject, of } from "rxjs"; import { PinServiceAbstraction } from "@bitwarden/auth/common"; import { AccountInfo, AccountService } from "@bitwarden/common/auth/abstractions/account.service"; @@ -178,8 +178,8 @@ describe("VaultExportService", () => { const activeAccount = { id: userId, ...accountInfo }; accountService.activeAccount$ = new BehaviorSubject(activeAccount); - folderService.getAllDecryptedFromState.mockResolvedValue(UserFolderViews); - folderService.getAllFromState.mockResolvedValue(UserFolders); + folderService.folderViews$.mockReturnValue(of(UserFolderViews)); + folderService.folders$.mockReturnValue(of(UserFolders)); kdfConfigService.getKdfConfig.mockResolvedValue(DEFAULT_KDF_CONFIG); encryptService.encrypt.mockResolvedValue(new EncString("encrypted")); @@ -295,7 +295,7 @@ describe("VaultExportService", () => { it("exported unencrypted object contains folders", async () => { cipherService.getAllDecrypted.mockResolvedValue(UserCipherViews.slice(0, 1)); - await folderService.getAllDecryptedFromState(); + folderService.folderViews$.mockReturnValue(of(UserFolderViews)); const actual = await exportService.getExport("json"); expectEqualFolderViews(UserFolderViews, actual); @@ -303,7 +303,7 @@ describe("VaultExportService", () => { it("exported encrypted json contains folders", async () => { cipherService.getAll.mockResolvedValue(UserCipherDomains.slice(0, 1)); - await folderService.getAllFromState(); + folderService.folders$.mockReturnValue(of(UserFolders)); const actual = await exportService.getExport("encrypted_json"); expectEqualFolders(UserFolders, actual); diff --git a/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.ts b/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.ts index dbd2024d3ad..f9df9c7057f 100644 --- a/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.ts +++ b/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.ts @@ -32,6 +32,8 @@ export class IndividualVaultExportService extends BaseVaultExportService implements IndividualVaultExportServiceAbstraction { + private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id)); + constructor( private folderService: FolderService, private cipherService: CipherService, @@ -61,9 +63,10 @@ export class IndividualVaultExportService let decFolders: FolderView[] = []; let decCiphers: CipherView[] = []; const promises = []; + const activeUserId = await firstValueFrom(this.activeUserId$); promises.push( - this.folderService.getAllDecryptedFromState().then((folders) => { + firstValueFrom(this.folderService.folderViews$(activeUserId)).then((folders) => { decFolders = folders; }), ); @@ -87,9 +90,10 @@ export class IndividualVaultExportService let folders: Folder[] = []; let ciphers: Cipher[] = []; const promises = []; + const activeUserId = await firstValueFrom(this.activeUserId$); promises.push( - this.folderService.getAllFromState().then((f) => { + firstValueFrom(this.folderService.folders$(activeUserId)).then((f) => { folders = f; }), ); @@ -102,10 +106,9 @@ export class IndividualVaultExportService await Promise.all(promises); - const activeUserId = await firstValueFrom( - this.accountService.activeAccount$.pipe(map((a) => a?.id)), + const userKey = await this.keyService.getUserKeyWithLegacySupport( + await firstValueFrom(this.activeUserId$), ); - const userKey = await this.keyService.getUserKeyWithLegacySupport(activeUserId); const encKeyValidation = await this.encryptService.encrypt(Utils.newGuid(), userKey); const jsonDoc: BitwardenEncryptedIndividualJsonExport = { diff --git a/libs/tools/export/vault-export/vault-export-core/src/services/vault-export.service.spec.ts b/libs/tools/export/vault-export/vault-export-core/src/services/vault-export.service.spec.ts index 6bf05796070..7e04972f8ab 100644 --- a/libs/tools/export/vault-export/vault-export-core/src/services/vault-export.service.spec.ts +++ b/libs/tools/export/vault-export/vault-export-core/src/services/vault-export.service.spec.ts @@ -1,5 +1,5 @@ import { mock, MockProxy } from "jest-mock-extended"; -import { BehaviorSubject } from "rxjs"; +import { BehaviorSubject, of } from "rxjs"; import { PinServiceAbstraction } from "@bitwarden/auth/common"; import { AccountInfo, AccountService } from "@bitwarden/common/auth/abstractions/account.service"; @@ -168,8 +168,8 @@ describe("VaultExportService", () => { kdfConfigService = mock(); - folderService.getAllDecryptedFromState.mockResolvedValue(UserFolderViews); - folderService.getAllFromState.mockResolvedValue(UserFolders); + folderService.folderViews$.mockReturnValue(of(UserFolderViews)); + folderService.folders$.mockReturnValue(of(UserFolders)); kdfConfigService.getKdfConfig.mockResolvedValue(DEFAULT_KDF_CONFIG); encryptService.encrypt.mockResolvedValue(new EncString("encrypted")); keyService.userKey$.mockReturnValue(new BehaviorSubject("mockOriginalUserKey" as any)); @@ -294,7 +294,7 @@ describe("VaultExportService", () => { it("exported unencrypted object contains folders", async () => { cipherService.getAllDecrypted.mockResolvedValue(UserCipherViews.slice(0, 1)); - await folderService.getAllDecryptedFromState(); + const actual = await exportService.getExport("json"); expectEqualFolderViews(UserFolderViews, actual); @@ -302,7 +302,7 @@ describe("VaultExportService", () => { it("exported encrypted json contains folders", async () => { cipherService.getAll.mockResolvedValue(UserCipherDomains.slice(0, 1)); - await folderService.getAllFromState(); + const actual = await exportService.getExport("encrypted_json"); expectEqualFolders(UserFolders, actual); diff --git a/libs/vault/src/cipher-form/services/default-cipher-form-config.service.ts b/libs/vault/src/cipher-form/services/default-cipher-form-config.service.ts index c2fbd024aa8..93a53345d3a 100644 --- a/libs/vault/src/cipher-form/services/default-cipher-form-config.service.ts +++ b/libs/vault/src/cipher-form/services/default-cipher-form-config.service.ts @@ -7,6 +7,7 @@ import { CollectionService } from "@bitwarden/admin-console/common"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { OrganizationUserStatusType, PolicyType } from "@bitwarden/common/admin-console/enums"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { CipherId } 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"; @@ -31,12 +32,17 @@ export class DefaultCipherFormConfigService implements CipherFormConfigService { private cipherService: CipherService = inject(CipherService); private folderService: FolderService = inject(FolderService); private collectionService: CollectionService = inject(CollectionService); + private accountService = inject(AccountService); + + private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id)); async buildConfig( mode: CipherFormMode, cipherId?: CipherId, cipherType?: CipherType, ): Promise { + const activeUserId = await firstValueFrom(this.activeUserId$); + const [organizations, collections, allowPersonalOwnership, folders, cipher] = await firstValueFrom( combineLatest([ @@ -49,9 +55,9 @@ export class DefaultCipherFormConfigService implements CipherFormConfigService { ), ), this.allowPersonalOwnership$, - this.folderService.folders$.pipe( + this.folderService.folders$(activeUserId).pipe( switchMap((f) => - this.folderService.folderViews$.pipe( + this.folderService.folderViews$(activeUserId).pipe( filter((d) => d.length - 1 === f.length), // -1 for "No Folder" in folderViews$ ), ), diff --git a/libs/vault/src/cipher-view/cipher-view.component.ts b/libs/vault/src/cipher-view/cipher-view.component.ts index 57af96258fa..4bd87a7869d 100644 --- a/libs/vault/src/cipher-view/cipher-view.component.ts +++ b/libs/vault/src/cipher-view/cipher-view.component.ts @@ -1,11 +1,12 @@ import { CommonModule } from "@angular/common"; import { Component, Input, OnChanges, OnDestroy } from "@angular/core"; -import { firstValueFrom, Observable, Subject, takeUntil } from "rxjs"; +import { firstValueFrom, map, Observable, Subject, takeUntil } from "rxjs"; import { CollectionService, CollectionView } from "@bitwarden/admin-console/common"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { isCardExpired } from "@bitwarden/common/autofill/utils"; import { CollectionId } from "@bitwarden/common/types/guid"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; @@ -48,6 +49,8 @@ import { ViewIdentitySectionsComponent } from "./view-identity-sections/view-ide export class CipherViewComponent implements OnChanges, OnDestroy { @Input({ required: true }) cipher: CipherView | null = null; + private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id)); + /** * Optional list of collections the cipher is assigned to. If none are provided, they will be fetched using the * `CipherService` and the `collectionIds` property of the cipher. @@ -66,6 +69,7 @@ export class CipherViewComponent implements OnChanges, OnDestroy { private organizationService: OrganizationService, private collectionService: CollectionService, private folderService: FolderService, + private accountService: AccountService, ) {} async ngOnChanges() { @@ -136,8 +140,14 @@ export class CipherViewComponent implements OnChanges, OnDestroy { } if (this.cipher.folderId) { + const activeUserId = await firstValueFrom(this.activeUserId$); + + if (!activeUserId) { + return; + } + this.folder$ = this.folderService - .getDecrypted$(this.cipher.folderId) + .getDecrypted$(this.cipher.folderId, activeUserId) .pipe(takeUntil(this.destroyed$)); } } From 9807b33181172c59ac5d52a3189f3d82c40147c3 Mon Sep 17 00:00:00 2001 From: Evan Bassler Date: Thu, 2 Jan 2025 16:17:47 -0600 Subject: [PATCH 68/75] add field type to show correct new cipher popup form (#12433) Co-authored-by: Evan Bassler --- apps/browser/src/autofill/background/overlay.background.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/browser/src/autofill/background/overlay.background.ts b/apps/browser/src/autofill/background/overlay.background.ts index fd16bfcf16a..8b577ccccf5 100644 --- a/apps/browser/src/autofill/background/overlay.background.ts +++ b/apps/browser/src/autofill/background/overlay.background.ts @@ -2275,6 +2275,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { card, identity, sender, + addNewCipherType, }: CurrentAddNewItemData) { const cipherView: CipherView = this.buildNewVaultItemCipherView({ login, @@ -2294,7 +2295,10 @@ export class OverlayBackground implements OverlayBackgroundInterface { collectionIds: cipherView.collectionIds, }); - await this.openAddEditVaultItemPopout(sender.tab, { cipherId: cipherView.id }); + await this.openAddEditVaultItemPopout(sender.tab, { + cipherId: cipherView.id, + cipherType: addNewCipherType, + }); await BrowserApi.sendMessage("inlineAutofillMenuRefreshAddEditCipher"); } catch (error) { this.logService.error("Error building cipher and opening add/edit vault item popout", error); From b370787239415e50b50b000b933d408f0493c4b7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 2 Jan 2025 18:24:57 -0500 Subject: [PATCH 69/75] [deps] BRE: Update gh minor (#11941) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build-browser.yml | 6 +- .github/workflows/build-cli.yml | 16 ++-- .github/workflows/build-desktop.yml | 82 ++++++++++----------- .github/workflows/build-web.yml | 8 +- .github/workflows/chromatic.yml | 4 +- .github/workflows/crowdin-pull.yml | 2 +- .github/workflows/release-desktop-beta.yml | 60 +++++++-------- .github/workflows/repository-management.yml | 6 +- .github/workflows/scan.yml | 4 +- .github/workflows/version-auto-bump.yml | 2 +- 10 files changed, 95 insertions(+), 95 deletions(-) diff --git a/.github/workflows/build-browser.yml b/.github/workflows/build-browser.yml index 68d1f10a51a..aa62d602ad8 100644 --- a/.github/workflows/build-browser.yml +++ b/.github/workflows/build-browser.yml @@ -169,7 +169,7 @@ jobs: zip -r browser-source.zip browser-source - name: Upload browser source - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: browser-source-${{ env._BUILD_NUMBER }}.zip path: browser-source.zip @@ -272,7 +272,7 @@ jobs: working-directory: browser-source/apps/browser - name: Upload extension artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: ${{ matrix.artifact_name }}-${{ env._BUILD_NUMBER }}.zip path: browser-source/apps/browser/dist/${{ matrix.archive_name }} @@ -409,7 +409,7 @@ jobs: ls -la - name: Upload Safari artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: dist-safari-${{ env._BUILD_NUMBER }}.zip path: apps/browser/dist/dist-safari.zip diff --git a/.github/workflows/build-cli.yml b/.github/workflows/build-cli.yml index d480879fb15..02432e0a5f4 100644 --- a/.github/workflows/build-cli.yml +++ b/.github/workflows/build-cli.yml @@ -163,14 +163,14 @@ jobs: matrix.license_type.artifact_prefix }}-${{ env.LOWER_RUNNER_OS }}-sha256-${{ env._PACKAGE_VERSION }}.txt - name: Upload unix zip asset - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: bw${{ matrix.license_type.artifact_prefix }}-${{ env.LOWER_RUNNER_OS }}-${{ env._PACKAGE_VERSION }}.zip path: apps/cli/dist/bw${{ matrix.license_type.artifact_prefix }}-${{ env.LOWER_RUNNER_OS }}-${{ env._PACKAGE_VERSION }}.zip if-no-files-found: error - name: Upload unix checksum asset - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: bw${{ matrix.license_type.artifact_prefix }}-${{ env.LOWER_RUNNER_OS }}-sha256-${{ env._PACKAGE_VERSION }}.txt path: apps/cli/dist/bw${{ matrix.license_type.artifact_prefix }}-${{ env.LOWER_RUNNER_OS }}-sha256-${{ env._PACKAGE_VERSION }}.txt @@ -324,14 +324,14 @@ jobs: -t sha256 | Out-File -Encoding ASCII ./dist/bw${{ matrix.license_type.artifact_prefix }}-windows-sha256-${env:_PACKAGE_VERSION}.txt - name: Upload windows zip asset - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: bw${{ matrix.license_type.artifact_prefix }}-windows-${{ env._PACKAGE_VERSION }}.zip path: apps/cli/dist/bw${{ matrix.license_type.artifact_prefix }}-windows-${{ env._PACKAGE_VERSION }}.zip if-no-files-found: error - name: Upload windows checksum asset - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: bw${{ matrix.license_type.artifact_prefix }}-windows-sha256-${{ env._PACKAGE_VERSION }}.txt path: apps/cli/dist/bw${{ matrix.license_type.artifact_prefix }}-windows-sha256-${{ env._PACKAGE_VERSION }}.txt @@ -339,7 +339,7 @@ jobs: - name: Upload Chocolatey asset if: matrix.license_type.build_prefix == 'bit' - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: bitwarden-cli.${{ env._PACKAGE_VERSION }}.nupkg path: apps/cli/dist/chocolatey/bitwarden-cli.${{ env._PACKAGE_VERSION }}.nupkg @@ -350,7 +350,7 @@ jobs: - name: Upload NPM Build Directory asset if: matrix.license_type.build_prefix == 'bit' - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: bitwarden-cli-${{ env._PACKAGE_VERSION }}-npm-build.zip path: apps/cli/bitwarden-cli-${{ env._PACKAGE_VERSION }}-npm-build.zip @@ -421,14 +421,14 @@ jobs: run: sudo snap remove bw - name: Upload snap asset - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: bw_${{ env._PACKAGE_VERSION }}_amd64.snap path: apps/cli/dist/snap/bw_${{ env._PACKAGE_VERSION }}_amd64.snap if-no-files-found: error - name: Upload snap checksum asset - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: bw-snap-sha256-${{ env._PACKAGE_VERSION }}.txt path: apps/cli/dist/snap/bw-snap-sha256-${{ env._PACKAGE_VERSION }}.txt diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml index 2a59dc28fc9..63453b93838 100644 --- a/.github/workflows/build-desktop.yml +++ b/.github/workflows/build-desktop.yml @@ -207,7 +207,7 @@ jobs: npm link ../sdk-internal - name: Cache Native Module - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 id: cache with: path: | @@ -232,42 +232,42 @@ jobs: run: npm run dist:lin - name: Upload .deb artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-amd64.deb path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-amd64.deb if-no-files-found: error - name: Upload .rpm artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.rpm path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.rpm if-no-files-found: error - name: Upload .freebsd artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x64.freebsd path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x64.freebsd if-no-files-found: error - name: Upload .snap artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: bitwarden_${{ env._PACKAGE_VERSION }}_amd64.snap path: apps/desktop/dist/bitwarden_${{ env._PACKAGE_VERSION }}_amd64.snap if-no-files-found: error - name: Upload .AppImage artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.AppImage path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.AppImage if-no-files-found: error - name: Upload auto-update artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: ${{ needs.setup.outputs.release_channel }}-linux.yml path: apps/desktop/dist/${{ needs.setup.outputs.release_channel }}-linux.yml @@ -280,7 +280,7 @@ jobs: sudo npm run pack:lin:flatpak - name: Upload flatpak artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: com.bitwarden.desktop.flatpak path: apps/desktop/dist/com.bitwarden.desktop.flatpak @@ -373,7 +373,7 @@ jobs: npm link ../sdk-internal - name: Cache Native Module - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 id: cache with: path: | @@ -428,91 +428,91 @@ jobs: -NewName bitwarden-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z - name: Upload portable exe artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-Portable-${{ env._PACKAGE_VERSION }}.exe path: apps/desktop/dist/Bitwarden-Portable-${{ env._PACKAGE_VERSION }}.exe if-no-files-found: error - name: Upload installer exe artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-Installer-${{ env._PACKAGE_VERSION }}.exe path: apps/desktop/dist/nsis-web/Bitwarden-Installer-${{ env._PACKAGE_VERSION }}.exe if-no-files-found: error - name: Upload appx ia32 artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-ia32.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-ia32.appx if-no-files-found: error - name: Upload store appx ia32 artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-ia32-store.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-ia32-store.appx if-no-files-found: error - name: Upload NSIS ia32 artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: bitwarden-${{ env._PACKAGE_VERSION }}-ia32.nsis.7z path: apps/desktop/dist/nsis-web/bitwarden-${{ env._PACKAGE_VERSION }}-ia32.nsis.7z if-no-files-found: error - name: Upload appx x64 artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x64.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x64.appx if-no-files-found: error - name: Upload store appx x64 artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x64-store.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x64-store.appx if-no-files-found: error - name: Upload NSIS x64 artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: bitwarden-${{ env._PACKAGE_VERSION }}-x64.nsis.7z path: apps/desktop/dist/nsis-web/bitwarden-${{ env._PACKAGE_VERSION }}-x64.nsis.7z if-no-files-found: error - name: Upload appx ARM64 artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-arm64.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-arm64.appx if-no-files-found: error - name: Upload store appx ARM64 artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-arm64-store.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-arm64-store.appx if-no-files-found: error - name: Upload NSIS ARM64 artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: bitwarden-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z path: apps/desktop/dist/nsis-web/bitwarden-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z if-no-files-found: error - name: Upload nupkg artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: bitwarden.${{ env._PACKAGE_VERSION }}.nupkg path: apps/desktop/dist/chocolatey/bitwarden.${{ env._PACKAGE_VERSION }}.nupkg if-no-files-found: error - name: Upload auto-update artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: ${{ needs.setup.outputs.release_channel }}.yml path: apps/desktop/dist/nsis-web/${{ needs.setup.outputs.release_channel }}.yml @@ -561,14 +561,14 @@ jobs: - name: Cache Build id: build-cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 with: path: apps/desktop/build key: ${{ runner.os }}-${{ github.run_id }}-build - name: Cache Safari id: safari-cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 with: path: apps/browser/dist/Safari key: ${{ runner.os }}-${{ github.run_id }}-safari-extension @@ -681,7 +681,7 @@ jobs: npm link ../sdk-internal - name: Cache Native Module - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 id: cache with: path: | @@ -749,14 +749,14 @@ jobs: - name: Get Build Cache id: build-cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 with: path: apps/desktop/build key: ${{ runner.os }}-${{ github.run_id }}-build - name: Setup Safari Cache id: safari-cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 with: path: apps/browser/dist/Safari key: ${{ runner.os }}-${{ github.run_id }}-safari-extension @@ -869,7 +869,7 @@ jobs: npm link ../sdk-internal - name: Cache Native Module - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 id: cache with: path: | @@ -918,28 +918,28 @@ jobs: run: npm run pack:mac - name: Upload .zip artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal-mac.zip path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-universal-mac.zip if-no-files-found: error - name: Upload .dmg artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg if-no-files-found: error - name: Upload .dmg blockmap artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg.blockmap path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg.blockmap if-no-files-found: error - name: Upload auto-update artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: ${{ needs.setup.outputs.release_channel }}-mac.yml path: apps/desktop/dist/${{ needs.setup.outputs.release_channel }}-mac.yml @@ -990,14 +990,14 @@ jobs: - name: Get Build Cache id: build-cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 with: path: apps/desktop/build key: ${{ runner.os }}-${{ github.run_id }}-build - name: Setup Safari Cache id: safari-cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 with: path: apps/browser/dist/Safari key: ${{ runner.os }}-${{ github.run_id }}-safari-extension @@ -1117,7 +1117,7 @@ jobs: npm link ../sdk-internal - name: Cache Native Module - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 id: cache with: path: | @@ -1166,7 +1166,7 @@ jobs: run: npm run pack:mac:mas - name: Upload .pkg artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal.pkg path: apps/desktop/dist/mas-universal/Bitwarden-${{ env._PACKAGE_VERSION }}-universal.pkg @@ -1193,7 +1193,7 @@ jobs: if: | github.event_name != 'pull_request_target' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc-desktop') - uses: slackapi/slack-github-action@37ebaef184d7626c5f204ab8d3baff4262dd30f0 # v1.27.0 + uses: slackapi/slack-github-action@fcfb566f8b0aab22203f066d80ca1d7e4b5d05b3 # v1.27.1 with: channel-id: C074F5UESQ0 payload: | @@ -1252,14 +1252,14 @@ jobs: - name: Get Build Cache id: build-cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 with: path: apps/desktop/build key: ${{ runner.os }}-${{ github.run_id }}-build - name: Setup Safari Cache id: safari-cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 with: path: apps/browser/dist/Safari key: ${{ runner.os }}-${{ github.run_id }}-safari-extension @@ -1372,7 +1372,7 @@ jobs: npm link ../sdk-internal - name: Cache Native Module - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 id: cache with: path: | @@ -1424,7 +1424,7 @@ jobs: zip -r Bitwarden-${{ env._PACKAGE_VERSION }}-masdev-universal.zip Bitwarden.app - name: Upload masdev artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-masdev-universal.zip path: apps/desktop/dist/mas-dev-universal/Bitwarden-${{ env._PACKAGE_VERSION }}-masdev-universal.zip diff --git a/.github/workflows/build-web.yml b/.github/workflows/build-web.yml index c686b46d51a..73ae0e14962 100644 --- a/.github/workflows/build-web.yml +++ b/.github/workflows/build-web.yml @@ -164,7 +164,7 @@ jobs: run: zip -r web-${{ env._VERSION }}-${{ matrix.name }}.zip build - name: Upload ${{ matrix.name }} artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: web-${{ env._VERSION }}-${{ matrix.name }}.zip path: apps/web/web-${{ env._VERSION }}-${{ matrix.name }}.zip @@ -274,7 +274,7 @@ jobs: - name: Build Docker image id: build-docker - uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 # v6.9.0 + uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 # v6.10.0 with: context: apps/web file: apps/web/Dockerfile @@ -303,14 +303,14 @@ jobs: - name: Scan Docker image id: container-scan - uses: anchore/scan-action@5ed195cc06065322983cae4bb31e2a751feb86fd # v5.2.0 + uses: anchore/scan-action@869c549e657a088dc0441b08ce4fc0ecdac2bb65 # v5.3.0 with: image: ${{ steps.image-name.outputs.name }} fail-build: false output-format: sarif - name: Upload Grype results to GitHub - uses: github/codeql-action/upload-sarif@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0 + uses: github/codeql-action/upload-sarif@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # v3.28.0 with: sarif_file: ${{ steps.container-scan.outputs.sarif }} diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml index 0efd9d22f17..a5ebd363f63 100644 --- a/.github/workflows/chromatic.yml +++ b/.github/workflows/chromatic.yml @@ -43,7 +43,7 @@ jobs: - name: Cache NPM id: npm-cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 with: path: "~/.npm" key: ${{ runner.os }}-npm-chromatic-${{ hashFiles('**/package-lock.json') }} @@ -56,7 +56,7 @@ jobs: run: npm run build-storybook:ci - name: Publish to Chromatic - uses: chromaui/action@dd2eecb9bef44f54774581f4163b0327fd8cf607 # v11.16.3 + uses: chromaui/action@64a9c0ca3bfb724389b0d536e544f56b7b5ff5b3 # v11.20.2 with: token: ${{ secrets.GITHUB_TOKEN }} projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} diff --git a/.github/workflows/crowdin-pull.yml b/.github/workflows/crowdin-pull.yml index 1ac6dd9113f..027a2f11e55 100644 --- a/.github/workflows/crowdin-pull.yml +++ b/.github/workflows/crowdin-pull.yml @@ -22,7 +22,7 @@ jobs: crowdin_project_id: "308189" steps: - name: Generate GH App token - uses: actions/create-github-app-token@5d869da34e18e7287c1daad50e0b8ea0f506ce69 # v1.11.0 + uses: actions/create-github-app-token@c1a285145b9d317df6ced56c09f525b5c2b6f755 # v1.11.1 id: app-token with: app-id: ${{ secrets.BW_GHAPP_ID }} diff --git a/.github/workflows/release-desktop-beta.yml b/.github/workflows/release-desktop-beta.yml index a940ce289ff..3ec11c77852 100644 --- a/.github/workflows/release-desktop-beta.yml +++ b/.github/workflows/release-desktop-beta.yml @@ -158,42 +158,42 @@ jobs: run: npm run dist:lin - name: Upload .deb artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-amd64.deb path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-amd64.deb if-no-files-found: error - name: Upload .rpm artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.rpm path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.rpm if-no-files-found: error - name: Upload .freebsd artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x64.freebsd path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x64.freebsd if-no-files-found: error - name: Upload .snap artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: bitwarden_${{ env._PACKAGE_VERSION }}_amd64.snap path: apps/desktop/dist/bitwarden_${{ env._PACKAGE_VERSION }}_amd64.snap if-no-files-found: error - name: Upload .AppImage artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.AppImage path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.AppImage if-no-files-found: error - name: Upload auto-update artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: ${{ needs.setup.outputs.release-channel }}-linux.yml path: apps/desktop/dist/${{ needs.setup.outputs.release-channel }}-linux.yml @@ -299,91 +299,91 @@ jobs: -NewName bitwarden-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z - name: Upload portable exe artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-Portable-${{ env._PACKAGE_VERSION }}.exe path: apps/desktop/dist/Bitwarden-Portable-${{ env._PACKAGE_VERSION }}.exe if-no-files-found: error - name: Upload installer exe artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-Installer-${{ env._PACKAGE_VERSION }}.exe path: apps/desktop/dist/nsis-web/Bitwarden-Installer-${{ env._PACKAGE_VERSION }}.exe if-no-files-found: error - name: Upload appx ia32 artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-ia32.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-ia32.appx if-no-files-found: error - name: Upload store appx ia32 artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-ia32-store.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-ia32-store.appx if-no-files-found: error - name: Upload NSIS ia32 artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: bitwarden-${{ env._PACKAGE_VERSION }}-ia32.nsis.7z path: apps/desktop/dist/nsis-web/bitwarden-${{ env._PACKAGE_VERSION }}-ia32.nsis.7z if-no-files-found: error - name: Upload appx x64 artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x64.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x64.appx if-no-files-found: error - name: Upload store appx x64 artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x64-store.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x64-store.appx if-no-files-found: error - name: Upload NSIS x64 artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: bitwarden-${{ env._PACKAGE_VERSION }}-x64.nsis.7z path: apps/desktop/dist/nsis-web/bitwarden-${{ env._PACKAGE_VERSION }}-x64.nsis.7z if-no-files-found: error - name: Upload appx ARM64 artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-arm64.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-arm64.appx if-no-files-found: error - name: Upload store appx ARM64 artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-arm64-store.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-arm64-store.appx if-no-files-found: error - name: Upload NSIS ARM64 artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: bitwarden-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z path: apps/desktop/dist/nsis-web/bitwarden-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z if-no-files-found: error - name: Upload nupkg artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: bitwarden.${{ env._PACKAGE_VERSION }}.nupkg path: apps/desktop/dist/chocolatey/bitwarden.${{ env._PACKAGE_VERSION }}.nupkg if-no-files-found: error - name: Upload auto-update artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: ${{ needs.setup.outputs.release-channel }}.yml path: apps/desktop/dist/nsis-web/${{ needs.setup.outputs.release-channel }}.yml @@ -426,14 +426,14 @@ jobs: - name: Cache Build id: build-cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 with: path: apps/desktop/build key: ${{ runner.os }}-${{ github.run_id }}-build - name: Cache Safari id: safari-cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 with: path: apps/browser/dist/Safari key: ${{ runner.os }}-${{ github.run_id }}-safari-extension @@ -560,14 +560,14 @@ jobs: - name: Get Build Cache id: build-cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 with: path: apps/desktop/build key: ${{ runner.os }}-${{ github.run_id }}-build - name: Setup Safari Cache id: safari-cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 with: path: apps/browser/dist/Safari key: ${{ runner.os }}-${{ github.run_id }}-safari-extension @@ -707,28 +707,28 @@ jobs: run: npm run pack:mac - name: Upload .zip artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal-mac.zip path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-universal-mac.zip if-no-files-found: error - name: Upload .dmg artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg if-no-files-found: error - name: Upload .dmg blockmap artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg.blockmap path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg.blockmap if-no-files-found: error - name: Upload auto-update artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: ${{ needs.setup.outputs.release-channel }}-mac.yml path: apps/desktop/dist/${{ needs.setup.outputs.release-channel }}-mac.yml @@ -773,14 +773,14 @@ jobs: - name: Get Build Cache id: build-cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 with: path: apps/desktop/build key: ${{ runner.os }}-${{ github.run_id }}-build - name: Setup Safari Cache id: safari-cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 with: path: apps/browser/dist/Safari key: ${{ runner.os }}-${{ github.run_id }}-safari-extension @@ -915,7 +915,7 @@ jobs: APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} - name: Upload .pkg artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal.pkg path: apps/desktop/dist/mas-universal/Bitwarden-${{ env._PACKAGE_VERSION }}-universal.pkg diff --git a/.github/workflows/repository-management.yml b/.github/workflows/repository-management.yml index aeafe17e5e0..a914a2c4a7a 100644 --- a/.github/workflows/repository-management.yml +++ b/.github/workflows/repository-management.yml @@ -66,7 +66,7 @@ jobs: runs-on: ubuntu-24.04 steps: - name: Generate GH App token - uses: actions/create-github-app-token@5d869da34e18e7287c1daad50e0b8ea0f506ce69 # v1.11.0 + uses: actions/create-github-app-token@c1a285145b9d317df6ced56c09f525b5c2b6f755 # v1.11.1 id: app-token with: app-id: ${{ secrets.BW_GHAPP_ID }} @@ -115,7 +115,7 @@ jobs: version: ${{ inputs.version_number_override }} - name: Generate GH App token - uses: actions/create-github-app-token@5d869da34e18e7287c1daad50e0b8ea0f506ce69 # v1.11.0 + uses: actions/create-github-app-token@c1a285145b9d317df6ced56c09f525b5c2b6f755 # v1.11.1 id: app-token with: app-id: ${{ secrets.BW_GHAPP_ID }} @@ -452,7 +452,7 @@ jobs: - setup steps: - name: Generate GH App token - uses: actions/create-github-app-token@5d869da34e18e7287c1daad50e0b8ea0f506ce69 # v1.11.0 + uses: actions/create-github-app-token@c1a285145b9d317df6ced56c09f525b5c2b6f755 # v1.11.1 id: app-token with: app-id: ${{ secrets.BW_GHAPP_ID }} diff --git a/.github/workflows/scan.yml b/.github/workflows/scan.yml index 6166ac79b1a..b0874b38cbf 100644 --- a/.github/workflows/scan.yml +++ b/.github/workflows/scan.yml @@ -31,7 +31,7 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} - name: Scan with Checkmarx - uses: checkmarx/ast-github-action@f0869bd1a37fddc06499a096101e6c900e815d81 # 2.0.36 + uses: checkmarx/ast-github-action@184bf2f64f55d1c93fd6636d539edf274703e434 # 2.0.41 env: INCREMENTAL: "${{ contains(github.event_name, 'pull_request') && '--sast-incremental' || '' }}" with: @@ -46,7 +46,7 @@ jobs: --output-path . ${{ env.INCREMENTAL }} - name: Upload Checkmarx results to GitHub - uses: github/codeql-action/upload-sarif@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0 + uses: github/codeql-action/upload-sarif@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # v3.28.0 with: sarif_file: cx_result.sarif diff --git a/.github/workflows/version-auto-bump.yml b/.github/workflows/version-auto-bump.yml index f41261cb39a..ef46dbc867d 100644 --- a/.github/workflows/version-auto-bump.yml +++ b/.github/workflows/version-auto-bump.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-24.04 steps: - name: Generate GH App token - uses: actions/create-github-app-token@5d869da34e18e7287c1daad50e0b8ea0f506ce69 # v1.11.0 + uses: actions/create-github-app-token@c1a285145b9d317df6ced56c09f525b5c2b6f755 # v1.11.1 id: app-token with: app-id: ${{ secrets.BW_GHAPP_ID }} From aeba2b3c73e953111c0907eba68d06596a5f6a12 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 2 Jan 2025 22:29:53 -0500 Subject: [PATCH 70/75] [deps] Platform: Update webpack-cli to v6 (#12552) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package-lock.json | 122 +++++++++++++++++++++++++++------------------- package.json | 2 +- 2 files changed, 73 insertions(+), 51 deletions(-) diff --git a/package-lock.json b/package-lock.json index 197b7cb0093..ba7953d0faf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -179,7 +179,7 @@ "util": "0.12.5", "wait-on": "8.0.1", "webpack": "5.97.1", - "webpack-cli": "5.1.4", + "webpack-cli": "6.0.1", "webpack-dev-server": "5.2.0", "webpack-node-externals": "3.0.0" }, @@ -10338,45 +10338,45 @@ "license": "BSD-3-Clause" }, "node_modules/@webpack-cli/configtest": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", - "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-3.0.1.tgz", + "integrity": "sha512-u8d0pJ5YFgneF/GuvEiDA61Tf1VDomHHYMjv/wc9XzYj7nopltpG96nXN5dJRstxZhcNpV1g+nT6CydO7pHbjA==", "dev": true, "license": "MIT", "engines": { - "node": ">=14.15.0" + "node": ">=18.12.0" }, "peerDependencies": { - "webpack": "5.x.x", - "webpack-cli": "5.x.x" + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" } }, "node_modules/@webpack-cli/info": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz", - "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-3.0.1.tgz", + "integrity": "sha512-coEmDzc2u/ffMvuW9aCjoRzNSPDl/XLuhPdlFRpT9tZHmJ/039az33CE7uH+8s0uL1j5ZNtfdv0HkfaKRBGJsQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=14.15.0" + "node": ">=18.12.0" }, "peerDependencies": { - "webpack": "5.x.x", - "webpack-cli": "5.x.x" + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" } }, "node_modules/@webpack-cli/serve": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz", - "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-3.0.1.tgz", + "integrity": "sha512-sbgw03xQaCLiT6gcY/6u3qBDn01CWw/nbaXl3gTdTFuJJ75Gffv3E3DBpgvY2fkkrdS1fpjaXNOmJlnbtKauKg==", "dev": true, "license": "MIT", "engines": { - "node": ">=14.15.0" + "node": ">=18.12.0" }, "peerDependencies": { - "webpack": "5.x.x", - "webpack-cli": "5.x.x" + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" }, "peerDependenciesMeta": { "webpack-dev-server": { @@ -18831,6 +18831,16 @@ "node": ">= 0.4" } }, + "node_modules/interpret": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", + "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/into-stream": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-6.0.0.tgz", @@ -27431,6 +27441,19 @@ "node": ">=0.10.0" } }, + "node_modules/rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve": "^1.20.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, "node_modules/redent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", @@ -31919,43 +31942,40 @@ } }, "node_modules/webpack-cli": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", - "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-6.0.1.tgz", + "integrity": "sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw==", "dev": true, "license": "MIT", "dependencies": { - "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^2.1.1", - "@webpack-cli/info": "^2.0.2", - "@webpack-cli/serve": "^2.0.5", + "@discoveryjs/json-ext": "^0.6.1", + "@webpack-cli/configtest": "^3.0.1", + "@webpack-cli/info": "^3.0.1", + "@webpack-cli/serve": "^3.0.1", "colorette": "^2.0.14", - "commander": "^10.0.1", + "commander": "^12.1.0", "cross-spawn": "^7.0.3", - "envinfo": "^7.7.3", + "envinfo": "^7.14.0", "fastest-levenshtein": "^1.0.12", "import-local": "^3.0.2", "interpret": "^3.1.1", "rechoir": "^0.8.0", - "webpack-merge": "^5.7.3" + "webpack-merge": "^6.0.1" }, "bin": { "webpack-cli": "bin/cli.js" }, "engines": { - "node": ">=14.15.0" + "node": ">=18.12.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "webpack": "5.x.x" + "webpack": "^5.82.0" }, "peerDependenciesMeta": { - "@webpack-cli/generators": { - "optional": true - }, "webpack-bundle-analyzer": { "optional": true }, @@ -31964,37 +31984,39 @@ } } }, - "node_modules/webpack-cli/node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "node_modules/webpack-cli/node_modules/@discoveryjs/json-ext": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.3.tgz", + "integrity": "sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=14" + "node": ">=14.17.0" } }, - "node_modules/webpack-cli/node_modules/interpret": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", - "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", + "node_modules/webpack-cli/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", "dev": true, "license": "MIT", "engines": { - "node": ">=10.13.0" + "node": ">=18" } }, - "node_modules/webpack-cli/node_modules/rechoir": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", - "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "node_modules/webpack-cli/node_modules/webpack-merge": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", + "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", "dev": true, "license": "MIT", "dependencies": { - "resolve": "^1.20.0" + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.1" }, "engines": { - "node": ">= 10.13.0" + "node": ">=18.0.0" } }, "node_modules/webpack-dev-middleware": { diff --git a/package.json b/package.json index 907723c3a02..843e9f34bb6 100644 --- a/package.json +++ b/package.json @@ -140,7 +140,7 @@ "util": "0.12.5", "wait-on": "8.0.1", "webpack": "5.97.1", - "webpack-cli": "5.1.4", + "webpack-cli": "6.0.1", "webpack-dev-server": "5.2.0", "webpack-node-externals": "3.0.0" }, From b0f597128720331d7d0e19dcf08256be539f4d09 Mon Sep 17 00:00:00 2001 From: Jonas Hendrickx Date: Fri, 3 Jan 2025 10:30:25 +0100 Subject: [PATCH 71/75] [PM-16664] Fix annual pricing for billable providers (#12662) --- .../provider-subscription.component.html | 13 +++++++------ .../provider-subscription.component.ts | 16 ++++++++++++++++ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/bitwarden_license/bit-web/src/app/billing/providers/subscription/provider-subscription.component.html b/bitwarden_license/bit-web/src/app/billing/providers/subscription/provider-subscription.component.html index 50db2cb6a36..d23216cf7c1 100644 --- a/bitwarden_license/bit-web/src/app/billing/providers/subscription/provider-subscription.component.html +++ b/bitwarden_license/bit-web/src/app/billing/providers/subscription/provider-subscription.component.html @@ -38,12 +38,14 @@ }} - {{ ((100 - subscription.discountPercentage) / 100) * i.cost | currency: "$" }} /{{ - "month" | i18n + {{ + ((100 - subscription.discountPercentage) / 100) * getMonthlyCost(i) + | currency: "$" }} + / {{ "month" | i18n }}
    - {{ i.cost | currency: "$" }} /{{ "month" | i18n }} + {{ getMonthlyCost(i) | currency: "$" }} / {{ "month" | i18n }}
    @@ -52,9 +54,8 @@ - Total: {{ totalCost | currency: "$" }} /{{ - "month" | i18n - }} + Total: + {{ getTotalMonthlyCost() | currency: "$" }} / {{ "month" | i18n }} diff --git a/bitwarden_license/bit-web/src/app/billing/providers/subscription/provider-subscription.component.ts b/bitwarden_license/bit-web/src/app/billing/providers/subscription/provider-subscription.component.ts index a6b27b9f3dd..8d222ec42bd 100644 --- a/bitwarden_license/bit-web/src/app/billing/providers/subscription/provider-subscription.component.ts +++ b/bitwarden_license/bit-web/src/app/billing/providers/subscription/provider-subscription.component.ts @@ -113,4 +113,20 @@ export class ProviderSubscriptionComponent implements OnInit, OnDestroy { } }); } + + protected getMonthlyCost(plan: ProviderPlanResponse): number { + return plan.cadence === "Monthly" ? plan.cost : plan.cost / 12; + } + + protected getDiscountedMonthlyCost(plan: ProviderPlanResponse): number { + return ((100 - this.subscription.discountPercentage) / 100) * this.getMonthlyCost(plan); + } + + protected getTotalMonthlyCost(): number { + let totalCost: number = 0; + for (const plan of this.subscription.plans) { + totalCost += this.getDiscountedMonthlyCost(plan); + } + return totalCost; + } } From 27e3f72e048c228b29b0770aed1fd6a2ab59e18d Mon Sep 17 00:00:00 2001 From: Jonas Hendrickx Date: Fri, 3 Jan 2025 10:46:35 +0100 Subject: [PATCH 72/75] Revert "[PM-16664] Fix annual pricing for billable providers (#12662)" (#12677) This reverts commit b0f597128720331d7d0e19dcf08256be539f4d09. --- .../provider-subscription.component.html | 13 ++++++------- .../provider-subscription.component.ts | 16 ---------------- 2 files changed, 6 insertions(+), 23 deletions(-) diff --git a/bitwarden_license/bit-web/src/app/billing/providers/subscription/provider-subscription.component.html b/bitwarden_license/bit-web/src/app/billing/providers/subscription/provider-subscription.component.html index d23216cf7c1..50db2cb6a36 100644 --- a/bitwarden_license/bit-web/src/app/billing/providers/subscription/provider-subscription.component.html +++ b/bitwarden_license/bit-web/src/app/billing/providers/subscription/provider-subscription.component.html @@ -38,14 +38,12 @@ }} - {{ - ((100 - subscription.discountPercentage) / 100) * getMonthlyCost(i) - | currency: "$" + {{ ((100 - subscription.discountPercentage) / 100) * i.cost | currency: "$" }} /{{ + "month" | i18n }} - / {{ "month" | i18n }}
    - {{ getMonthlyCost(i) | currency: "$" }} / {{ "month" | i18n }} + {{ i.cost | currency: "$" }} /{{ "month" | i18n }}
    @@ -54,8 +52,9 @@ - Total: - {{ getTotalMonthlyCost() | currency: "$" }} / {{ "month" | i18n }} + Total: {{ totalCost | currency: "$" }} /{{ + "month" | i18n + }} diff --git a/bitwarden_license/bit-web/src/app/billing/providers/subscription/provider-subscription.component.ts b/bitwarden_license/bit-web/src/app/billing/providers/subscription/provider-subscription.component.ts index 8d222ec42bd..a6b27b9f3dd 100644 --- a/bitwarden_license/bit-web/src/app/billing/providers/subscription/provider-subscription.component.ts +++ b/bitwarden_license/bit-web/src/app/billing/providers/subscription/provider-subscription.component.ts @@ -113,20 +113,4 @@ export class ProviderSubscriptionComponent implements OnInit, OnDestroy { } }); } - - protected getMonthlyCost(plan: ProviderPlanResponse): number { - return plan.cadence === "Monthly" ? plan.cost : plan.cost / 12; - } - - protected getDiscountedMonthlyCost(plan: ProviderPlanResponse): number { - return ((100 - this.subscription.discountPercentage) / 100) * this.getMonthlyCost(plan); - } - - protected getTotalMonthlyCost(): number { - let totalCost: number = 0; - for (const plan of this.subscription.plans) { - totalCost += this.getDiscountedMonthlyCost(plan); - } - return totalCost; - } } From 1f00eb1bfb7703db9e9686c62084274f57617c60 Mon Sep 17 00:00:00 2001 From: "bw-ghapp[bot]" <178206702+bw-ghapp[bot]@users.noreply.github.com> Date: Fri, 3 Jan 2025 11:05:20 +0100 Subject: [PATCH 73/75] Autosync the updated translations (#12671) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/browser/src/_locales/hr/messages.json | 2 +- apps/browser/src/_locales/nl/messages.json | 186 +++++++++--------- apps/browser/src/_locales/pl/messages.json | 2 +- apps/browser/src/_locales/pt_PT/messages.json | 2 +- apps/browser/src/_locales/sr/messages.json | 34 ++-- apps/browser/src/_locales/uk/messages.json | 2 +- apps/browser/src/_locales/zh_CN/messages.json | 18 +- apps/browser/store/locales/nl/copy.resx | 58 +++--- 8 files changed, 152 insertions(+), 152 deletions(-) diff --git a/apps/browser/src/_locales/hr/messages.json b/apps/browser/src/_locales/hr/messages.json index 3b60cfe45c8..c93922cb913 100644 --- a/apps/browser/src/_locales/hr/messages.json +++ b/apps/browser/src/_locales/hr/messages.json @@ -1005,7 +1005,7 @@ "message": "Prikazuj identitete za jednostavnu auto-ispunu." }, "clickToAutofillOnVault": { - "message": "Click items to autofill on Vault view" + "message": "Klikni stavke za auto-ispunu na prikazu trezora" }, "clearClipboard": { "message": "Očisti međuspremnik", diff --git a/apps/browser/src/_locales/nl/messages.json b/apps/browser/src/_locales/nl/messages.json index 7a252ee3ddb..d167ba220c1 100644 --- a/apps/browser/src/_locales/nl/messages.json +++ b/apps/browser/src/_locales/nl/messages.json @@ -181,7 +181,7 @@ "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." }, "autoFill": { - "message": "Auto-invullen" + "message": "Automatisch invullen" }, "autoFillLogin": { "message": "Login automatisch invullen" @@ -688,7 +688,7 @@ "message": "Nu vergrendelen" }, "lockAll": { - "message": "Vergrendel alles" + "message": "Alles vergrendelen" }, "immediately": { "message": "Onmiddellijk" @@ -1043,7 +1043,7 @@ "message": "Ja, nu bijwerken" }, "notificationUnlockDesc": { - "message": "Ontgrendel je Bitwarden-kluis om het auto-invulverzoek te voltooien." + "message": "Ontgrendel je Bitwarden-kluis om het automatisch invullen verzoek te voltooien." }, "notificationUnlock": { "message": "Ontgrendelen" @@ -1200,7 +1200,7 @@ "message": "Geen bijlagen." }, "attachmentSaved": { - "message": "De bijlage is opgeslagen." + "message": "Bijlage opgeslagen" }, "file": { "message": "Bestand" @@ -1209,7 +1209,7 @@ "message": "Bestand om te delen" }, "selectFile": { - "message": "Selecteer een bestand." + "message": "Selecteer een bestand" }, "maxFileSize": { "message": "Maximale bestandsgrootte is 500 MB." @@ -1242,7 +1242,7 @@ "message": "1 GB versleutelde opslag voor bijlagen." }, "premiumSignUpEmergency": { - "message": "Noodtoegang" + "message": "Noodtoegang." }, "premiumSignUpTwoStepOptions": { "message": "Eigen opties voor tweestapsaanmelding zoals YubiKey en Duo." @@ -1425,10 +1425,10 @@ "message": "Specificeer de basis-URL van je zelfgehoste Bitwarden-installatie. Bijvoorbeeld: https://bitwarden.company.com" }, "selfHostedCustomEnvHeader": { - "message": "For advanced configuration, you can specify the base URL of each service independently." + "message": "Voor geavanceerde configuratie kun je de basis URL van elke service onafhankelijk opgeven." }, "selfHostedEnvFormInvalid": { - "message": "You must add either the base Server URL or at least one custom environment." + "message": "Je moet de basis URL van de server of ten minste één aangepaste omgeving toevoegen." }, "customEnvironment": { "message": "Aangepaste omgeving" @@ -1459,14 +1459,14 @@ "message": "Pictogrammenserver-URL" }, "environmentSaved": { - "message": "De omgeving-URL's zijn opgeslagen." + "message": "Omgeving-URL's opgeslagen" }, "showAutoFillMenuOnFormFields": { - "message": "Auto-invulmenu op formuliervelden weergeven", + "message": "Automatisch invullen menu op formuliervelden weergeven", "description": "Represents the message for allowing the user to enable the autofill overlay" }, "autofillSuggestionsSectionTitle": { - "message": "Suggesties voor automatisch invullen" + "message": "Suggesties automatisch invullen" }, "showInlineMenuLabel": { "message": "Suggesties voor automatisch invullen op formuliervelden weergeven" @@ -1511,7 +1511,7 @@ "message": "Als een inlogformulier wordt gedetecteerd, dan worden de inloggegevens automatisch ingevuld." }, "experimentalFeature": { - "message": "Gehackte of onbetrouwbare websites kunnen auto-invullen tijdens het laden van de pagina misbruiken." + "message": "Gehackte of onbetrouwbare websites kunnen automatisch invullen tijdens het laden van de pagina misbruiken." }, "learnMoreAboutAutofillOnPageLoadLinkText": { "message": "Meer over risico's lezen" @@ -1535,7 +1535,7 @@ "message": "Automatisch invullen bij laden van pagina" }, "autoFillOnPageLoadNo": { - "message": "Niet Automatisch invullen bij laden van pagina" + "message": "Niet automatisch invullen bij laden van pagina" }, "commandOpenPopup": { "message": "Open kluis in pop-up" @@ -1606,7 +1606,7 @@ "message": "Een herkenbare afbeelding naast iedere login weergeven." }, "faviconDescAlt": { - "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + "message": "Toon een herkenbare afbeelding naast elke login. Geldt voor alle ingelogde accounts." }, "enableBadgeCounter": { "message": "Teller weergeven" @@ -1690,7 +1690,7 @@ "message": "Dr." }, "mx": { - "message": "Mx" + "message": "Mx." }, "firstName": { "message": "Voornaam" @@ -1993,10 +1993,10 @@ "message": "Ontgrendelen met PIN" }, "setYourPinTitle": { - "message": "PIN-code instellen" + "message": "PIN instellen" }, "setYourPinButton": { - "message": "PIN-code instellen" + "message": "PIN instellen" }, "setYourPinCode": { "message": "Stel je PIN-code in voor het ontgrendelen van Bitwarden. Je PIN-code wordt opnieuw ingesteld als je je ooit volledig afmeldt bij de applicatie." @@ -2111,10 +2111,10 @@ "message": "Invullen en opslaan" }, "autoFillSuccessAndSavedUri": { - "message": "Automatisch gevuld item en opgeslagen URI" + "message": "Automatisch ingevuld item en opgeslagen URI" }, "autoFillSuccess": { - "message": "Automatisch gevuld item" + "message": "Item automatisch ingevuld " }, "insecurePageWarning": { "message": "Waarschuwing: Dit is een onbeveiligde HTTP-pagina waardoor anderen alle informatie die je verstuurt kunnen zien en wijzigen. Deze login is oorspronkelijk opgeslagen op een beveiligde (HTTPS) pagina." @@ -2225,7 +2225,7 @@ "message": "Fout bij vernieuwen toegangstoken" }, "errorRefreshingAccessTokenDesc": { - "message": "No refresh token or API keys found. Please try logging out and logging back in." + "message": "Geen verversingstoken of API-sleutels gevonden. Probeer uit te loggen en weer in te loggen." }, "desktopSyncVerificationTitle": { "message": "Desktopsynchronisatieverificatie" @@ -2282,10 +2282,10 @@ "message": "Dit apparaat ondersteunt geen browserbiometrie." }, "biometricsNotUnlockedTitle": { - "message": "User locked or logged out" + "message": "Gebruiker vergrendeld of uitgelogd" }, "biometricsNotUnlockedDesc": { - "message": "Please unlock this user in the desktop application and try again." + "message": "Ontgrendel deze gebruiker in de desktopapplicatie en probeer het opnieuw." }, "biometricsNotAvailableTitle": { "message": "Biometrisch ontgrendelen niet beschikbaar" @@ -2623,11 +2623,11 @@ "description": "Used as a message within the notification bar when no folders are found" }, "orgPermissionsUpdatedMustSetPassword": { - "message": "Your organization permissions were updated, requiring you to set a master password.", + "message": "De machtigingen van je organisatie zijn bijgewerkt, waardoor je een hoofdwachtwoord moet instellen.", "description": "Used as a card title description on the set password page to explain why the user is there" }, "orgRequiresYouToSetPassword": { - "message": "Your organization requires you to set a master password.", + "message": "Je organisatie vereist dat je een hoofdwachtwoord instelt.", "description": "Used as a card title description on the set password page to explain why the user is there" }, "cardMetrics": { @@ -2766,7 +2766,7 @@ "message": "Persoonlijke kluis exporteren" }, "exportingIndividualVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", + "message": "Alleen de individuele kluisitems die gekoppeld zijn aan $EMAIL$ worden geëxporteerd. Kluisitems van organisaties worden niet meegenomen. Alleen de informatie over het kluisitem wordt geëxporteerd en niet de bijbehorende bijlagen.", "placeholders": { "email": { "content": "$1", @@ -2869,7 +2869,7 @@ "description": "Guidance provided for email forwarding services that support multiple email domains." }, "forwarderError": { - "message": "$SERVICENAME$ error: $ERRORMESSAGE$", + "message": "$SERVICENAME$ fout: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", "placeholders": { "servicename": { @@ -2887,7 +2887,7 @@ "description": "Displayed with the address on the forwarding service's configuration screen." }, "forwarderGeneratedByWithWebsite": { - "message": "Website: $WEBSITE$. Generated by Bitwarden.", + "message": "Website: $WEBSITE$. Gegenereerd door Bitwarden.", "description": "Displayed with the address on the forwarding service's configuration screen.", "placeholders": { "WEBSITE": { @@ -2897,7 +2897,7 @@ } }, "forwaderInvalidToken": { - "message": "Invalid $SERVICENAME$ API token", + "message": "Ongeldig $SERVICENAME$ API token", "description": "Displayed when the user's API token is empty or rejected by the forwarding service.", "placeholders": { "servicename": { @@ -2907,7 +2907,7 @@ } }, "forwaderInvalidTokenWithMessage": { - "message": "Invalid $SERVICENAME$ API token: $ERRORMESSAGE$", + "message": "Ongeldige $SERVICENAME$ API token: $ERRORMESSAGE$", "description": "Displayed when the user's API token is rejected by the forwarding service with an error message.", "placeholders": { "servicename": { @@ -2921,7 +2921,7 @@ } }, "forwarderNoAccountId": { - "message": "Unable to obtain $SERVICENAME$ masked email account ID.", + "message": "Kan $SERVICENAME$ gemaskeerde e-mailaccount-ID niet verkrijgen.", "description": "Displayed when the forwarding service fails to return an account ID.", "placeholders": { "servicename": { @@ -2931,7 +2931,7 @@ } }, "forwarderNoDomain": { - "message": "Invalid $SERVICENAME$ domain.", + "message": "Ongeldig $SERVICENAME$ domein.", "description": "Displayed when the domain is empty or domain authorization failed at the forwarding service.", "placeholders": { "servicename": { @@ -2941,7 +2941,7 @@ } }, "forwarderNoUrl": { - "message": "Invalid $SERVICENAME$ url.", + "message": "Ongeldige $SERVICENAME$ url.", "description": "Displayed when the url of the forwarding service wasn't supplied.", "placeholders": { "servicename": { @@ -2951,7 +2951,7 @@ } }, "forwarderUnknownError": { - "message": "Unknown $SERVICENAME$ error occurred.", + "message": "Onbekende $SERVICENAME$ fout opgetreden.", "description": "Displayed when the forwarding service failed due to an unknown error.", "placeholders": { "servicename": { @@ -2961,7 +2961,7 @@ } }, "forwarderUnknownForwarder": { - "message": "Unknown forwarder: '$SERVICENAME$'.", + "message": "Onbekende doorstuurder: '$SERVICENAME$'.", "description": "Displayed when the forwarding service is not supported.", "placeholders": { "servicename": { @@ -3017,7 +3017,7 @@ "message": "zelfgehost" }, "thirdParty": { - "message": "van derden" + "message": "Derde partij" }, "thirdPartyServerMessage": { "message": "Verbonden met server implementatie van derden, $SERVERNAME$. Reproduceer bugs via de officiële server, of meld ze bij het serverproject.", @@ -3071,7 +3071,7 @@ "message": "Alle inlogopties bekijken" }, "viewAllLoginOptionsV1": { - "message": "View all log in options" + "message": "Alle inlogopties weergeven" }, "notificationSentDevice": { "message": "Er is een melding naar je apparaat verzonden." @@ -3125,10 +3125,10 @@ "message": "Je organisatiebeleid heeft het automatisch invullen bij laden van pagina ingeschakeld." }, "howToAutofill": { - "message": "Hoe automatisch aanvullen" + "message": "Hoe automatisch invullen" }, "autofillSelectInfoWithCommand": { - "message": "Select an item from this screen, use the shortcut $COMMAND$, or explore other options in settings.", + "message": "Selecteer een item in dit scherm, gebruik de sneltoets $COMMAND$ of verken andere opties in de instellingen.", "placeholders": { "command": { "content": "$1", @@ -3137,16 +3137,16 @@ } }, "autofillSelectInfoWithoutCommand": { - "message": "Select an item from this screen, or explore other options in settings." + "message": "Selecteer een item in dit scherm of verken andere opties in de instellingen." }, "gotIt": { - "message": "Ik snap het" + "message": "Oké" }, "autofillSettings": { "message": "Instellingen automatisch invullen" }, "autofillKeyboardShortcutSectionTitle": { - "message": "Shortcut voor automatisch invullen" + "message": "Snelkoppeling automatisch invullen" }, "autofillKeyboardShortcutUpdateLabel": { "message": "Snelkoppeling wijzigen" @@ -3243,7 +3243,7 @@ "message": "Algemeen" }, "display": { - "message": "Display" + "message": "Weergave" }, "accountSuccessfullyCreated": { "message": "Account succesvol aangemaakt!" @@ -3372,7 +3372,7 @@ "message": "-- Type om te filteren --" }, "multiSelectLoading": { - "message": "Opties ophalen..." + "message": "Opties ophalen…" }, "multiSelectNotFound": { "message": "Geen items gevonden" @@ -3413,7 +3413,7 @@ "description": "Notification button text for starting a fileless import." }, "importing": { - "message": "Importeren...", + "message": "Importeren…", "description": "Notification message for when an import is in progress." }, "dataSuccessfullyImported": { @@ -3432,33 +3432,33 @@ "message": "Aliasdomein" }, "passwordRepromptDisabledAutofillOnPageLoad": { - "message": "Items with master password re-prompt cannot be autofilled on page load. Autofill on page load turned off.", + "message": "Items met hoofdwachtwoord re-prompt kunnen niet automatisch worden ingevuld bij het laden van de pagina. Automatisch invullen bij laden van pagina uitgeschakeld.", "description": "Toast message for describing that master password re-prompt cannot be autofilled on page load." }, "autofillOnPageLoadSetToDefault": { - "message": "Autofill on page load set to use default setting.", + "message": "Automatisch invullen bij het laden van een pagina ingesteld op de standaardinstelling.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, "turnOffMasterPasswordPromptToEditField": { - "message": "Turn off master password re-prompt to edit this field", + "message": "Hoofdwachtwoord herhaalprompt uitschakelen om dit veld te bewerken", "description": "Message appearing below the autofill on load message when master password reprompt is set for a vault item." }, "toggleSideNavigation": { "message": "Zijnavigatie schakelen" }, "skipToContent": { - "message": "Skip to content" + "message": "Overslaan naar inhoud" }, "bitwardenOverlayButton": { "message": "Menuknop Bitwarden automatisch invullen", "description": "Page title for the iframe containing the overlay button" }, "toggleBitwardenVaultOverlay": { - "message": "Bitwarden auto-invulmenu in- en uitschakelen", + "message": "Bitwarden automatisch invullen menu in- en uitschakelen", "description": "Screen reader and tool tip label for the overlay button" }, "bitwardenVault": { - "message": "Bitwarden auto-invulmenu", + "message": "Bitwarden automatisch invullen menu", "description": "Page title in overlay" }, "unlockYourAccountToViewMatchingLogins": { @@ -3486,7 +3486,7 @@ "description": "Aria label for the totp seconds displayed in the inline menu for autofill" }, "fillCredentialsFor": { - "message": "Inloggegevens invullen voor", + "message": "Referenties invullen voor", "description": "Screen reader text for when overlay item is in focused" }, "partialUsername": { @@ -3530,7 +3530,7 @@ "description": "Screen reader text (aria-label) for new identity button within inline menu" }, "bitwardenOverlayMenuAvailable": { - "message": "Bitwarden auto-invulmenu beschikbaar. Druk op de pijltjestoets omlaag om te selecteren.", + "message": "Bitwarden automatisch invullen menu beschikbaar. Druk op de pijltjestoets omlaag om te selecteren.", "description": "Screen reader text for announcing when the overlay opens on the page" }, "turnOn": { @@ -3574,7 +3574,7 @@ "message": "Deze actie vereist verificatie actie. Stel een pincode in om door te gaan." }, "setPin": { - "message": "Pincode instellen" + "message": "PIN instellen" }, "verifyWithBiometrics": { "message": "Biometrisch ontgrendelen" @@ -3619,7 +3619,7 @@ "message": "Fout bij het verbinden met de Duo-service. Gebruik een andere tweestapsaanmeldingsmethode of neem contact op met Duo voor hulp." }, "launchDuoAndFollowStepsToFinishLoggingIn": { - "message": "Launch Duo and follow the steps to finish logging in." + "message": "Start Duo en volg de stappen om het inloggen te voltooien." }, "duoRequiredForAccount": { "message": "Jouw account vereist Duo-tweestapsaanmelding." @@ -3628,10 +3628,10 @@ "message": "Open de extensie om in te loggen." }, "popoutExtension": { - "message": "Popout extension" + "message": "Pop-out extensie" }, "launchDuo": { - "message": "Launch Duo" + "message": "Duo starten" }, "importFormatError": { "message": "De gegevens zijn niet correct opgemaakt. Controleer je importbestand en probeer het opnieuw." @@ -3759,7 +3759,7 @@ "message": "Kies een passkey om mee in te loggen" }, "passkeyItem": { - "message": "Passkey-Item" + "message": "Passkey item" }, "overwritePasskey": { "message": "Passkey overschrijven?" @@ -3801,7 +3801,7 @@ "message": "LastPass Email" }, "importingYourAccount": { - "message": "Account impoteren..." + "message": "Account impoteren…" }, "lastPassMFARequired": { "message": "LastPass multifactor-authenticatie vereist" @@ -3825,10 +3825,10 @@ "message": "Wacht op SSO-authenticatie" }, "awaitingSSODesc": { - "message": "Ga door met inloggen met de inloggegevens van je bedrijf." + "message": "Ga door met inloggen met de referenties van je bedrijf." }, "seeDetailedInstructions": { - "message": "See detailed instructions on our help site at", + "message": "Zie gedetailleerde instructies op onze helpsite op", "description": "This is followed a by a hyperlink to the help website." }, "importDirectlyFromLastPass": { @@ -3844,52 +3844,52 @@ "message": "Collectie" }, "lastPassYubikeyDesc": { - "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + "message": "Steek de YubiKey die bij je LastPass account hoort in de USB poort van je computer en druk dan op de knop." }, "switchAccount": { - "message": "Switch account" + "message": "Account wisselen" }, "switchAccounts": { - "message": "Switch accounts" + "message": "Accounts wisselen" }, "switchToAccount": { - "message": "Switch to account" + "message": "Wisselen naar account" }, "activeAccount": { - "message": "Active account" + "message": "Actief account" }, "availableAccounts": { "message": "Beschikbare accounts" }, "accountLimitReached": { - "message": "Account limit reached. Log out of an account to add another." + "message": "Accountlimiet bereikt. Log uit bij een account om een andere toe te voegen." }, "active": { - "message": "active" + "message": "actief" }, "locked": { - "message": "locked" + "message": "vergrendeld" }, "unlocked": { - "message": "unlocked" + "message": "ontgrendeld" }, "server": { "message": "server" }, "hostedAt": { - "message": "hosted at" + "message": "gehost op" }, "useDeviceOrHardwareKey": { - "message": "Use your device or hardware key" + "message": "Gebruik je apparaat of hardwaresleutel" }, "justOnce": { - "message": "Just once" + "message": "Eenmalig" }, "alwaysForThisSite": { - "message": "Always for this site" + "message": "Altijd voor deze site" }, "domainAddedToExcludedDomains": { - "message": "$DOMAIN$ added to excluded domains.", + "message": "$DOMAIN$ toegevoegd aan uitgesloten domeinen.", "placeholders": { "domain": { "content": "$1", @@ -3950,7 +3950,7 @@ "description": "Button text for the setting that allows overriding the default browser autofill settings" }, "saveCipherAttemptSuccess": { - "message": "Credentials saved successfully!", + "message": "Referenties succesvol opgeslagen!", "description": "Notification message for when saving credentials has succeeded." }, "passwordSaved": { @@ -3958,7 +3958,7 @@ "description": "Notification message for when saving credentials has succeeded." }, "updateCipherAttemptSuccess": { - "message": "Credentials updated successfully!", + "message": "Referenties succesvol bijgewerkt!", "description": "Notification message for when updating credentials has succeeded." }, "passwordUpdated": { @@ -3966,7 +3966,7 @@ "description": "Notification message for when updating credentials has succeeded." }, "saveCipherAttemptFailed": { - "message": "Error saving credentials. Check console for details.", + "message": "Fout bij het opslaan van referenties. Controleer console voor details.", "description": "Notification message for when saving credentials has failed." }, "success": { @@ -3979,7 +3979,7 @@ "message": "Passkey verwijderd" }, "autofillSuggestions": { - "message": "Suggesties voor automatisch invullen" + "message": "Suggesties automatisch invullen" }, "autofillSuggestionsTip": { "message": "Inlogitem opslaan voor automatisch invullen op deze site" @@ -3994,7 +3994,7 @@ "message": "Wis filters of probeer een andere zoekterm" }, "copyInfoTitle": { - "message": "Copy info - $ITEMNAME$", + "message": "Info kopiëren - $ITEMNAME$", "description": "Title for a button that opens a menu with options to copy information from an item.", "placeholders": { "itemname": { @@ -4004,7 +4004,7 @@ } }, "copyNoteTitle": { - "message": "Copy Note - $ITEMNAME$", + "message": "Notitie kopiëren - $ITEMNAME$", "description": "Title for a button copies a note to the clipboard.", "placeholders": { "itemname": { @@ -4014,7 +4014,7 @@ } }, "moreOptionsLabel": { - "message": "More options, $ITEMNAME$", + "message": "Meer opties, $ITEMNAME$", "description": "Aria label for a button that opens a menu with more options for an item.", "placeholders": { "itemname": { @@ -4024,7 +4024,7 @@ } }, "moreOptionsTitle": { - "message": "More options - $ITEMNAME$", + "message": "Meer opties - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", "placeholders": { "itemname": { @@ -4034,7 +4034,7 @@ } }, "viewItemTitle": { - "message": "View item - $ITEMNAME$", + "message": "Item bekijken - $ITEMNAME$", "description": "Title for a link that opens a view for an item.", "placeholders": { "itemname": { @@ -4060,22 +4060,22 @@ "message": "Aan collecties toewijzen" }, "copyEmail": { - "message": "Copy email" + "message": "E-mail kopiëren" }, "copyPhone": { - "message": "Copy phone" + "message": "Telefoon kopiëren" }, "copyAddress": { - "message": "Copy address" + "message": "Adres kopiëren" }, "adminConsole": { - "message": "Admin Console" + "message": "Beheerconsole" }, "accountSecurity": { "message": "Accountbeveiliging" }, "notifications": { - "message": "Notifications" + "message": "Meldingen" }, "appearance": { "message": "Uiterlijk" @@ -4107,7 +4107,7 @@ } }, "new": { - "message": "New" + "message": "Nieuw" }, "removeItem": { "message": "Verwijder $NAME$", @@ -4245,7 +4245,7 @@ "description": "Used within the inline menu to provide an aria description when users are attempting to fill a card cipher." }, "loginCredentials": { - "message": "Inloggegevens" + "message": "Login referenties" }, "authenticatorKey": { "message": "Authenticatiesleutel" @@ -4546,10 +4546,10 @@ "message": "Itemlocatie" }, "fileSend": { - "message": "Bestand-Sends" + "message": "Bestand verzenden" }, "fileSends": { - "message": "Bestand-Sends" + "message": "Bestanden verzenden" }, "textSend": { "message": "Tekst-Sends" @@ -4567,7 +4567,7 @@ "message": "Accountacties" }, "showNumberOfAutofillSuggestions": { - "message": "Aantal login-autofill-suggesties op het extensie-pictogram weergeven" + "message": "Aantal login automatisch invullen suggesties op het extensie-pictogram weergeven" }, "showQuickCopyActions": { "message": "Toon snelle kopieeracties in de kluis" diff --git a/apps/browser/src/_locales/pl/messages.json b/apps/browser/src/_locales/pl/messages.json index 5d1cd22c9ef..a429059ea7d 100644 --- a/apps/browser/src/_locales/pl/messages.json +++ b/apps/browser/src/_locales/pl/messages.json @@ -1005,7 +1005,7 @@ "message": "Pokaż elementy tożsamości na stronie głównej, aby ułatwić autouzupełnianie." }, "clickToAutofillOnVault": { - "message": "Click items to autofill on Vault view" + "message": "Kliknij na dane logowania, aby autouzupełnić w widoku Sejfu" }, "clearClipboard": { "message": "Wyczyść schowek", diff --git a/apps/browser/src/_locales/pt_PT/messages.json b/apps/browser/src/_locales/pt_PT/messages.json index 24caa7e813d..def50289ae6 100644 --- a/apps/browser/src/_locales/pt_PT/messages.json +++ b/apps/browser/src/_locales/pt_PT/messages.json @@ -803,7 +803,7 @@ "message": "Código de verificação inválido" }, "valueCopied": { - "message": "$VALUE$ copiado", + "message": "$VALUE$ copiado(a)", "description": "Value has been copied to the clipboard.", "placeholders": { "value": { diff --git a/apps/browser/src/_locales/sr/messages.json b/apps/browser/src/_locales/sr/messages.json index 1a1ebf69794..d515c2a0c6b 100644 --- a/apps/browser/src/_locales/sr/messages.json +++ b/apps/browser/src/_locales/sr/messages.json @@ -193,10 +193,10 @@ "message": "Ауто-пуњење идентитета" }, "fillVerificationCode": { - "message": "Fill verification code" + "message": "Пуни верификациони кôд" }, "fillVerificationCodeAria": { - "message": "Fill Verification Code", + "message": "Пуни верификациони кôд", "description": "Aria label for the heading displayed the inline menu for totp code autofill" }, "generatePasswordCopied": { @@ -1005,7 +1005,7 @@ "message": "Прикажи ставке идентитета на страници за лакше аутоматско допуњавање." }, "clickToAutofillOnVault": { - "message": "Click items to autofill on Vault view" + "message": "Кликните на ставке за ауто-попуњавање у приказу сефа" }, "clearClipboard": { "message": "Обриши привремену меморију", @@ -3071,7 +3071,7 @@ "message": "Погледајте сав извештај у опције" }, "viewAllLoginOptionsV1": { - "message": "View all log in options" + "message": "Погледајте сав извештај у опције" }, "notificationSentDevice": { "message": "Обавештење је послато на ваш уређај." @@ -3478,11 +3478,11 @@ "description": "Screen reader text (aria-label) for unlock account button in overlay" }, "totpCodeAria": { - "message": "Time-based One-Time Password Verification Code", + "message": "Једнократни верификациони кôд заснован на времену", "description": "Aria label for the totp code displayed in the inline menu for autofill" }, "totpSecondsSpanAria": { - "message": "Time remaining before current TOTP expires", + "message": "Преостало време до истека актуелног ТОТП-а", "description": "Aria label for the totp seconds displayed in the inline menu for autofill" }, "fillCredentialsFor": { @@ -4570,7 +4570,7 @@ "message": "Прикажи број предлога за ауто-попуњавање пријаве на икони додатка" }, "showQuickCopyActions": { - "message": "Show quick copy actions on Vault" + "message": "Приказати брзе радње копирања у Сефу" }, "systemDefault": { "message": "Системски подразумевано" @@ -4804,22 +4804,22 @@ "message": "Бета" }, "importantNotice": { - "message": "Important notice" + "message": "Важно обавештење" }, "setupTwoStepLogin": { - "message": "Set up two-step login" + "message": "Поставити дво-степенску пријаву" }, "newDeviceVerificationNoticeContentPage1": { - "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + "message": "Bitwarden ће послати кôд на имејл вашег налога за верификовање пријављивања са нових уређаја почевши од фебруара 2025." }, "newDeviceVerificationNoticeContentPage2": { - "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + "message": "Можете да подесите пријаву у два корака као алтернативни начин да заштитите свој налог или да промените свој имејл у један који можете да приступите." }, "remindMeLater": { - "message": "Remind me later" + "message": "Подсети ме касније" }, "newDeviceVerificationNoticePageOneFormContent": { - "message": "Do you have reliable access to your email, $EMAIL$?", + "message": "Да ли имате поуздан приступ својим имејлом, $EMAIL$?", "placeholders": { "email": { "content": "$1", @@ -4828,16 +4828,16 @@ } }, "newDeviceVerificationNoticePageOneEmailAccessNo": { - "message": "No, I do not" + "message": "Не, ненам" }, "newDeviceVerificationNoticePageOneEmailAccessYes": { - "message": "Yes, I can reliably access my email" + "message": "Да, могу поуздано да приступим овим имејлом" }, "turnOnTwoStepLogin": { - "message": "Turn on two-step login" + "message": "Упалити дво-степенску пријаву" }, "changeAcctEmail": { - "message": "Change account email" + "message": "Променити имејл налога" }, "extensionWidth": { "message": "Ширина додатка" diff --git a/apps/browser/src/_locales/uk/messages.json b/apps/browser/src/_locales/uk/messages.json index c817b55dab3..8e20bc56ff5 100644 --- a/apps/browser/src/_locales/uk/messages.json +++ b/apps/browser/src/_locales/uk/messages.json @@ -1005,7 +1005,7 @@ "message": "Показувати список посвідчень на сторінці вкладки для легкого автозаповнення." }, "clickToAutofillOnVault": { - "message": "Click items to autofill on Vault view" + "message": "Натисніть на запис у режимі перегляду сховища для автозаповнення" }, "clearClipboard": { "message": "Очистити буфер обміну", diff --git a/apps/browser/src/_locales/zh_CN/messages.json b/apps/browser/src/_locales/zh_CN/messages.json index 39a945360f8..9af42f75e08 100644 --- a/apps/browser/src/_locales/zh_CN/messages.json +++ b/apps/browser/src/_locales/zh_CN/messages.json @@ -284,7 +284,7 @@ "message": "前往网页 App 吗?" }, "continueToWebAppDesc": { - "message": "在网页应用上探索 Bitwarden 账户的更多功能。" + "message": "在网页 App 上探索 Bitwarden 账户的更多功能。" }, "continueToHelpCenter": { "message": "前往帮助中心吗?" @@ -299,7 +299,7 @@ "message": "帮助别人了解 Bitwarden 是否适合他们。立即访问浏览器的扩展程序商店并留下评分。" }, "changeMasterPasswordOnWebConfirmation": { - "message": "您可以在 Bitwarden 网页应用上更改您的主密码。" + "message": "您可以在 Bitwarden 网页 App 上更改您的主密码。" }, "fingerprintPhrase": { "message": "指纹短语", @@ -597,7 +597,7 @@ "message": "前往" }, "launchWebsite": { - "message": "启动网站" + "message": "打开网站" }, "launchWebsiteName": { "message": "前往 $ITEMNAME$ 的网站", @@ -763,7 +763,7 @@ "message": "必须填写确认主密码。" }, "masterPasswordMinlength": { - "message": "主密码必须至少 $VALUE$ 个字符长度。", + "message": "主密码长度必须至少为 $VALUE$ 个字符。", "description": "The Master Password must be at least a specific number of characters long.", "placeholders": { "value": { @@ -1071,7 +1071,7 @@ "message": "主题" }, "themeDesc": { - "message": "更改本应用程序的颜色主题。" + "message": "更改应用程序的颜色主题。" }, "themeDescAlt": { "message": "更改应用程序的颜色主题。适用于所有已登录的账户。" @@ -1133,7 +1133,7 @@ "message": "确认密码库导出" }, "exportWarningDesc": { - "message": "导出的密码库数据包含未加密格式。您不应该通过不安全的渠道(例如电子邮件)来存储或发送导出的文件。用完后请立即将其删除。" + "message": "此导出包含未加密格式的密码库数据。您不应该通过不安全的渠道(例如电子邮件)来存储或发送此导出文件。使用完后请立即将其删除。" }, "encExportKeyWarningDesc": { "message": "此导出将使用您账户的加密密钥来加密您的数据。如果您曾经轮换过账户的加密密钥,您应将其重新导出,否则您将无法解密导出的文件。" @@ -4810,7 +4810,7 @@ "message": "设置两步登录" }, "newDeviceVerificationNoticeContentPage1": { - "message": "从 2025 年 02 月开始,Bitwarden 将向您的账户电子邮箱发送一个代码,以验证来自新设备的登录。" + "message": "从 2025 年 02 月起,当有来自新设备的登录时,Bitwarden 将向您的账户电子邮箱发送验证码。" }, "newDeviceVerificationNoticeContentPage2": { "message": "您可以设置两步登录作为保护账户的替代方法,或将您的电子邮箱更改为您可以访问的电子邮箱。" @@ -4819,7 +4819,7 @@ "message": "稍后提醒我" }, "newDeviceVerificationNoticePageOneFormContent": { - "message": "您能可靠地访问您的电子邮箱 $EMAIL$ 吗?", + "message": "您能可正常访问您的电子邮箱 $EMAIL$ 吗?", "placeholders": { "email": { "content": "$1", @@ -4831,7 +4831,7 @@ "message": "不,我不能" }, "newDeviceVerificationNoticePageOneEmailAccessYes": { - "message": "是的,我可以可靠地访问我的电子邮箱" + "message": "是的,我可以正常访问我的电子邮箱" }, "turnOnTwoStepLogin": { "message": "开启两步登录" diff --git a/apps/browser/store/locales/nl/copy.resx b/apps/browser/store/locales/nl/copy.resx index 6e646d5ece8..17720f54f4c 100644 --- a/apps/browser/store/locales/nl/copy.resx +++ b/apps/browser/store/locales/nl/copy.resx @@ -118,58 +118,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden - wachtwoordbeheerder + Bitwarden Wachtwoordbeheerder - At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. + Thuis, op het werk of onderweg, Bitwarden beveiligt eenvoudig al je wachtwoorden, sleutels en gevoelige informatie. - Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! + Erkend als de beste wachtwoordmanager door PCMag, WIRED, The Verge, CNET, G2 en meer! -SECURE YOUR DIGITAL LIFE -Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. +BEVEILIG JE DIGITALE LEVEN +Beveilig je digitale leven en bescherm je tegen datalekken door unieke, sterke wachtwoorden te genereren en op te slaan voor elke account. Bewaar alles in een end-to-end versleutelde wachtwoordkluis waar alleen jij toegang toe hebt. -ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE -Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. +OVERAL EN ALTIJD TOEGANG TOT JE GEGEVENS, OP ELK APPARAAT +Beheer, bewaar, beveilig en deel een onbeperkt aantal wachtwoorden op een onbeperkt aantal apparaten zonder beperkingen. -EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE -Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. +IEDEREEN ZOU DE MIDDELEN MOETEN HEBBEN OM VEILIG ONLINE TE BLIJVEN +Gebruik Bitwarden gratis, zonder advertenties of verkoop van gegevens. Bitwarden vindt dat iedereen de mogelijkheid moet hebben om veilig online te zijn. Premium abonnementen bieden toegang tot geavanceerde functies. -EMPOWER YOUR TEAMS WITH BITWARDEN -Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. +VERSTERK JE TEAMS MET BITWARDEN +Abonnementen voor Teams en Enterprise worden geleverd met professionele zakelijke functies. Enkele voorbeelden zijn SSO-integratie, zelf hosten, directory-integratie en SCIM provisioning, globaal beleid, API-toegang, gebeurtenislogboeken en meer. -Use Bitwarden to secure your workforce and share sensitive information with colleagues. +Gebruik Bitwarden om je medewerkers te beveiligen en gevoelige informatie te delen met collega's. -More reasons to choose Bitwarden: +Meer redenen om voor Bitwarden te kiezen: -World-Class Encryption -Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private. +Encryptie van wereldklasse +Wachtwoorden worden beschermd met geavanceerde end-to-end versleuteling (AES-256 bit, salted hashtag en PBKDF2 SHA-256) zodat je gegevens veilig en privé blijven. -3rd-party Audits -Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. +Audits door derde partijen +Bitwarden voert regelmatig uitgebreide beveiligingsaudits uit bij gerenommeerde beveiligingsbedrijven. Deze jaarlijkse audits omvatten broncodebeoordelingen en penetratietests voor Bitwarden IP's, servers en webapplicaties. -Advanced 2FA -Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. +Geavanceerde 2FA +Beveilig je login met een authenticator van derden, codes per e-mail of FIDO2 WebAuthn referenties zoals een hardware beveiligingssleutel of passkey. Bitwarden Send -Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. +Verstuur gegevens rechtstreeks naar anderen met behoud van end-to-end versleutelde beveiliging en beperking van blootstelling. -Built-in Generator -Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. +Ingebouwde generator +Maak lange, complexe en duidelijke wachtwoorden en unieke gebruikersnamen voor elke site die je bezoekt. Integreer met e-mail alias providers voor extra privacy. -Global Translations -Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. +Wereldwijde vertalingen +Bitwarden vertalingen bestaan voor meer dan 60 talen, vertaald door de wereldwijde gemeenschap via Crowdin. -Cross-Platform Applications -Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. +Platformoverkoepelende applicaties +Beveilig en deel gevoelige gegevens in je Bitwarden Vault vanuit elke browser, mobiel apparaat of desktop OS, en meer. -Bitwarden secures more than just passwords -End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! +Bitwarden beveiligt meer dan alleen wachtwoorden +Met de end-to-end versleutelde oplossingen voor referentiebeheer van Bitwarden kunnen organisaties alles beveiligen, inclusief ontwikkelaarsgeheimen en ervaringen met wachtwoorden. Bezoek Bitwarden.com voor meer informatie over Bitwarden Secrets Manager en Bitwarden Passwordless.dev! - At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. + Thuis, op het werk of onderweg, Bitwarden beveiligt eenvoudig al je wachtwoorden, sleutels en gevoelige informatie. Synchroniseer en gebruik je kluis op meerdere apparaten From 654eef1de27ffd45798e8a9694a6a529e373381f Mon Sep 17 00:00:00 2001 From: Daniel James Smith <2670567+djsmith85@users.noreply.github.com> Date: Fri, 3 Jan 2025 11:11:12 +0100 Subject: [PATCH 74/75] Remove v1 account switcher (#12577) Remove conditional rendering for extensionRefresh feature flag Co-authored-by: Daniel James Smith --- .../account-switcher.component.html | 236 +++++------------- .../account-switcher.component.ts | 9 - .../account-switching/account.component.html | 123 +++------ .../account-switching/account.component.ts | 1 - 4 files changed, 95 insertions(+), 274 deletions(-) diff --git a/apps/browser/src/auth/popup/account-switching/account-switcher.component.html b/apps/browser/src/auth/popup/account-switching/account-switcher.component.html index f0723d75ff8..0152cd1c7ff 100644 --- a/apps/browser/src/auth/popup/account-switching/account-switcher.component.html +++ b/apps/browser/src/auth/popup/account-switching/account-switcher.component.html @@ -1,182 +1,78 @@ - - - - - - - - + + + + + + + - - - -
    - -
    + + + +
    + +
    - - -

    {{ "availableAccounts" | i18n }}

    -
    + + +

    {{ "availableAccounts" | i18n }}

    +
    -
    - -
    -
    +
    + +
    +
    - -

    - {{ "accountLimitReached" | i18n }} -

    -
    -
    +

    + {{ "accountLimitReached" | i18n }} +

    +
    +
    -
    - - -

    - {{ "options" | i18n }} -

    -
    +
    + + +

    + {{ "options" | i18n }} +

    +
    - - - - - - - - - -
    -
    - - - - - -
    - -
    -
    {{ "switchAccounts" | i18n }}
    -
    - -
    - -
    -
    -
    -
    -
      - -
    • - -
    • - -
      - {{ "availableAccounts" | i18n }} -
      -
    • - -
    • -
      -
      -
    - -

    +

    - -
    -
    {{ "options" | i18n }}
    -
    - - - -
    -
    -
    -
    -
    + + {{ "lockNow" | i18n }} + + + + + + + + +
    +
    +
    diff --git a/apps/browser/src/auth/popup/account-switching/account-switcher.component.ts b/apps/browser/src/auth/popup/account-switching/account-switcher.component.ts index fb636ecaf6d..25e1b2ae83f 100644 --- a/apps/browser/src/auth/popup/account-switching/account-switcher.component.ts +++ b/apps/browser/src/auth/popup/account-switching/account-switcher.component.ts @@ -10,9 +10,7 @@ import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeou import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { UserId } from "@bitwarden/common/types/guid"; import { AvatarModule, @@ -25,7 +23,6 @@ import { import { enableAccountSwitching } from "../../../platform/flags"; import { PopOutComponent } from "../../../platform/popup/components/pop-out.component"; -import { HeaderComponent } from "../../../platform/popup/header.component"; import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component"; import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.component"; @@ -44,7 +41,6 @@ import { AccountSwitcherService } from "./services/account-switcher.service"; AvatarModule, PopupPageComponent, PopupHeaderComponent, - HeaderComponent, PopOutComponent, CurrentAccountComponent, AccountComponent, @@ -58,7 +54,6 @@ export class AccountSwitcherComponent implements OnInit, OnDestroy { loading = false; activeUserCanLock = false; - extensionRefreshFlag = false; enableAccountSwitching = true; constructor( @@ -70,7 +65,6 @@ export class AccountSwitcherComponent implements OnInit, OnDestroy { private router: Router, private vaultTimeoutSettingsService: VaultTimeoutSettingsService, private authService: AuthService, - private configService: ConfigService, private lockService: LockService, ) {} @@ -109,9 +103,6 @@ export class AccountSwitcherComponent implements OnInit, OnDestroy { async ngOnInit() { this.enableAccountSwitching = enableAccountSwitching(); - this.extensionRefreshFlag = await this.configService.getFeatureFlag( - FeatureFlag.ExtensionRefresh, - ); const availableVaultTimeoutActions = await firstValueFrom( this.vaultTimeoutSettingsService.availableVaultTimeoutActions$(), diff --git a/apps/browser/src/auth/popup/account-switching/account.component.html b/apps/browser/src/auth/popup/account-switching/account.component.html index d062c67a2e3..d2e15d31899 100644 --- a/apps/browser/src/auth/popup/account-switching/account.component.html +++ b/apps/browser/src/auth/popup/account-switching/account.component.html @@ -1,109 +1,44 @@ - - - - - - - - - - - - + - - + diff --git a/apps/browser/src/auth/popup/account-switching/account.component.ts b/apps/browser/src/auth/popup/account-switching/account.component.ts index 5c4132e491c..104241e9c7b 100644 --- a/apps/browser/src/auth/popup/account-switching/account.component.ts +++ b/apps/browser/src/auth/popup/account-switching/account.component.ts @@ -19,7 +19,6 @@ import { AccountSwitcherService, AvailableAccount } from "./services/account-swi }) export class AccountComponent { @Input() account: AvailableAccount; - @Input() extensionRefreshFlag: boolean = false; @Output() loading = new EventEmitter(); constructor( From c07454342d020bb8b9c775dbabf3dbad89eb047e Mon Sep 17 00:00:00 2001 From: Victoria League Date: Fri, 3 Jan 2025 09:41:18 -0500 Subject: [PATCH 75/75] [PM-16675] Prevent scrollbar from appearing on each send item (#12666) --- libs/components/src/item/item.component.ts | 2 +- .../send-list-items-container.component.html | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/libs/components/src/item/item.component.ts b/libs/components/src/item/item.component.ts index 833949ddb96..97a80484373 100644 --- a/libs/components/src/item/item.component.ts +++ b/libs/components/src/item/item.component.ts @@ -20,7 +20,7 @@ import { ItemActionComponent } from "./item-action.component"; providers: [{ provide: A11yRowDirective, useExisting: ItemComponent }], host: { class: - "tw-block tw-box-border tw-overflow-auto tw-flex tw-bg-background [&:has(.item-main-content_button:hover,.item-main-content_a:hover)]:tw-cursor-pointer [&:has(.item-main-content_button:hover,.item-main-content_a:hover)]:tw-bg-primary-100 tw-text-main tw-border-solid tw-border-b tw-border-0 [&:not(bit-layout_*)]:tw-rounded-lg bit-compact:[&:not(bit-layout_*)]:tw-rounded-none bit-compact:[&:not(bit-layout_*)]:last-of-type:tw-rounded-b-lg bit-compact:[&:not(bit-layout_*)]:first-of-type:tw-rounded-t-lg tw-min-h-9 tw-mb-1.5 bit-compact:tw-mb-0", + "tw-block tw-box-border tw-overflow-hidden tw-flex tw-bg-background [&:has(.item-main-content_button:hover,.item-main-content_a:hover)]:tw-cursor-pointer [&:has(.item-main-content_button:hover,.item-main-content_a:hover)]:tw-bg-primary-100 tw-text-main tw-border-solid tw-border-b tw-border-0 [&:not(bit-layout_*)]:tw-rounded-lg bit-compact:[&:not(bit-layout_*)]:tw-rounded-none bit-compact:[&:not(bit-layout_*)]:last-of-type:tw-rounded-b-lg bit-compact:[&:not(bit-layout_*)]:first-of-type:tw-rounded-t-lg tw-min-h-9 tw-mb-1.5 bit-compact:tw-mb-0", }, }) export class ItemComponent extends A11yRowDirective { diff --git a/libs/tools/send/send-ui/src/send-list-items-container/send-list-items-container.component.html b/libs/tools/send/send-ui/src/send-list-items-container/send-list-items-container.component.html index a0f6c09f83e..d7755546365 100644 --- a/libs/tools/send/send-ui/src/send-list-items-container/send-list-items-container.component.html +++ b/libs/tools/send/send-ui/src/send-list-items-container/send-list-items-container.component.html @@ -14,7 +14,6 @@

    [queryParams]="{ sendId: send.id, type: send.type }" appStopClick type="button" - class="tw-pb-1" >