[PM-2414] Angular 16 Upgrade - SetPinComponent (#7214)
* migrate to DialogService * use static method * add reactive form dependencies * begin migrating to reactive forms * migrate template inputs to use CL * update set-pin.component.ts file to work with reactive forms * migrate desktop template and class file to Dialog and ReactiveForms * update settings page * remove old properties * update settings form upon dialog close * refactor ngOnInit() * remove duplicate validator (already have a validator in class file)
This commit is contained in:
parent
3d30823d2a
commit
00bb814fbe
|
@ -1,64 +1,29 @@
|
||||||
<div class="modal fade" role="dialog" aria-modal="true">
|
<form [bitSubmit]="submit" [formGroup]="setPinForm">
|
||||||
<div class="modal-dialog modal-dialog-scrollable" role="document">
|
<bit-dialog>
|
||||||
<form class="modal-content" #form (ngSubmit)="submit()">
|
<div class="tw-font-semibold" bitDialogTitle>
|
||||||
<div class="modal-body">
|
{{ "unlockWithPin" | i18n }}
|
||||||
<div>
|
</div>
|
||||||
{{ "setYourPinCode" | i18n }}
|
<div bitDialogContent>
|
||||||
</div>
|
<p>
|
||||||
<div class="box">
|
{{ "setYourPinCode" | i18n }}
|
||||||
<div class="box-content">
|
</p>
|
||||||
<div class="box-content-row box-content-row-flex no-hover no-bg" appBoxRow>
|
<bit-form-field>
|
||||||
<div class="row-main">
|
<bit-label>{{ "pin" | i18n }}</bit-label>
|
||||||
<label for="pin">{{ "pin" | i18n }}</label>
|
<input class="tw-font-mono" bitInput type="password" formControlName="pin" />
|
||||||
<input
|
<button type="button" bitIconButton bitSuffix bitPasswordInputToggle></button>
|
||||||
id="pin"
|
</bit-form-field>
|
||||||
type="{{ showPin ? 'text' : 'password' }}"
|
<label class="tw-flex tw-items-start tw-gap-2" *ngIf="showMasterPassOnRestart">
|
||||||
name="Pin"
|
<input class="tw-mt-1" type="checkbox" bitCheckbox formControlName="masterPassOnRestart" />
|
||||||
class="monospaced"
|
<span>{{ "lockWithMasterPassOnRestart" | i18n }}</span>
|
||||||
[(ngModel)]="pin"
|
</label>
|
||||||
required
|
</div>
|
||||||
appInputVerbatim
|
<div bitDialogFooter>
|
||||||
/>
|
<button type="submit" bitButton bitFormButton buttonType="primary">
|
||||||
</div>
|
<span>{{ "ok" | i18n }}</span>
|
||||||
<div class="action-buttons">
|
</button>
|
||||||
<button
|
<button type="button" bitButton bitFormButton buttonType="secondary" bitDialogClose>
|
||||||
type="button"
|
{{ "cancel" | i18n }}
|
||||||
class="row-btn"
|
</button>
|
||||||
appStopClick
|
</div>
|
||||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
</bit-dialog>
|
||||||
(click)="toggleVisibility()"
|
</form>
|
||||||
[attr.aria-pressed]="showPin"
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
class="bwi bwi-lg"
|
|
||||||
aria-hidden="true"
|
|
||||||
[ngClass]="{ 'bwi-eye': !showPin, 'bwi-eye-slash': showPin }"
|
|
||||||
></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="checkbox" *ngIf="showMasterPassOnRestart">
|
|
||||||
<label for="masterPasswordOnRestart">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
id="masterPasswordOnRestart"
|
|
||||||
name="MasterPasswordOnRestart"
|
|
||||||
[(ngModel)]="masterPassOnRestart"
|
|
||||||
/>
|
|
||||||
<span>{{ "lockWithMasterPassOnRestart" | i18n }}</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="submit" class="btn btn-primary btn-submit">
|
|
||||||
<span>{{ "ok" | i18n }}</span>
|
|
||||||
</button>
|
|
||||||
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">
|
|
||||||
{{ "cancel" | i18n }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
|
@ -1,8 +1,34 @@
|
||||||
|
import { CommonModule } from "@angular/common";
|
||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
|
import { ReactiveFormsModule } from "@angular/forms";
|
||||||
|
|
||||||
import { SetPinComponent as BaseSetPinComponent } from "@bitwarden/angular/auth/components/set-pin.component";
|
import { SetPinComponent as BaseSetPinComponent } from "@bitwarden/angular/auth/components/set-pin.component";
|
||||||
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
|
import {
|
||||||
|
AsyncActionsModule,
|
||||||
|
ButtonModule,
|
||||||
|
DialogModule,
|
||||||
|
DialogService,
|
||||||
|
FormFieldModule,
|
||||||
|
IconButtonModule,
|
||||||
|
} from "@bitwarden/components";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
standalone: true,
|
||||||
templateUrl: "set-pin.component.html",
|
templateUrl: "set-pin.component.html",
|
||||||
|
imports: [
|
||||||
|
DialogModule,
|
||||||
|
CommonModule,
|
||||||
|
JslibModule,
|
||||||
|
ButtonModule,
|
||||||
|
IconButtonModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
AsyncActionsModule,
|
||||||
|
FormFieldModule,
|
||||||
|
],
|
||||||
})
|
})
|
||||||
export class SetPinComponent extends BaseSetPinComponent {}
|
export class SetPinComponent extends BaseSetPinComponent {
|
||||||
|
static open(dialogService: DialogService) {
|
||||||
|
return dialogService.open<boolean>(SetPinComponent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -20,7 +20,6 @@ import { AvatarModule } from "@bitwarden/components";
|
||||||
import { AccountSwitcherComponent } from "../auth/popup/account-switching/account-switcher.component";
|
import { AccountSwitcherComponent } from "../auth/popup/account-switching/account-switcher.component";
|
||||||
import { AccountComponent } from "../auth/popup/account-switching/account.component";
|
import { AccountComponent } from "../auth/popup/account-switching/account.component";
|
||||||
import { CurrentAccountComponent } from "../auth/popup/account-switching/current-account.component";
|
import { CurrentAccountComponent } from "../auth/popup/account-switching/current-account.component";
|
||||||
import { SetPinComponent } from "../auth/popup/components/set-pin.component";
|
|
||||||
import { EnvironmentComponent } from "../auth/popup/environment.component";
|
import { EnvironmentComponent } from "../auth/popup/environment.component";
|
||||||
import { HintComponent } from "../auth/popup/hint.component";
|
import { HintComponent } from "../auth/popup/hint.component";
|
||||||
import { HomeComponent } from "../auth/popup/home.component";
|
import { HomeComponent } from "../auth/popup/home.component";
|
||||||
|
@ -148,7 +147,6 @@ import "../platform/popup/locales";
|
||||||
SendListComponent,
|
SendListComponent,
|
||||||
SendTypeComponent,
|
SendTypeComponent,
|
||||||
SetPasswordComponent,
|
SetPasswordComponent,
|
||||||
SetPinComponent,
|
|
||||||
SettingsComponent,
|
SettingsComponent,
|
||||||
ShareComponent,
|
ShareComponent,
|
||||||
SsoComponent,
|
SsoComponent,
|
||||||
|
|
|
@ -316,14 +316,15 @@ export class SettingsComponent implements OnInit {
|
||||||
|
|
||||||
async updatePin(value: boolean) {
|
async updatePin(value: boolean) {
|
||||||
if (value) {
|
if (value) {
|
||||||
const ref = this.modalService.open(SetPinComponent, { allowMultipleModals: true });
|
const dialogRef = SetPinComponent.open(this.dialogService);
|
||||||
|
|
||||||
if (ref == null) {
|
if (dialogRef == null) {
|
||||||
this.form.controls.pin.setValue(false, { emitEvent: false });
|
this.form.controls.pin.setValue(false, { emitEvent: false });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.form.controls.pin.setValue(await ref.onClosedPromise(), { emitEvent: false });
|
const userHasPinSet = await firstValueFrom(dialogRef.closed);
|
||||||
|
this.form.controls.pin.setValue(userHasPinSet, { emitEvent: false });
|
||||||
} else {
|
} else {
|
||||||
await this.vaultTimeoutSettingsService.clear();
|
await this.vaultTimeoutSettingsService.clear();
|
||||||
}
|
}
|
||||||
|
|
|
@ -409,16 +409,17 @@ export class SettingsComponent implements OnInit {
|
||||||
|
|
||||||
async updatePin(value: boolean) {
|
async updatePin(value: boolean) {
|
||||||
if (value) {
|
if (value) {
|
||||||
const ref = this.modalService.open(SetPinComponent, { allowMultipleModals: true });
|
const dialogRef = SetPinComponent.open(this.dialogService);
|
||||||
|
|
||||||
if (ref == null) {
|
if (dialogRef == null) {
|
||||||
this.form.controls.pin.setValue(false, { emitEvent: false });
|
this.form.controls.pin.setValue(false, { emitEvent: false });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.userHasPinSet = await ref.onClosedPromise();
|
this.userHasPinSet = await firstValueFrom(dialogRef.closed);
|
||||||
this.form.controls.pin.setValue(this.userHasPinSet, { emitEvent: false });
|
this.form.controls.pin.setValue(this.userHasPinSet, { emitEvent: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!value) {
|
if (!value) {
|
||||||
// If user turned off PIN without having a MP and has biometric + require MP/PIN on restart enabled
|
// If user turned off PIN without having a MP and has biometric + require MP/PIN on restart enabled
|
||||||
if (this.form.value.requirePasswordOnStart && !this.userHasMasterPassword) {
|
if (this.form.value.requirePasswordOnStart && !this.userHasMasterPassword) {
|
||||||
|
@ -429,6 +430,7 @@ export class SettingsComponent implements OnInit {
|
||||||
|
|
||||||
await this.vaultTimeoutSettingsService.clear();
|
await this.vaultTimeoutSettingsService.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.messagingService.send("redrawMenu");
|
this.messagingService.send("redrawMenu");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ import { ColorPasswordPipe } from "@bitwarden/angular/pipes/color-password.pipe"
|
||||||
import { DialogModule } from "@bitwarden/components";
|
import { DialogModule } from "@bitwarden/components";
|
||||||
|
|
||||||
import { AccessibilityCookieComponent } from "../auth/accessibility-cookie.component";
|
import { AccessibilityCookieComponent } from "../auth/accessibility-cookie.component";
|
||||||
import { SetPinComponent } from "../auth/components/set-pin.component";
|
|
||||||
import { DeleteAccountComponent } from "../auth/delete-account.component";
|
import { DeleteAccountComponent } from "../auth/delete-account.component";
|
||||||
import { EnvironmentComponent } from "../auth/environment.component";
|
import { EnvironmentComponent } from "../auth/environment.component";
|
||||||
import { HintComponent } from "../auth/hint.component";
|
import { HintComponent } from "../auth/hint.component";
|
||||||
|
@ -92,7 +91,6 @@ import { SendComponent } from "./tools/send/send.component";
|
||||||
SendAddEditComponent,
|
SendAddEditComponent,
|
||||||
SendComponent,
|
SendComponent,
|
||||||
SetPasswordComponent,
|
SetPasswordComponent,
|
||||||
SetPinComponent,
|
|
||||||
SettingsComponent,
|
SettingsComponent,
|
||||||
ShareComponent,
|
ShareComponent,
|
||||||
SsoComponent,
|
SsoComponent,
|
||||||
|
|
|
@ -1,65 +1,29 @@
|
||||||
<div class="modal fade" role="dialog" aria-modal="true">
|
<form [bitSubmit]="submit" [formGroup]="setPinForm">
|
||||||
<div class="modal-dialog modal-dialog-scrollable set-pin-modal" role="document">
|
<bit-dialog>
|
||||||
<form class="modal-content" #form (ngSubmit)="submit()">
|
<div class="tw-font-semibold" bitDialogTitle>
|
||||||
<div class="modal-body">
|
{{ "unlockWithPin" | i18n }}
|
||||||
<div>
|
</div>
|
||||||
{{ "setYourPinCode" | i18n }}
|
<div bitDialogContent>
|
||||||
</div>
|
<p>
|
||||||
<div class="box">
|
{{ "setYourPinCode" | i18n }}
|
||||||
<div class="box-content">
|
</p>
|
||||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
<bit-form-field>
|
||||||
<div class="row-main">
|
<bit-label>{{ "pin" | i18n }}</bit-label>
|
||||||
<label for="pin">{{ "pin" | i18n }}</label>
|
<input class="tw-font-mono" bitInput type="password" formControlName="pin" />
|
||||||
<input
|
<button type="button" bitIconButton bitSuffix bitPasswordInputToggle></button>
|
||||||
id="pin"
|
</bit-form-field>
|
||||||
type="{{ showPin ? 'text' : 'password' }}"
|
<label class="tw-flex tw-items-start tw-gap-2" *ngIf="showMasterPassOnRestart">
|
||||||
name="Pin"
|
<input class="tw-mt-1" type="checkbox" bitCheckbox formControlName="masterPassOnRestart" />
|
||||||
class="monospaced"
|
<span>{{ "lockWithMasterPassOnRestart" | i18n }}</span>
|
||||||
[(ngModel)]="pin"
|
</label>
|
||||||
required
|
</div>
|
||||||
appInputVerbatim
|
<div bitDialogFooter>
|
||||||
appAutofocus
|
<button type="submit" bitButton bitFormButton buttonType="primary">
|
||||||
/>
|
<span>{{ "ok" | i18n }}</span>
|
||||||
</div>
|
</button>
|
||||||
<div class="action-buttons">
|
<button type="button" bitButton bitFormButton buttonType="secondary" bitDialogClose>
|
||||||
<button
|
{{ "cancel" | i18n }}
|
||||||
type="button"
|
</button>
|
||||||
class="row-btn"
|
</div>
|
||||||
appStopClick
|
</bit-dialog>
|
||||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
</form>
|
||||||
[attr.aria-pressed]="showPin"
|
|
||||||
(click)="toggleVisibility()"
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
class="bwi bwi-lg"
|
|
||||||
aria-hidden="true"
|
|
||||||
[ngClass]="{ 'bwi-eye': !showPin, 'bwi-eye-slash': showPin }"
|
|
||||||
></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="checkbox" *ngIf="showMasterPassOnRestart">
|
|
||||||
<label for="masterPasswordOnRestart">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
id="masterPasswordOnRestart"
|
|
||||||
name="MasterPasswordOnRestart"
|
|
||||||
[(ngModel)]="masterPassOnRestart"
|
|
||||||
/>
|
|
||||||
<span>{{ "lockWithMasterPassOnRestart" | i18n }}</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="submit" class="btn btn-primary btn-submit">
|
|
||||||
<span>{{ "ok" | i18n }}</span>
|
|
||||||
</button>
|
|
||||||
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">
|
|
||||||
{{ "cancel" | i18n }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
|
@ -1,8 +1,33 @@
|
||||||
|
import { CommonModule } from "@angular/common";
|
||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
|
import { ReactiveFormsModule } from "@angular/forms";
|
||||||
|
|
||||||
import { SetPinComponent as BaseSetPinComponent } from "@bitwarden/angular/auth/components/set-pin.component";
|
import { SetPinComponent as BaseSetPinComponent } from "@bitwarden/angular/auth/components/set-pin.component";
|
||||||
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
|
import {
|
||||||
|
AsyncActionsModule,
|
||||||
|
ButtonModule,
|
||||||
|
DialogModule,
|
||||||
|
DialogService,
|
||||||
|
FormFieldModule,
|
||||||
|
IconButtonModule,
|
||||||
|
} from "@bitwarden/components";
|
||||||
@Component({
|
@Component({
|
||||||
|
standalone: true,
|
||||||
templateUrl: "set-pin.component.html",
|
templateUrl: "set-pin.component.html",
|
||||||
|
imports: [
|
||||||
|
DialogModule,
|
||||||
|
CommonModule,
|
||||||
|
JslibModule,
|
||||||
|
ButtonModule,
|
||||||
|
IconButtonModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
AsyncActionsModule,
|
||||||
|
FormFieldModule,
|
||||||
|
],
|
||||||
})
|
})
|
||||||
export class SetPinComponent extends BaseSetPinComponent {}
|
export class SetPinComponent extends BaseSetPinComponent {
|
||||||
|
static open(dialogService: DialogService) {
|
||||||
|
return dialogService.open<boolean>(SetPinComponent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,57 +1,63 @@
|
||||||
|
import { DialogRef } from "@angular/cdk/dialog";
|
||||||
import { Directive, OnInit } from "@angular/core";
|
import { Directive, OnInit } from "@angular/core";
|
||||||
|
import { FormBuilder, Validators } from "@angular/forms";
|
||||||
|
|
||||||
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
||||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
|
|
||||||
import { ModalRef } from "../../components/modal/modal.ref";
|
|
||||||
|
|
||||||
@Directive()
|
@Directive()
|
||||||
export class SetPinComponent implements OnInit {
|
export class SetPinComponent implements OnInit {
|
||||||
pin = "";
|
|
||||||
showPin = false;
|
|
||||||
masterPassOnRestart = true;
|
|
||||||
showMasterPassOnRestart = true;
|
showMasterPassOnRestart = true;
|
||||||
|
|
||||||
|
setPinForm = this.formBuilder.group({
|
||||||
|
pin: ["", [Validators.required]],
|
||||||
|
masterPassOnRestart: true,
|
||||||
|
});
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private modalRef: ModalRef,
|
private dialogRef: DialogRef,
|
||||||
private cryptoService: CryptoService,
|
private cryptoService: CryptoService,
|
||||||
private userVerificationService: UserVerificationService,
|
private userVerificationService: UserVerificationService,
|
||||||
private stateService: StateService,
|
private stateService: StateService,
|
||||||
|
private formBuilder: FormBuilder,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.showMasterPassOnRestart = this.masterPassOnRestart =
|
const hasMasterPassword = await this.userVerificationService.hasMasterPassword();
|
||||||
await this.userVerificationService.hasMasterPassword();
|
|
||||||
|
this.setPinForm.controls.masterPassOnRestart.setValue(hasMasterPassword);
|
||||||
|
this.showMasterPassOnRestart = hasMasterPassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleVisibility() {
|
submit = async () => {
|
||||||
this.showPin = !this.showPin;
|
const pin = this.setPinForm.get("pin").value;
|
||||||
}
|
const masterPassOnRestart = this.setPinForm.get("masterPassOnRestart").value;
|
||||||
|
|
||||||
async submit() {
|
if (Utils.isNullOrWhitespace(pin)) {
|
||||||
if (Utils.isNullOrWhitespace(this.pin)) {
|
this.dialogRef.close(false);
|
||||||
this.modalRef.close(false);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const pinKey = await this.cryptoService.makePinKey(
|
const pinKey = await this.cryptoService.makePinKey(
|
||||||
this.pin,
|
pin,
|
||||||
await this.stateService.getEmail(),
|
await this.stateService.getEmail(),
|
||||||
await this.stateService.getKdfType(),
|
await this.stateService.getKdfType(),
|
||||||
await this.stateService.getKdfConfig(),
|
await this.stateService.getKdfConfig(),
|
||||||
);
|
);
|
||||||
const userKey = await this.cryptoService.getUserKey();
|
const userKey = await this.cryptoService.getUserKey();
|
||||||
const pinProtectedKey = await this.cryptoService.encrypt(userKey.key, pinKey);
|
const pinProtectedKey = await this.cryptoService.encrypt(userKey.key, pinKey);
|
||||||
const encPin = await this.cryptoService.encrypt(this.pin, userKey);
|
const encPin = await this.cryptoService.encrypt(pin, userKey);
|
||||||
|
|
||||||
await this.stateService.setProtectedPin(encPin.encryptedString);
|
await this.stateService.setProtectedPin(encPin.encryptedString);
|
||||||
if (this.masterPassOnRestart) {
|
|
||||||
|
if (masterPassOnRestart) {
|
||||||
await this.stateService.setPinKeyEncryptedUserKeyEphemeral(pinProtectedKey);
|
await this.stateService.setPinKeyEncryptedUserKeyEphemeral(pinProtectedKey);
|
||||||
} else {
|
} else {
|
||||||
await this.stateService.setPinKeyEncryptedUserKey(pinProtectedKey);
|
await this.stateService.setPinKeyEncryptedUserKey(pinProtectedKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.modalRef.close(true);
|
this.dialogRef.close(true);
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue