1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-06 00:13:28 +00:00
Files
browser/libs/auth/src/angular/registration/registration-start/registration-start.component.ts
Addison Beck 56a3b14583 Introduce eslint errors for risky/circular imports (#14804)
* first draft at an idea dependency graph

* ignore existing errors

* remove conflicting rule regarding internal platform logic in libs

* review: allow components to import from platform
2025-05-23 08:01:25 -04:00

213 lines
6.6 KiB
TypeScript

// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { CommonModule } from "@angular/common";
import { Component, EventEmitter, OnDestroy, OnInit, Output } from "@angular/core";
import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms";
import { ActivatedRoute, Router } from "@angular/router";
import { Subject, takeUntil } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { AccountApiService } from "@bitwarden/common/auth/abstractions/account-api.service";
import { RegisterSendVerificationEmailRequest } from "@bitwarden/common/auth/models/request/registration/register-send-verification-email.request";
import { RegionConfig, Region } from "@bitwarden/common/platform/abstractions/environment.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
// eslint-disable-next-line no-restricted-imports
import {
AsyncActionsModule,
ButtonModule,
CheckboxModule,
FormFieldModule,
IconModule,
LinkModule,
} from "@bitwarden/components";
import { LoginEmailService } from "../../../common";
import { AnonLayoutWrapperDataService } from "../../anon-layout/anon-layout-wrapper-data.service";
import { RegistrationUserAddIcon } from "../../icons";
import { RegistrationCheckEmailIcon } from "../../icons/registration-check-email.icon";
import { RegistrationEnvSelectorComponent } from "../registration-env-selector/registration-env-selector.component";
// FIXME: update to use a const object instead of a typescript enum
// eslint-disable-next-line @bitwarden/platform/no-enums
export enum RegistrationStartState {
USER_DATA_ENTRY = "UserDataEntry",
CHECK_EMAIL = "CheckEmail",
}
const DEFAULT_MARKETING_EMAILS_PREF_BY_REGION: Record<Region, boolean> = {
[Region.US]: true,
[Region.EU]: false,
[Region.SelfHosted]: false,
};
@Component({
standalone: true,
selector: "auth-registration-start",
templateUrl: "./registration-start.component.html",
imports: [
CommonModule,
ReactiveFormsModule,
JslibModule,
FormFieldModule,
AsyncActionsModule,
CheckboxModule,
ButtonModule,
LinkModule,
IconModule,
RegistrationEnvSelectorComponent,
],
})
export class RegistrationStartComponent implements OnInit, OnDestroy {
@Output() registrationStartStateChange = new EventEmitter<RegistrationStartState>();
state: RegistrationStartState = RegistrationStartState.USER_DATA_ENTRY;
RegistrationStartState = RegistrationStartState;
isSelfHost = false;
formGroup = this.formBuilder.group({
email: ["", [Validators.required, Validators.email]],
name: [""],
receiveMarketingEmails: [false],
});
get email() {
return this.formGroup.controls.email;
}
get name() {
return this.formGroup.controls.name;
}
get receiveMarketingEmails() {
return this.formGroup.controls.receiveMarketingEmails;
}
emailReadonly: boolean = false;
showErrorSummary = false;
private destroy$ = new Subject<void>();
constructor(
private formBuilder: FormBuilder,
private route: ActivatedRoute,
private platformUtilsService: PlatformUtilsService,
private accountApiService: AccountApiService,
private router: Router,
private loginEmailService: LoginEmailService,
private anonLayoutWrapperDataService: AnonLayoutWrapperDataService,
) {
this.isSelfHost = platformUtilsService.isSelfHost();
}
async ngOnInit() {
// Emit the initial state
this.registrationStartStateChange.emit(this.state);
this.listenForQueryParamChanges();
/**
* If the user has a login email, set the email field to the login email.
*/
this.loginEmailService.loginEmail$.pipe(takeUntil(this.destroy$)).subscribe((email) => {
if (email) {
this.formGroup.patchValue({ email });
}
});
}
private listenForQueryParamChanges() {
this.route.queryParams.pipe(takeUntil(this.destroy$)).subscribe((qParams) => {
if (qParams.email != null && qParams.email.indexOf("@") > -1) {
this.email?.setValue(qParams.email);
this.emailReadonly = qParams.emailReadonly === "true";
}
});
}
setReceiveMarketingEmailsByRegion(region: RegionConfig | Region.SelfHosted) {
let defaultValue;
if (region === Region.SelfHosted) {
defaultValue = DEFAULT_MARKETING_EMAILS_PREF_BY_REGION[region];
} else {
const regionKey = (region as RegionConfig).key;
defaultValue = DEFAULT_MARKETING_EMAILS_PREF_BY_REGION[regionKey];
}
this.receiveMarketingEmails.setValue(defaultValue);
}
submit = async () => {
const valid = this.validateForm();
if (!valid) {
return;
}
// The app expects null for name and not empty string.
const sanitizedName = this.name.value === "" ? null : this.name.value;
const request: RegisterSendVerificationEmailRequest = new RegisterSendVerificationEmailRequest(
this.email.value,
sanitizedName,
this.receiveMarketingEmails.value,
);
const result = await this.accountApiService.registerSendVerificationEmail(request);
if (typeof result === "string") {
// we received a token, so the env doesn't support email verification
// send the user directly to the finish registration page with the token as a query param
await this.router.navigate(["/finish-signup"], {
queryParams: { token: result, email: this.email.value },
});
}
// Result is null, so email verification is required
this.state = RegistrationStartState.CHECK_EMAIL;
this.anonLayoutWrapperDataService.setAnonLayoutWrapperData({
pageTitle: {
key: "checkYourEmail",
},
pageIcon: RegistrationCheckEmailIcon,
});
this.registrationStartStateChange.emit(this.state);
};
handleSelectedRegionChange(region: RegionConfig | Region.SelfHosted | null) {
this.isSelfHost = region === Region.SelfHosted;
if (region !== null) {
this.setReceiveMarketingEmailsByRegion(region);
}
}
private validateForm(): boolean {
this.formGroup.markAllAsTouched();
if (this.formGroup.invalid) {
this.showErrorSummary = true;
}
return this.formGroup.valid;
}
goBack() {
this.state = RegistrationStartState.USER_DATA_ENTRY;
this.anonLayoutWrapperDataService.setAnonLayoutWrapperData({
pageIcon: RegistrationUserAddIcon,
pageTitle: {
key: "createAccount",
},
});
this.registrationStartStateChange.emit(this.state);
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
}