1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-10 21:50:15 +00:00

fix(misc-refactors-and-fixes): [PM-16783] Tech Debt and Toast Fixes - Initial changes being brought over from other branch.

This commit is contained in:
Patrick Pimentel
2025-01-30 15:56:50 -05:00
parent cbba1a686c
commit 6995e8d759
20 changed files with 95 additions and 106 deletions

View File

@@ -1,5 +1,3 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { Component, OnDestroy, OnInit, ViewChild } from "@angular/core";
import { FormBuilder, Validators } from "@angular/forms";
import { ActivatedRoute, Router } from "@angular/router";
@@ -10,6 +8,7 @@ import { LoginEmailServiceAbstraction } from "@bitwarden/auth/common";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { ToastService } from "@bitwarden/components";
@@ -25,7 +24,7 @@ export class HomeComponent implements OnInit, OnDestroy {
private destroyed$: Subject<void> = new Subject();
loginInitiated = false;
formGroup = this.formBuilder.group({
formGroup = this.formBuilder.nonNullable.group({
email: ["", [Validators.required, Validators.email]],
rememberEmail: [false],
});
@@ -40,6 +39,7 @@ export class HomeComponent implements OnInit, OnDestroy {
private toastService: ToastService,
private configService: ConfigService,
private route: ActivatedRoute,
private logService: LogService,
) {}
async ngOnInit(): Promise<void> {
@@ -107,7 +107,7 @@ export class HomeComponent implements OnInit, OnDestroy {
if (this.formGroup.invalid) {
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("errorOccured"),
title: this.i18nService.t("errorOccurred"),
message: this.i18nService.t("invalidEmail"),
});
return;

View File

@@ -1,4 +1,4 @@
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise" [formGroup]="formGroup">
<form #form (ngSubmit)="submitLogin()" [appApiAction]="formPromise" [formGroup]="formGroup">
<header>
<h1 class="login-center">
<span class="title">{{ "logIn" | i18n }}</span>

View File

@@ -1,5 +1,3 @@
// 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 { FormBuilder } from "@angular/forms";
import { ActivatedRoute, Router } from "@angular/router";
@@ -82,15 +80,9 @@ export class LoginComponentV1 extends BaseLoginComponent implements OnInit {
await this.validateEmail();
}
settings() {
// 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(["environment"]);
}
async launchSsoBrowser() {
// Save off email for SSO
await this.ssoLoginService.setSsoEmail(this.formGroup.value.email);
await this.ssoLoginService.setSsoEmail(this.formGroup.controls.email.value);
// Generate necessary sso params
const passwordOptions: any = {

View File

@@ -70,7 +70,7 @@ export class DesktopLoginComponentService
} catch (err) {
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("errorOccured"),
title: this.i18nService.t("errorOccurred"),
message: this.i18nService.t("ssoError"),
});
}

View File

@@ -50,7 +50,7 @@ export class LoginComponentV1 extends BaseLoginComponent implements OnInit, OnDe
private deferFocus: boolean = null;
get loggedEmail() {
return this.formGroup.value.email;
return this.formGroup.controls.email.value;
}
constructor(
@@ -191,7 +191,7 @@ export class LoginComponentV1 extends BaseLoginComponent implements OnInit, OnDe
if (!this.formGroup.controls.email.valid) {
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("errorOccured"),
title: this.i18nService.t("errorOccurred"),
message: this.i18nService.t("invalidEmail"),
});
return;
@@ -249,7 +249,7 @@ export class LoginComponentV1 extends BaseLoginComponent implements OnInit, OnDe
} catch (err) {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccured"),
this.i18nService.t("errorOccurred"),
this.i18nService.t("ssoError"),
);
}

View File

@@ -1,10 +1,7 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { Component, inject } from "@angular/core";
import { Params } from "@angular/router";
import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction";
import { OrganizationSponsorshipResponse } from "@bitwarden/common/admin-console/models/response/organization-sponsorship.response";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { ToastService } from "@bitwarden/components";
@@ -25,7 +22,6 @@ export class AcceptFamilySponsorshipComponent extends BaseAcceptComponent {
requiredParameters = ["email", "token"];
policyResponse!: OrganizationSponsorshipResponse;
policyApiService = inject(PolicyApiServiceAbstraction);
configService = inject(ConfigService);
toastService = inject(ToastService);

View File

@@ -1,5 +1,3 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { Component, OnDestroy, OnInit, ViewChild } from "@angular/core";
import { FormBuilder, Validators } from "@angular/forms";
import { ActivatedRoute, Router } from "@angular/router";
@@ -16,7 +14,6 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { PlanSponsorshipType, PlanType, ProductTierType } from "@bitwarden/common/billing/enums";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { DialogService, ToastService } from "@bitwarden/components";
@@ -64,7 +61,6 @@ export class FamiliesForEnterpriseSetupComponent implements OnInit, OnDestroy {
constructor(
private router: Router,
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService,
private route: ActivatedRoute,
private apiService: ApiService,
@@ -87,7 +83,6 @@ export class FamiliesForEnterpriseSetupComponent implements OnInit, OnDestroy {
variant: "error",
title: null,
message: this.i18nService.t("sponsoredFamiliesAcceptFailed"),
timeout: 10000,
});
// 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
@@ -105,7 +100,7 @@ export class FamiliesForEnterpriseSetupComponent implements OnInit, OnDestroy {
if (this.preValidateSponsorshipResponse.isFreeFamilyPolicyEnabled) {
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("errorOccured"),
title: this.i18nService.t("errorOccurred"),
message: this.i18nService.t("offerNoLongerValid"),
});
@@ -137,7 +132,7 @@ export class FamiliesForEnterpriseSetupComponent implements OnInit, OnDestroy {
}
});
this.formGroup.valueChanges.pipe(takeUntil(this._destroy)).subscribe((val) => {
this.selectedFamilyOrganizationId = val.selectedFamilyOrganizationId;
this.selectedFamilyOrganizationId = val.selectedFamilyOrganizationId ?? "";
});
}

