From 54cc35e29a97c93f0f31ad92fe29f2fd4cb8a900 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Fri, 13 Sep 2024 18:11:05 +0200 Subject: [PATCH 1/5] [PM-6037] Fix process reload not triggering on inactive account lock/logout (#9805) * Send loggedOut/locked events on logout/lock event * Revert "Send loggedOut/locked events on logout/lock event" This reverts commit 293f2d613171ce9f9d52db11d18e511a16dd54e0. * Ensure loggedOut is sent for non-active user logouts too * Make loggedOut accept userIds * Add userBeingLoggedOut in desktop app component * Await updateconnection calls --- apps/desktop/src/app/app.component.ts | 10 ++++------ apps/web/src/app/app.component.ts | 11 +++++++---- libs/common/src/auth/abstractions/auth.service.ts | 2 +- libs/common/src/auth/services/auth.service.ts | 4 ++-- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/apps/desktop/src/app/app.component.ts b/apps/desktop/src/app/app.component.ts index 089eb1c027c..b1f50a7b751 100644 --- a/apps/desktop/src/app/app.component.ts +++ b/apps/desktop/src/app/app.component.ts @@ -194,9 +194,9 @@ export class AppComponent implements OnInit, OnDestroy { break; case "loggedOut": this.modalService.closeAll(); - // 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.notificationsService.updateConnection(); + if (message.userId == null || message.userId === this.activeUserId) { + await this.notificationsService.updateConnection(); + } // 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.updateAppMenu(); @@ -694,9 +694,7 @@ export class AppComponent implements OnInit, OnDestroy { // This must come last otherwise the logout will prematurely trigger // a process reload before all the state service user data can be cleaned up - if (userBeingLoggedOut === activeUserId) { - this.authService.logOut(async () => {}); - } + this.authService.logOut(async () => {}, userBeingLoggedOut); } private async recordActivity() { diff --git a/apps/web/src/app/app.component.ts b/apps/web/src/app/app.component.ts index 828fe8ea3fe..ef6cbd2804a 100644 --- a/apps/web/src/app/app.component.ts +++ b/apps/web/src/app/app.component.ts @@ -119,9 +119,12 @@ export class AppComponent implements OnDestroy, OnInit { this.notificationsService.updateConnection(false); break; case "loggedOut": - // 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.notificationsService.updateConnection(false); + if ( + message.userId == null || + message.userId === (await firstValueFrom(this.accountService.activeAccount$)) + ) { + await this.notificationsService.updateConnection(false); + } break; case "unlocked": // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. @@ -311,7 +314,7 @@ export class AppComponent implements OnDestroy, OnInit { // eslint-disable-next-line @typescript-eslint/no-floating-promises this.router.navigate(["/"]); } - }); + }, userId); } private async recordActivity() { diff --git a/libs/common/src/auth/abstractions/auth.service.ts b/libs/common/src/auth/abstractions/auth.service.ts index 36d5d219b26..df408e76f8b 100644 --- a/libs/common/src/auth/abstractions/auth.service.ts +++ b/libs/common/src/auth/abstractions/auth.service.ts @@ -16,5 +16,5 @@ export abstract class AuthService { abstract authStatusFor$(userId: UserId): Observable; /** @deprecated use {@link activeAccountStatus$} instead */ abstract getAuthStatus: (userId?: string) => Promise; - abstract logOut: (callback: () => void) => void; + abstract logOut: (callback: () => void, userId?: string) => void; } diff --git a/libs/common/src/auth/services/auth.service.ts b/libs/common/src/auth/services/auth.service.ts index 25e7b92edf2..307da55a5eb 100644 --- a/libs/common/src/auth/services/auth.service.ts +++ b/libs/common/src/auth/services/auth.service.ts @@ -93,8 +93,8 @@ export class AuthService implements AuthServiceAbstraction { return await firstValueFrom(this.authStatusFor$(userId as UserId)); } - logOut(callback: () => void) { + logOut(callback: () => void, userId?: string): void { callback(); - this.messageSender.send("loggedOut"); + this.messageSender.send("loggedOut", { userId }); } } From 96d116d643ce825477e9e88a223c246058c0c36e Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Fri, 13 Sep 2024 09:16:25 -0700 Subject: [PATCH 2/5] [PM-8116] Auth Browser Refresh: Password Hint Component (#10492) * setup component, services, and web HTML * make Web and Browser functional * make desktop functional * update template to solidify common client HTML * simplify template and class * update browser routing * move canActivate to correct location * simplify post submit routing * update routing to use unauthUiRefreshSwap() * constrain AnonLayout title/subtitle width, reduce height on destkop to account for header * reduce height on browser to account for header (otherwise have to scroll to see EnvSelector * resolve email issue when clicking 'cancel' on extension popout * update routing for web * persist email to popout * update web router and anon-layout min-h based on client * change anchor link to button * remove unnecessary formatting changes * add new icon * remove unnecessary call to loginEmailService --- apps/browser/src/_locales/en/messages.json | 12 ++ apps/browser/src/popup/app-routing.module.ts | 46 +++++++- apps/browser/src/popup/app.module.ts | 2 + apps/desktop/src/app/app-routing.module.ts | 48 +++++++- apps/desktop/src/locales/en/messages.json | 12 ++ apps/web/src/app/oss-routing.module.ts | 67 +++++++---- apps/web/src/locales/en/messages.json | 12 ++ .../src/auth/components/login.component.ts | 1 + .../functions/unauth-ui-refresh-route-swap.ts | 2 + .../anon-layout/anon-layout.component.html | 8 +- libs/auth/src/angular/icons/index.ts | 1 + libs/auth/src/angular/icons/user-lock.icon.ts | 22 ++++ libs/auth/src/angular/index.ts | 7 +- .../password-hint.component.html | 40 +++++++ .../password-hint/password-hint.component.ts | 107 ++++++++++++++++++ 15 files changed, 357 insertions(+), 30 deletions(-) create mode 100644 libs/auth/src/angular/icons/user-lock.icon.ts create mode 100644 libs/auth/src/angular/password-hint/password-hint.component.html create mode 100644 libs/auth/src/angular/password-hint/password-hint.component.ts diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 47e89dcb44a..b8d3a2d47cd 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -179,6 +179,18 @@ "addItem": { "message": "Add item" }, + "accountEmail": { + "message": "Account email" + }, + "requestHint": { + "message": "Request hint" + }, + "requestPasswordHint": { + "message": "Request password hint" + }, + "enterYourAccountEmailAddressAndYourPasswordHintWillBeSentToYou": { + "message": "Enter your account email address and your password hint will be sent to you" + }, "passwordHint": { "message": "Password hint" }, diff --git a/apps/browser/src/popup/app-routing.module.ts b/apps/browser/src/popup/app-routing.module.ts index f715d38422d..0f6a9d9248d 100644 --- a/apps/browser/src/popup/app-routing.module.ts +++ b/apps/browser/src/popup/app-routing.module.ts @@ -1,6 +1,8 @@ import { Injectable, NgModule } from "@angular/core"; import { ActivatedRouteSnapshot, RouteReuseStrategy, RouterModule, Routes } from "@angular/router"; +import { EnvironmentSelectorComponent } from "@bitwarden/angular/auth/components/environment-selector.component"; +import { unauthUiRefreshSwap } from "@bitwarden/angular/auth/functions/unauth-ui-refresh-route-swap"; import { authGuard, lockGuard, @@ -15,11 +17,13 @@ import { extensionRefreshSwap } from "@bitwarden/angular/utils/extension-refresh import { AnonLayoutWrapperComponent, AnonLayoutWrapperData, + PasswordHintComponent, RegistrationFinishComponent, RegistrationStartComponent, RegistrationStartSecondaryComponent, RegistrationStartSecondaryComponentData, SetPasswordJitComponent, + UserLockIcon, } from "@bitwarden/auth/angular"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; @@ -27,6 +31,7 @@ import { twofactorRefactorSwap } from "../../../../libs/angular/src/utils/two-fa import { fido2AuthGuard } from "../auth/guards/fido2-auth.guard"; import { AccountSwitcherComponent } from "../auth/popup/account-switching/account-switcher.component"; 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"; @@ -213,12 +218,6 @@ const routes: Routes = [ canActivate: [unauthGuardFn(unauthRouteOverrides)], data: { state: "register" }, }, - { - path: "hint", - component: HintComponent, - canActivate: [unauthGuardFn(unauthRouteOverrides)], - data: { state: "hint" }, - }, { path: "environment", component: EnvironmentComponent, @@ -385,6 +384,41 @@ const routes: Routes = [ canActivate: [authGuard], data: { state: "update-temp-password" }, }, + ...unauthUiRefreshSwap( + HintComponent, + ExtensionAnonLayoutWrapperComponent, + { + path: "hint", + canActivate: [unauthGuardFn(unauthRouteOverrides)], + data: { + state: "hint", + }, + }, + { + path: "", + children: [ + { + path: "hint", + canActivate: [unauthGuardFn(unauthRouteOverrides)], + data: { + pageTitle: "requestPasswordHint", + pageSubtitle: "enterYourAccountEmailAddressAndYourPasswordHintWillBeSentToYou", + pageIcon: UserLockIcon, + showBackButton: true, + state: "hint", + }, + children: [ + { path: "", component: PasswordHintComponent }, + { + path: "", + component: EnvironmentSelectorComponent, + outlet: "environment-selector", + }, + ], + }, + ], + }, + ), { path: "", component: AnonLayoutWrapperComponent, diff --git a/apps/browser/src/popup/app.module.ts b/apps/browser/src/popup/app.module.ts index f8d3c691051..f14dafacb70 100644 --- a/apps/browser/src/popup/app.module.ts +++ b/apps/browser/src/popup/app.module.ts @@ -20,6 +20,7 @@ import { AvatarModule, ButtonModule, ToastModule } from "@bitwarden/components"; import { AccountComponent } from "../auth/popup/account-switching/account.component"; import { CurrentAccountComponent } from "../auth/popup/account-switching/current-account.component"; 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"; @@ -131,6 +132,7 @@ import "../platform/popup/locales"; HeaderComponent, UserVerificationDialogComponent, CurrentAccountComponent, + ExtensionAnonLayoutWrapperComponent, ], declarations: [ ActionButtonsComponent, diff --git a/apps/desktop/src/app/app-routing.module.ts b/apps/desktop/src/app/app-routing.module.ts index 2376eb3844b..2e44d2213e3 100644 --- a/apps/desktop/src/app/app-routing.module.ts +++ b/apps/desktop/src/app/app-routing.module.ts @@ -1,6 +1,8 @@ import { NgModule } from "@angular/core"; import { RouterModule, Routes } from "@angular/router"; +import { EnvironmentSelectorComponent } from "@bitwarden/angular/auth/components/environment-selector.component"; +import { unauthUiRefreshSwap } from "@bitwarden/angular/auth/functions/unauth-ui-refresh-route-swap"; import { authGuard, lockGuard, @@ -12,11 +14,13 @@ import { canAccessFeature } from "@bitwarden/angular/platform/guard/feature-flag import { AnonLayoutWrapperComponent, AnonLayoutWrapperData, + PasswordHintComponent, RegistrationFinishComponent, RegistrationStartComponent, RegistrationStartSecondaryComponent, RegistrationStartSecondaryComponentData, SetPasswordJitComponent, + UserLockIcon, } from "@bitwarden/auth/angular"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; @@ -94,7 +98,6 @@ const routes: Routes = [ canActivate: [authGuard], }, { path: "accessibility-cookie", component: AccessibilityCookieComponent }, - { path: "hint", component: HintComponent }, { path: "set-password", component: SetPasswordComponent }, { path: "sso", component: SsoComponent }, { @@ -113,10 +116,53 @@ const routes: Routes = [ canActivate: [authGuard], data: { titleId: "removeMasterPassword" }, }, + ...unauthUiRefreshSwap( + HintComponent, + AnonLayoutWrapperComponent, + { + path: "hint", + canActivate: [unauthGuardFn()], + data: { + pageTitle: "passwordHint", + titleId: "passwordHint", + }, + }, + { + path: "", + children: [ + { + path: "hint", + canActivate: [unauthGuardFn()], + data: { + pageTitle: "requestPasswordHint", + pageSubtitle: "enterYourAccountEmailAddressAndYourPasswordHintWillBeSentToYou", + pageIcon: UserLockIcon, + state: "hint", + }, + children: [ + { path: "", component: PasswordHintComponent }, + { + path: "", + component: EnvironmentSelectorComponent, + outlet: "environment-selector", + }, + ], + }, + ], + }, + ), { path: "", component: AnonLayoutWrapperComponent, children: [ + { + path: "hint", + component: PasswordHintComponent, + data: { + pageTitle: "requestPasswordHint", + pageSubtitle: "enterYourAccountEmailAddressAndYourPasswordHintWillBeSentToYou", + }, + }, { path: "signup", canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()], diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json index 5991fc4d06d..721faa25675 100644 --- a/apps/desktop/src/locales/en/messages.json +++ b/apps/desktop/src/locales/en/messages.json @@ -560,6 +560,18 @@ "settings": { "message": "Settings" }, + "accountEmail": { + "message": "Account email" + }, + "requestHint": { + "message": "Request hint" + }, + "requestPasswordHint": { + "message": "Request password hint" + }, + "enterYourAccountEmailAddressAndYourPasswordHintWillBeSentToYou": { + "message": "Enter your account email address and your password hint will be sent to you" + }, "passwordHint": { "message": "Password hint" }, diff --git a/apps/web/src/app/oss-routing.module.ts b/apps/web/src/app/oss-routing.module.ts index de0e8a2da93..b8d1502b6b7 100644 --- a/apps/web/src/app/oss-routing.module.ts +++ b/apps/web/src/app/oss-routing.module.ts @@ -1,6 +1,7 @@ import { NgModule } from "@angular/core"; import { Route, RouterModule, Routes } from "@angular/router"; +import { unauthUiRefreshSwap } from "@bitwarden/angular/auth/functions/unauth-ui-refresh-route-swap"; import { authGuard, lockGuard, @@ -12,13 +13,15 @@ import { canAccessFeature } from "@bitwarden/angular/platform/guard/feature-flag import { AnonLayoutWrapperComponent, AnonLayoutWrapperData, + PasswordHintComponent, RegistrationFinishComponent, RegistrationStartComponent, RegistrationStartSecondaryComponent, RegistrationStartSecondaryComponentData, SetPasswordJitComponent, - LockIcon, RegistrationLinkExpiredComponent, + LockIcon, + UserLockIcon, } from "@bitwarden/auth/angular"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; @@ -167,6 +170,49 @@ const routes: Routes = [ }, ], }, + ...unauthUiRefreshSwap( + AnonLayoutWrapperComponent, + AnonLayoutWrapperComponent, + { + path: "hint", + canActivate: [unauthGuardFn()], + data: { + pageTitle: "passwordHint", + titleId: "passwordHint", + }, + children: [ + { path: "", component: HintComponent }, + { + path: "", + component: EnvironmentSelectorComponent, + outlet: "environment-selector", + }, + ], + }, + { + path: "", + children: [ + { + path: "hint", + canActivate: [unauthGuardFn()], + data: { + pageTitle: "requestPasswordHint", + pageSubtitle: "enterYourAccountEmailAddressAndYourPasswordHintWillBeSentToYou", + pageIcon: UserLockIcon, + state: "hint", + }, + children: [ + { path: "", component: PasswordHintComponent }, + { + path: "", + component: EnvironmentSelectorComponent, + outlet: "environment-selector", + }, + ], + }, + ], + }, + ), { path: "", component: AnonLayoutWrapperComponent, @@ -388,25 +434,6 @@ const routes: Routes = [ }, ], }, - { - path: "hint", - canActivate: [unauthGuardFn()], - data: { - pageTitle: "passwordHint", - titleId: "passwordHint", - } satisfies DataProperties & AnonLayoutWrapperData, - children: [ - { - path: "", - component: HintComponent, - }, - { - path: "", - component: EnvironmentSelectorComponent, - outlet: "environment-selector", - }, - ], - }, { path: "remove-password", component: RemovePasswordComponent, diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 4a189a54d6f..d1d10dc9676 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -960,6 +960,18 @@ "settings": { "message": "Settings" }, + "accountEmail": { + "message": "Account email" + }, + "requestHint": { + "message": "Request hint" + }, + "requestPasswordHint": { + "message": "Request password hint" + }, + "enterYourAccountEmailAddressAndYourPasswordHintWillBeSentToYou": { + "message": "Enter your account email address and your password hint will be sent to you" + }, "passwordHint": { "message": "Password hint" }, diff --git a/libs/angular/src/auth/components/login.component.ts b/libs/angular/src/auth/components/login.component.ts index b798a8df0b4..831d505a388 100644 --- a/libs/angular/src/auth/components/login.component.ts +++ b/libs/angular/src/auth/components/login.component.ts @@ -317,6 +317,7 @@ export class LoginComponent extends CaptchaProtectedComponent implements OnInit, // Try to load from memory first const email = await firstValueFrom(this.loginEmailService.loginEmail$); const rememberEmail = this.loginEmailService.getRememberEmail(); + if (email) { this.formGroup.controls.email.setValue(email); this.formGroup.controls.rememberEmail.setValue(rememberEmail); diff --git a/libs/angular/src/auth/functions/unauth-ui-refresh-route-swap.ts b/libs/angular/src/auth/functions/unauth-ui-refresh-route-swap.ts index 45dad4a1a71..1146b7b40e3 100644 --- a/libs/angular/src/auth/functions/unauth-ui-refresh-route-swap.ts +++ b/libs/angular/src/auth/functions/unauth-ui-refresh-route-swap.ts @@ -20,6 +20,7 @@ export function unauthUiRefreshSwap( defaultComponent: Type, refreshedComponent: Type, options: Route, + altOptions?: Route, ): Routes { return componentRouteSwap( defaultComponent, @@ -29,5 +30,6 @@ export function unauthUiRefreshSwap( return configService.getFeatureFlag(FeatureFlag.UnauthenticatedExtensionUIRefresh); }, options, + altOptions, ); } diff --git a/libs/auth/src/angular/anon-layout/anon-layout.component.html b/libs/auth/src/angular/anon-layout/anon-layout.component.html index 6603fa970d4..9e6c27f6016 100644 --- a/libs/auth/src/angular/anon-layout/anon-layout.component.html +++ b/libs/auth/src/angular/anon-layout/anon-layout.component.html @@ -1,9 +1,12 @@ +
@@ -23,6 +26,7 @@ {{ title }} +
{{ subtitle }}
diff --git a/libs/auth/src/angular/icons/index.ts b/libs/auth/src/angular/icons/index.ts index f502fbe5f3d..cfcad992e34 100644 --- a/libs/auth/src/angular/icons/index.ts +++ b/libs/auth/src/angular/icons/index.ts @@ -1,4 +1,5 @@ export * from "./bitwarden-logo.icon"; export * from "./bitwarden-shield.icon"; export * from "./lock.icon"; +export * from "./user-lock.icon"; export * from "./user-verification-biometrics-fingerprint.icon"; diff --git a/libs/auth/src/angular/icons/user-lock.icon.ts b/libs/auth/src/angular/icons/user-lock.icon.ts new file mode 100644 index 00000000000..fef00a09a92 --- /dev/null +++ b/libs/auth/src/angular/icons/user-lock.icon.ts @@ -0,0 +1,22 @@ +import { svgIcon } from "@bitwarden/components"; + +export const UserLockIcon = svgIcon` + + + + + + + + + + + + + + + + + + +`; diff --git a/libs/auth/src/angular/index.ts b/libs/auth/src/angular/index.ts index f6a9ffde55f..bfb3a67aedc 100644 --- a/libs/auth/src/angular/index.ts +++ b/libs/auth/src/angular/index.ts @@ -16,7 +16,9 @@ export * from "./fingerprint-dialog/fingerprint-dialog.component"; // password callout export * from "./password-callout/password-callout.component"; -export * from "./vault-timeout-input/vault-timeout-input.component"; + +// password hint +export * from "./password-hint/password-hint.component"; // input password export * from "./input-password/input-password.component"; @@ -40,3 +42,6 @@ export * from "./registration/registration-start/registration-start-secondary.co export * from "./registration/registration-env-selector/registration-env-selector.component"; export * from "./registration/registration-finish/registration-finish.service"; export * from "./registration/registration-finish/default-registration-finish.service"; + +// vault timeout +export * from "./vault-timeout-input/vault-timeout-input.component"; diff --git a/libs/auth/src/angular/password-hint/password-hint.component.html b/libs/auth/src/angular/password-hint/password-hint.component.html new file mode 100644 index 00000000000..2a811a1b3b7 --- /dev/null +++ b/libs/auth/src/angular/password-hint/password-hint.component.html @@ -0,0 +1,40 @@ +
+ + + + {{ "accountEmail" | i18n }} + + + + + + + + +
+ +
+ + + + + +
diff --git a/libs/auth/src/angular/password-hint/password-hint.component.ts b/libs/auth/src/angular/password-hint/password-hint.component.ts new file mode 100644 index 00000000000..1ae1fd337b0 --- /dev/null +++ b/libs/auth/src/angular/password-hint/password-hint.component.ts @@ -0,0 +1,107 @@ +import { CommonModule } from "@angular/common"; +import { Component, OnInit } from "@angular/core"; +import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms"; +import { Router, RouterModule } from "@angular/router"; +import { firstValueFrom } from "rxjs"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { LoginEmailServiceAbstraction } from "@bitwarden/auth/common"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { PasswordHintRequest } from "@bitwarden/common/auth/models/request/password-hint.request"; +import { ClientType } from "@bitwarden/common/enums"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { + AsyncActionsModule, + ButtonModule, + FormFieldModule, + ToastService, +} from "@bitwarden/components"; + +@Component({ + standalone: true, + templateUrl: "./password-hint.component.html", + imports: [ + AsyncActionsModule, + ButtonModule, + CommonModule, + FormFieldModule, + JslibModule, + ReactiveFormsModule, + RouterModule, + ], +}) +export class PasswordHintComponent implements OnInit { + protected clientType: ClientType; + + protected formGroup = this.formBuilder.group({ + email: ["", [Validators.required, Validators.email]], + }); + + protected get email() { + return this.formGroup.controls.email.value; + } + + constructor( + private apiService: ApiService, + private formBuilder: FormBuilder, + private i18nService: I18nService, + private loginEmailService: LoginEmailServiceAbstraction, + private platformUtilsService: PlatformUtilsService, + private toastService: ToastService, + private router: Router, + ) { + this.clientType = this.platformUtilsService.getClientType(); + } + + async ngOnInit(): Promise { + const email = (await firstValueFrom(this.loginEmailService.loginEmail$)) ?? ""; + this.formGroup.controls.email.setValue(email); + } + + submit = async () => { + const isEmailValid = this.validateEmailOrShowToast(this.email); + if (!isEmailValid) { + return; + } + + await this.apiService.postPasswordHint(new PasswordHintRequest(this.email)); + + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("masterPassSent"), + }); + + await this.router.navigate(["login"]); + }; + + protected async cancel() { + this.loginEmailService.setLoginEmail(this.email); + await this.router.navigate(["login"]); + } + + private validateEmailOrShowToast(email: string): boolean { + // If email is null or empty, show error toast and return false + if (email == null || email === "") { + this.toastService.showToast({ + variant: "error", + title: this.i18nService.t("errorOccurred"), + message: this.i18nService.t("emailRequired"), + }); + return false; + } + + // If not a valid email format, show error toast and return false + if (email.indexOf("@") === -1) { + this.toastService.showToast({ + variant: "error", + title: this.i18nService.t("errorOccurred"), + message: this.i18nService.t("invalidEmail"), + }); + return false; + } + + return true; // email is valid + } +} From b713e18b1ab19c981f1d7af444798ef9242fc78f Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Fri, 13 Sep 2024 10:46:31 -0700 Subject: [PATCH 3/5] remove duplicate route (#11034) --- apps/desktop/src/app/app-routing.module.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/apps/desktop/src/app/app-routing.module.ts b/apps/desktop/src/app/app-routing.module.ts index 2e44d2213e3..f208da73559 100644 --- a/apps/desktop/src/app/app-routing.module.ts +++ b/apps/desktop/src/app/app-routing.module.ts @@ -155,14 +155,6 @@ const routes: Routes = [ path: "", component: AnonLayoutWrapperComponent, children: [ - { - path: "hint", - component: PasswordHintComponent, - data: { - pageTitle: "requestPasswordHint", - pageSubtitle: "enterYourAccountEmailAddressAndYourPasswordHintWillBeSentToYou", - }, - }, { path: "signup", canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()], From f816e8031442b80429fd931f7b688cb57db7e619 Mon Sep 17 00:00:00 2001 From: cyprain-okeke <108260115+cyprain-okeke@users.noreply.github.com> Date: Mon, 16 Sep 2024 10:14:46 +0100 Subject: [PATCH 4/5] [AC-3023] Resolve the capitalization here (#11019) * Resolve the capitalization here * Resolve the returned issue on A11y * Fix the storage bug --- .../change-plan-dialog.component.html | 13 ++++- .../change-plan-dialog.component.ts | 56 ++++++++++++++++++- apps/web/src/locales/en/messages.json | 2 +- 3 files changed, 66 insertions(+), 5 deletions(-) diff --git a/apps/web/src/app/billing/organizations/change-plan-dialog.component.html b/apps/web/src/app/billing/organizations/change-plan-dialog.component.html index 766646003ba..8420916c8e0 100644 --- a/apps/web/src/app/billing/organizations/change-plan-dialog.component.html +++ b/apps/web/src/app/billing/organizations/change-plan-dialog.component.html @@ -53,10 +53,19 @@ [class]="'tw-grid-cols-' + selectableProducts.length" >
{ + const card = cardElements[newIndex]; + if ( + !( + card.classList.contains("tw-bg-secondary-100") && + card.classList.contains("tw-text-muted") + ) + ) { + card?.focus(); + } + }, 0); + } + } + + onFocus(index: number) { + this.focusedIndex = index; + this.selectPlan(this.selectableProducts[index]); + } + + isCardDisabled(index: number): boolean { + const card = this.selectableProducts[index]; + return card === (this.currentPlan || this.isCardStateDisabled); + } + + manageSelectableProduct(index: number) { + return index; + } } diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index d1d10dc9676..340acc8efb5 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -9062,7 +9062,7 @@ "message": "Directory integration" }, "passwordLessSso": { - "message": "PasswordLess SSO" + "message": "Passwordless SSO" }, "accountRecovery": { "message": "Account recovery" From 74ee0315c061507cd4f755ec07ef8c6ed79ba371 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 12:37:58 +0200 Subject: [PATCH 5/5] [deps]: Update @yao-pkg/pkg to v5.14.0 (#11068) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package-lock.json | 16 ++++++++-------- package.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7cc733665a9..09cea6cacc1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -119,7 +119,7 @@ "@typescript-eslint/eslint-plugin": "7.16.1", "@typescript-eslint/parser": "7.16.1", "@webcomponents/custom-elements": "1.6.0", - "@yao-pkg/pkg": "5.12.1", + "@yao-pkg/pkg": "5.14.0", "autoprefixer": "10.4.20", "babel-loader": "9.1.3", "base64-loader": "1.0.0", @@ -10828,16 +10828,16 @@ "license": "Apache-2.0" }, "node_modules/@yao-pkg/pkg": { - "version": "5.12.1", - "resolved": "https://registry.npmjs.org/@yao-pkg/pkg/-/pkg-5.12.1.tgz", - "integrity": "sha512-vqp8Z9o39LDKTpjfeDjJsLf4mi0zS4jkbTTZbptfc/K1KKDU2hosex64TaattPO9NLkibc6EJldmSjVmc63ooA==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/@yao-pkg/pkg/-/pkg-5.14.0.tgz", + "integrity": "sha512-34oflUyAOI64a4cc4AF3ckvS8Qqnk/ISvZ1bDBa1/JAYaaFtzAO+RlhPaU+wCHzhk6VXvZwEywJpb+SlVDTgdA==", "dev": true, "license": "MIT", "dependencies": { "@babel/generator": "7.23.0", "@babel/parser": "7.23.0", "@babel/types": "7.23.0", - "@yao-pkg/pkg-fetch": "3.5.9", + "@yao-pkg/pkg-fetch": "3.5.11", "chalk": "^4.1.2", "fs-extra": "^9.1.0", "globby": "^11.1.0", @@ -10854,9 +10854,9 @@ } }, "node_modules/@yao-pkg/pkg-fetch": { - "version": "3.5.9", - "resolved": "https://registry.npmjs.org/@yao-pkg/pkg-fetch/-/pkg-fetch-3.5.9.tgz", - "integrity": "sha512-usMwwqFCd2B7k+V87u6kiTesyDSlw+3LpiuYBWe+UgryvSOk/NXjx3XVCub8hQoi0bCREbdQ6NDBqminyHJJrg==", + "version": "3.5.11", + "resolved": "https://registry.npmjs.org/@yao-pkg/pkg-fetch/-/pkg-fetch-3.5.11.tgz", + "integrity": "sha512-2tQ/1n7BLTptW6lL0pfTCnVMIxls8Jiw0/ClK1J2Fja9z2S2j4uzNL5dwGRqtvPJPn/q9i8X+Y+c4dwnMb+NOA==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 365508e31ef..1f73d180e1f 100644 --- a/package.json +++ b/package.json @@ -81,7 +81,7 @@ "@typescript-eslint/eslint-plugin": "7.16.1", "@typescript-eslint/parser": "7.16.1", "@webcomponents/custom-elements": "1.6.0", - "@yao-pkg/pkg": "5.12.1", + "@yao-pkg/pkg": "5.14.0", "autoprefixer": "10.4.20", "babel-loader": "9.1.3", "base64-loader": "1.0.0",