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 de438a09467..8fe283ba289 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."
@@ -1157,9 +1150,6 @@
"moveToOrganization": {
"message": "Move to organization"
},
- "share": {
- "message": "Share"
- },
"movedItemToOrg": {
"message": "$ITEMNAME$ moved to $ORGNAME$",
"placeholders": {
@@ -2047,9 +2037,6 @@
"clone": {
"message": "Clone"
},
- "passwordGeneratorPolicyInEffect": {
- "message": "One or more organization policies are affecting your generator settings."
- },
"passwordGenerator": {
"message": "Password generator"
},
@@ -2389,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"
},
@@ -2413,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"
},
@@ -2472,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."
@@ -2497,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"
},
@@ -2516,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."
@@ -2635,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."
},
@@ -2662,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"
},
@@ -2884,9 +2786,6 @@
"error": {
"message": "Error"
},
- "regenerateUsername": {
- "message": "Regenerate username"
- },
"generateUsername": {
"message": "Generate username"
},
@@ -2927,9 +2826,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 +2848,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/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 @@
-
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/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 73147cace23..0a6ff456938 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";
@@ -88,11 +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";
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";
@@ -173,13 +166,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,
@@ -339,15 +325,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,
@@ -425,21 +412,17 @@ const routes: Routes = [
data: { elevation: 1 } satisfies RouteDataProperties,
}),
{
- path: "send-type",
- component: SendTypeComponent,
+ path: "add-send",
+ component: SendAddEditV2Component,
canActivate: [authGuard],
data: { elevation: 1 } satisfies RouteDataProperties,
},
- ...extensionRefreshSwap(SendAddEditComponent, SendAddEditV2Component, {
- path: "add-send",
- 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,
@@ -650,8 +633,8 @@ const routes: Routes = [
],
},
{
- path: "lockV2",
- canActivate: [canAccessFeature(FeatureFlag.ExtensionRefresh), lockGuard()],
+ path: "lock",
+ canActivate: [lockGuard()],
data: {
pageIcon: LockIcon,
pageTitle: {
@@ -662,19 +645,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,
},
],
},
@@ -766,22 +749,24 @@ 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,
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 4695f0820b2..b547078084b 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";
@@ -57,12 +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";
-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";
@@ -156,21 +149,14 @@ import "../platform/popup/locales";
VaultFilterComponent,
HintComponent,
HomeComponent,
- LockComponent,
LoginViaAuthRequestComponentV1,
LoginComponentV1,
LoginDecryptionOptionsComponentV1,
NotificationsSettingsV1Component,
AppearanceComponent,
- GeneratorComponent,
- PasswordGeneratorHistoryComponent,
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/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 @@
-
-
-
-
- {{ "cancel" | i18n }}
-
-
-
- {{ "generator" | i18n }}
-
-
-
- {{ "select" | i18n }}
-
-
-
-
-
- {{ "passwordGeneratorPolicyInEffect" | i18n }}
-
-
-
-
-
-
-
-
-
-
- {{ o.name }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ o.name }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ o.name }}
-
- {{ o.desc }}
-
-
-
-
-
-
-
-
-
-
- {{ "emailAddress" | i18n }}
-
-
-
1"
- >
-
-
-
-
- {{ o.name }}
-
-
-
-
- {{ "website" | i18n }}
-
-
-
-
-
-
-
- {{ "domainName" | i18n }}
-
-
-
1"
- >
-
-
-
-
- {{ o.name }}
-
-
-
-
- {{ "website" | 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 @@
-
-
-
-
-
-
-
-
-
{{ 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();
- }
-}
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 @@
-
-
-
-
-
- {{ s.name }}
-
-
- {{ "disabled" | i18n }}
-
-
-
- {{ "passwordProtected" | i18n }}
-
-
-
- {{ "maxAccessCountReached" | i18n }}
-
-
-
- {{ "expired" | i18n }}
-
-
-
- {{ "pendingDeletion" | i18n }}
-
-
- {{ s.deletionDate | date: "medium" }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
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 @@
-
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 }}
-
- {{ "addSend" | i18n }}
-
-
-
-
-
-
-
-
-
-
-
{{ "sendTypeText" | i18n }}
-
- {{ getSendCount(sends, sendType.Text) }}
-
-
-
-
-
-
{{ "sendTypeFile" | i18n }}
-
- {{ getSendCount(sends, sendType.File) }}
-
-
-
-
-
-
-
-
-
{{ "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 @@
-
-
-
- {{ "sendDisabledWarning" | i18n }}
-
-
-
-
- {{ "noItemsInList" | i18n }}
-
- {{ "addSend" | i18n }}
-
-
-
-
-
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"],
- },
-);
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.
*/
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 }}
-
-
-
-
- {{ "pin" | i18n }}
-
-
-
- {{ "masterPass" | 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 }}
-
-
-
-
-
-
-
- {{ "unlock" | i18n }}
-
-
- {{ "logOut" | i18n }}
-
-
-
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/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) {
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);
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 87%
rename from libs/components/src/badge/badge.directive.ts
rename to libs/components/src/badge/badge.component.ts
index eef876a664d..0f8a64ee998 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",
@@ -62,8 +65,8 @@ export class BadgeDirective implements FocusableElement {
"disabled:tw-cursor-not-allowed",
]
.concat(styles[this.variant])
- .concat(this.hasHoverEffects ? hoverStyles[this.variant] : [])
- .concat(this.truncate ? ["tw-truncate", this.maxWidthClass] : []);
+ .concat(this.hasHoverEffects ? [...hoverStyles[this.variant], "tw-min-w-10"] : [])
+ .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 = {
- Auto-fill
+ Fill
@@ -163,7 +163,7 @@ const multipleActionListTemplate = /*html*/ `
- Auto-fill
+ Fill
@@ -182,7 +182,7 @@ const multipleActionListTemplate = /*html*/ `
- Auto-fill
+ Fill
@@ -201,7 +201,7 @@ const multipleActionListTemplate = /*html*/ `
- Auto-fill
+ Fill
@@ -220,7 +220,7 @@ const multipleActionListTemplate = /*html*/ `
- Auto-fill
+ Fill
@@ -239,7 +239,7 @@ const multipleActionListTemplate = /*html*/ `
- Auto-fill
+ Fill
@@ -258,7 +258,7 @@ const multipleActionListTemplate = /*html*/ `
- Auto-fill
+ Fill
@@ -332,14 +332,14 @@ export const SingleActionWithBadge: Story = {
Foobar
- Auto-fill
+ Fill
Helloooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo!
- Auto-fill
+ Fill
@@ -375,7 +375,7 @@ export const VirtualScrolling: Story = {
- Auto-fill
+ Fill
@@ -405,7 +405,7 @@ export const WithoutBorderRadius: Story = {
- Auto-fill
+ Fill