View File

@@ -1,11 +1,9 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { Component } from "@angular/core";
import { ActivatedRoute, Params, Router } from "@angular/router";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { ToastService } from "@bitwarden/components";
import { BaseAcceptComponent } from "../../../common/base.accept.component";
import { SharedModule } from "../../../shared";
@@ -28,24 +26,23 @@ export class AcceptEmergencyComponent extends BaseAcceptComponent {
constructor(
router: Router,
platformUtilsService: PlatformUtilsService,
i18nService: I18nService,
route: ActivatedRoute,
authService: AuthService,
toastService: ToastService,
private emergencyAccessService: EmergencyAccessService,
) {
super(router, platformUtilsService, i18nService, route, authService);
super(router, i18nService, route, authService, toastService);
}
async authedHandler(qParams: Params): Promise<void> {
this.actionPromise = this.emergencyAccessService.accept(qParams.id, qParams.token);
await this.actionPromise;
this.platformUtilService.showToast(
"success",
this.i18nService.t("inviteAccepted"),
this.i18nService.t("emergencyInviteAcceptedDesc"),
{ timeout: 10000 },
);
this.toastService.showToast({
variant: "success",
title: this.i18nService.t("inviteAccepted"),
message: this.i18nService.t("emergencyInviteAcceptedDesc"),
});
// 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(["/vault"]);

View File

@@ -122,7 +122,7 @@
<hr />
<div class="tw-m-0 tw-text-sm">
<p class="tw-mb-1">{{ "loggingInAs" | i18n }} {{ loggedEmail }}</p>
<p class="tw-mb-1">{{ "loggingInAs" | i18n }} {{ this.formGroup.controls.email.value }}</p>
<a bitLink [routerLink]="[]" (click)="toggleValidateEmail(false)">{{ "notYou" | i18n }}</a>
</div>
</div>

View File

@@ -136,7 +136,7 @@ export class LoginComponentV1 extends BaseLoginComponent implements OnInit {
}
async goAfterLogIn(userId: UserId) {
const masterPassword = this.formGroup.value.masterPassword;
const masterPassword = this.formGroup.controls.masterPassword.value;
// Check master password against policy
if (this.enforcedPasswordPolicyOptions != null) {

View File

@@ -1,11 +1,10 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { Component } from "@angular/core";
import { ActivatedRoute, Params, Router } from "@angular/router";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { ToastService } from "@bitwarden/components";
import { BaseAcceptComponent } from "../../common/base.accept.component";
@@ -21,13 +20,14 @@ export class AcceptOrganizationComponent extends BaseAcceptComponent {
constructor(
router: Router,
platformUtilsService: PlatformUtilsService,
i18nService: I18nService,
route: ActivatedRoute,
authService: AuthService,
private toastService: ToastService,
private acceptOrganizationInviteService: AcceptOrganizationInviteService,
private logService: LogService,
) {
super(router, platformUtilsService, i18nService, route, authService);
super(router, i18nService, route, authService, toastService);
}
async authedHandler(qParams: Params): Promise<void> {
@@ -38,14 +38,13 @@ export class AcceptOrganizationComponent extends BaseAcceptComponent {
return;
}
this.platformUtilService.showToast(
"success",
this.i18nService.t("inviteAccepted"),
invite.initOrganization
this.toastService.showToast({
variant: "success",
title: this.i18nService.t("inviteAccepted"),
message: invite.initOrganization
? this.i18nService.t("inviteInitAcceptedDesc")
: this.i18nService.t("inviteAcceptedDesc"),
{ timeout: 10000 },
);
});
await this.router.navigate(["/vault"]);
}

View File

@@ -1,17 +1,15 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { Params } from "@angular/router";
import { Jsonify } from "type-fest";
export class OrganizationInvite {
email: string;
initOrganization: boolean;
orgSsoIdentifier: string;
orgUserHasExistingUser: boolean;
organizationId: string;
organizationName: string;
organizationUserId: string;
token: string;
email!: string;
initOrganization!: boolean;
orgSsoIdentifier!: string;
orgUserHasExistingUser!: boolean;
organizationId!: string;
organizationName!: string;
organizationUserId!: string;
token!: string;
static fromJSON(json: Jsonify<OrganizationInvite>): OrganizationInvite | null {
if (json == null) {
@@ -21,11 +19,7 @@ export class OrganizationInvite {
return Object.assign(new OrganizationInvite(), json);
}
static fromParams(params: Params): OrganizationInvite | null {
if (params == null) {
return null;
}
static fromParams(params: Params): OrganizationInvite {
return Object.assign(new OrganizationInvite(), {
email: params.email,
initOrganization: params.initOrganization?.toLocaleLowerCase() === "true",

View File

@@ -1,5 +1,3 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { Directive, OnInit } from "@angular/core";
import { ActivatedRoute, Params, Router } from "@angular/router";
import { Subject, firstValueFrom } from "rxjs";
@@ -8,7 +6,7 @@ import { first, switchMap, takeUntil } from "rxjs/operators";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { ToastService } from "@bitwarden/components";
@Directive()
export abstract class BaseAcceptComponent implements OnInit {
@@ -25,10 +23,10 @@ export abstract class BaseAcceptComponent implements OnInit {
constructor(
protected router: Router,
protected platformUtilService: PlatformUtilsService,
protected i18nService: I18nService,
protected route: ActivatedRoute,
protected authService: AuthService,
protected toastService: ToastService,
) {}
abstract authedHandler(qParams: Params): Promise<void>;
@@ -43,7 +41,7 @@ export abstract class BaseAcceptComponent implements OnInit {
let error = this.requiredParameters.some(
(e) => qParams?.[e] == null || qParams[e] === "",
);
let errorMessage: string = null;
let errorMessage: string | null = null;
if (!error) {
this.email = qParams.email;
@@ -51,7 +49,7 @@ export abstract class BaseAcceptComponent implements OnInit {
if (status !== AuthenticationStatus.LoggedOut) {
try {
await this.authedHandler(qParams);
} catch (e) {
} catch (e: { message: string } | any) {
error = true;
errorMessage = e.message;
}
@@ -65,7 +63,13 @@ export abstract class BaseAcceptComponent implements OnInit {
errorMessage != null
? this.i18nService.t(this.failedShortMessage, errorMessage)
: this.i18nService.t(this.failedMessage);
this.platformUtilService.showToast("error", null, message, { timeout: 10000 });
this.toastService.showToast({
variant: "error",
title: null,
message,
});
// 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(["/"]);

View File

@@ -1,5 +1,3 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { Component, OnInit } from "@angular/core";
import { FormBuilder } from "@angular/forms";
import { ActivatedRoute } from "@angular/router";

View File

@@ -1,5 +1,3 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { Component } from "@angular/core";
import { ActivatedRoute, Params, Router } from "@angular/router";
@@ -8,6 +6,7 @@ import { ProviderUserAcceptRequest } from "@bitwarden/common/admin-console/model
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { ToastService } from "@bitwarden/components";
import { BaseAcceptComponent } from "@bitwarden/web-vault/app/common/base.accept.component";
@Component({
@@ -30,9 +29,10 @@ export class AcceptProviderComponent extends BaseAcceptComponent {
route: ActivatedRoute,
authService: AuthService,
private apiService: ApiService,
toastService: ToastService,
platformUtilService: PlatformUtilsService,
) {
super(router, platformUtilService, i18nService, route, authService);
super(router, platformUtilService, i18nService, route, authService, toastService);
}
async authedHandler(qParams: Params) {
@@ -45,12 +45,12 @@ export class AcceptProviderComponent extends BaseAcceptComponent {
request,
);
this.platformUtilService.showToast(
"success",
this.i18nService.t("inviteAccepted"),
this.i18nService.t("providerInviteAcceptedDesc"),
{ timeout: 10000 },
);
this.toastService.showToast({
variant: "success",
title: this.i18nService.t("inviteAccepted"),
message: this.i18nService.t("providerInviteAcceptedDesc"),
});
this.router.navigate(["/vault"]);
}

View File

@@ -1,5 +1,3 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { Directive, ElementRef, NgZone, OnDestroy, OnInit, ViewChild } from "@angular/core";
import { FormBuilder, Validators } from "@angular/forms";
import { ActivatedRoute, NavigationSkipped, Router } from "@angular/router";
@@ -223,12 +221,16 @@ export class LoginComponentV1 extends CaptchaProtectedComponent implements OnIni
togglePassword() {
this.showPassword = !this.showPassword;
if (this.ngZone.isStable) {
document.getElementById("masterPassword").focus();
const masterPasswordElement = document.getElementById("masterPassword");
if (masterPasswordElement) {
if (this.ngZone.isStable) {
masterPasswordElement.focus();
} else {
this.ngZone.onStable.pipe(take(1)).subscribe(() => masterPasswordElement.focus());
}
} else {
this.ngZone.onStable
.pipe(take(1))
.subscribe(() => document.getElementById("masterPassword").focus());
this.logService.warning("Element with ID 'masterPassword' not found.");
}
}
@@ -246,7 +248,7 @@ export class LoginComponentV1 extends CaptchaProtectedComponent implements OnIni
async launchSsoBrowser(clientId: string, ssoRedirectUri: string) {
// Save off email for SSO
await this.ssoLoginService.setSsoEmail(this.formGroup.value.email);
await this.ssoLoginService.setSsoEmail(this.formGroup.controls.email.value);
// Generate necessary sso params
const passwordOptions: any = {
@@ -288,11 +290,11 @@ export class LoginComponentV1 extends CaptchaProtectedComponent implements OnIni
async validateEmail() {
this.formGroup.controls.email.markAsTouched();
const emailValid = this.formGroup.get("email").valid;
const emailValid = this.formGroup.controls.email.valid;
if (emailValid) {
this.toggleValidateEmail(true);
await this.getLoginWithDevice(this.loggedEmail);
await this.getLoginWithDevice(this.formGroup.controls.email.value);
}
}
@@ -339,8 +341,8 @@ export class LoginComponentV1 extends CaptchaProtectedComponent implements OnIni
}
protected async saveEmailSettings() {
this.loginEmailService.setLoginEmail(this.formGroup.value.email);
this.loginEmailService.setRememberEmail(this.formGroup.value.rememberEmail);
this.loginEmailService.setLoginEmail(this.formGroup.controls.email.value);
this.loginEmailService.setRememberEmail(this.formGroup.controls.rememberEmail.value);
await this.loginEmailService.saveEmailSettings();
}
@@ -352,14 +354,14 @@ export class LoginComponentV1 extends CaptchaProtectedComponent implements OnIni
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("errorOccured"),
title: this.i18nService.t("errorOccurred"),
message: this.i18nService.t("encryptionKeyMigrationRequired"),
});
return true;
}
private getErrorToastMessage() {
const error: AllValidationErrors = this.formValidationErrorService
private getErrorToastMessage(): string {
const error: AllValidationErrors | undefined = this.formValidationErrorService
.getFormValidationErrors(this.formGroup.controls)
.shift();
@@ -372,9 +374,12 @@ export class LoginComponentV1 extends CaptchaProtectedComponent implements OnIni
default:
return this.i18nService.t(this.errorTag(error));
}
} else {
this.logService.warning(
"formValidationErrorService produced no errors even when the form is invalid.",
);
return this.i18nService.t("errorOccurred");
}
return;
}
private errorTag(error: AllValidationErrors): string {

View File

@@ -268,7 +268,7 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("errorOccured"),
title: this.i18nService.t("errorOccurred"),
message: this.i18nService.t("encryptionKeyMigrationRequired"),
});
return true;

View File

@@ -263,7 +263,7 @@ export class LoginComponent implements OnInit, OnDestroy {
} else {
this.toastService.showToast({
variant: "error",
title: this.i18nService.t("errorOccured"),
title: this.i18nService.t("errorOccurred"),
message: this.i18nService.t("encryptionKeyMigrationRequired"),
});
}

View File

@@ -45,7 +45,7 @@ export class ToastComponent {
message: string | string[];
/** An optional title to display over the message. */
@Input() title: string;
@Input() title: string | null;
/**
* The percent width of the progress bar, from 0-100

View File

@@ -20,6 +20,15 @@ export type ToastOptions = {
export class ToastService {
constructor(private toastrService: ToastrService) {}
/**
* This will present the toast to a user.
*
* Note: The toast will be displayed for a minimum of 5 seconds if no timeout is provided in
* the options.
*
* @param options Options for toasts. If no timeout is specified an appropriate duration will be
* calculated on the size of the message being displayed.
*/
showToast(options: ToastOptions): void {
const toastrConfig: Partial<IndividualConfig> = {
payload: {