All files / app/identity/reset-password reset-password.component.ts

100% Statements 15/15
100% Branches 3/3
100% Functions 2/2
100% Lines 15/15

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110                                                                              1x                       18x   18x                             18x 18x   18x         18x           18x               18x   18x 18x             2x   2x 1x     1x      
/* eslint-disable import-x/max-dependencies -- 11 dependencies */
import { AsyncPipe } from '@angular/common';
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import type { Signal } from '@angular/core';
import { FormGroup, ReactiveFormsModule } from '@angular/forms';
import type { FormControl, ValidationErrors } from '@angular/forms';
import { Router, RouterLink } from '@angular/router';
import type { Observable } from 'rxjs';
 
import { SpinnerComponent } from '@app/shared/spinner/spinner.component';
 
import { getState } from '../actions/get-state';
import { AuthErrorMessagesComponent } from '../auth-error-messages/auth-error-messages.component';
import { createPasswordControl, PASSWORDS } from '../identity-forms';
import { confirmMatch, confirmMatchFormErrors } from '../validators/confirm-match';
import { ResetPasswordService } from './reset-password.service';
import type { ResetPasswordResults } from './reset-password.service';
 
/** Collect and confirm a new password for user's account. */
type ResetPasswordFormGroup = FormGroup<{
  password1: FormControl<string | null>;
  password2: FormControl<string | null>;
}>;
 
/**
 * Collects and confirm new password to recover user's account with Firebase Authentication.
 */
@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [
    AsyncPipe,
    AuthErrorMessagesComponent,
    ReactiveFormsModule,
    RouterLink,
    SpinnerComponent,
  ],
  selector: 'app-reset-password',
  templateUrl: './reset-password.component.html',
})
export class ResetPasswordComponent {
  /** Form level aria-invalid. */
  public readonly $formPasswordsInvalid: Signal<boolean>;
  /** Errors specific to first password field. */
  public readonly $password1CntrlErrors: Signal<ValidationErrors | undefined>;
  /** Aria-invalid attribute for first password field. */
  public readonly $password1CntrlInvalid: Signal<boolean>;
  /** Errors specific to second password field. */
  public readonly $password2CntrlErrors: Signal<ValidationErrors | undefined>;
  /** Aria-invalid attribute for second password field */
  public readonly $password2CntrlInvalid: Signal<boolean>;
  /** Used in error message for password maximum length. */
  public readonly maxPasswordLength: number = PASSWORDS.maxLength;
  /** Used in error message for password minimum length. */
  public readonly minPasswordLength: number = PASSWORDS.minLength;
  public readonly password1Cntrl: FormControl<string | null>;
  public readonly password2Cntrl: FormControl<string | null>;
  public readonly resetPasswordForm: ResetPasswordFormGroup;
  /** Verification of password reset oobCode. */
  public readonly vm$: Observable<ResetPasswordResults>;
 
  private readonly _router: Router;
  private readonly _service: ResetPasswordService;
 
  /**
   * Gets the current navigation statically to obtain the oobCode from Firebase needed to reset the
   * User's password.
   */
  constructor() {
    this._router = inject(Router);
    this._service = inject(ResetPasswordService);
 
    ({
      $errors: this.$password1CntrlErrors,
      $invalid: this.$password1CntrlInvalid,
      control: this.password1Cntrl,
    } = createPasswordControl(true));
    ({
      $errors: this.$password2CntrlErrors,
      $invalid: this.$password2CntrlInvalid,
      control: this.password2Cntrl,
    } = createPasswordControl());
 
    this.resetPasswordForm = new FormGroup(
      {
        password1: this.password1Cntrl,
        password2: this.password2Cntrl,
      },
      confirmMatch('password1', 'password2'),
    );
 
    this.$formPasswordsInvalid = confirmMatchFormErrors(this.resetPasswordForm, this.password1Cntrl, this.password2Cntrl);
 
    const { oobCode } = getState(this._router.getCurrentNavigation());
    this.vm$ = this._service.resetPassword$(oobCode);
  }
 
  /**
   * Replace the user's password with the new password from the form.
   */
  public onSubmit(): void {
    const { password1 } = this.resetPasswordForm.value;
 
    if (this.resetPasswordForm.invalid || !password1) {
      throw new Error('Invalid form submitted');
    }
 
    this._service.replacePassword(password1);
  }
}