55 */
66
77import { Component , computed , inject , signal } from '@angular/core' ;
8- import { toSignal } from '@angular/core/rxjs-interop' ;
9- import {
10- FormControl ,
11- FormsModule ,
12- ReactiveFormsModule ,
13- Validators ,
14- } from '@angular/forms' ;
8+ import { form , FormField , maxLength , pattern , required } from '@angular/forms/signals' ;
159import { MatButtonModule } from '@angular/material/button' ;
1610import { MatFormFieldModule } from '@angular/material/form-field' ;
1711import { MatInputModule } from '@angular/material/input' ;
@@ -23,63 +17,67 @@ import { NotificationService } from '@app/shared/services/notification';
2317 */
2418@Component ( {
2519 selector : 'app-confirm-totp' ,
26- imports : [
27- FormsModule ,
28- ReactiveFormsModule ,
29- MatButtonModule ,
30- MatFormFieldModule ,
31- MatInputModule ,
32- ] ,
20+ imports : [ FormField , MatButtonModule , MatFormFieldModule , MatInputModule ] ,
3321 templateUrl : './confirm-totp.html' ,
3422 styleUrl : './confirm-totp.scss' ,
3523} )
3624export class ConfirmTotpComponent {
3725 #notify = inject ( NotificationService ) ;
3826 #authService = inject ( AuthService ) ;
3927
40- protected disabled = computed (
41- ( ) => this . #validInput ( ) !== 'VALID' || this . #isProcessing ( ) ,
42- ) ;
28+ protected totpModel = signal < { code : string } > ( {
29+ code : '' ,
30+ } ) ;
4331
44- #previousSubmission: string | undefined = undefined ;
32+ protected totpForm = form ( this . totpModel , ( schemaPath ) => {
33+ required ( schemaPath . code ) ;
34+ maxLength ( schemaPath . code , 6 ) ;
35+ pattern ( schemaPath . code , / ^ \d { 6 } $ / ) ;
36+ } ) ;
4537
46- #isProcessing = signal ( false ) ;
38+ #previousSubmission = signal ( '' ) ;
4739
48- protected codeControl = new FormControl < string > ( '' , [
49- Validators . required ,
50- Validators . pattern ( / ^ \d { 6 } $ / ) ,
51- ] ) ;
52- #validInput = toSignal ( this . codeControl . statusChanges , {
53- initialValue : this . codeControl . status ,
54- } ) ;
40+ #isProcessing = signal ( false ) ;
5541
5642 protected verificationError = signal ( false ) ;
5743
5844 allowNavigation = false ; // used by canDeactivate guard
5945
46+ protected disabled = computed < boolean > (
47+ ( ) =>
48+ ! this . totpForm . code ( ) . value ( ) ||
49+ this . totpForm . code ( ) . invalid ( ) ||
50+ this . #isProcessing( ) ||
51+ this . totpForm . code ( ) . value ( ) === this . #previousSubmission( ) ,
52+ ) ;
53+
6054 /**
6155 * Input handler for the TOTP code
6256 * @param event The input event object
6357 */
6458 onInput ( event : Event ) : void {
65- event . preventDefault ( ) ;
6659 const target = event . target as HTMLInputElement ;
67- target . value = target . value . replace ( / \D / g, '' ) . slice ( 0 , 6 ) ;
68- this . codeControl . setValue ( target . value ) ;
69- if ( ! this . codeControl . valid ) return ;
70- if ( this . codeControl . value === this . #previousSubmission) return ;
71- this . onSubmit ( ) ;
60+ const sanitized = target . value . replace ( / \D / g, '' ) . slice ( 0 , 6 ) ;
61+ target . value = sanitized ;
62+ this . totpForm . code ( ) . value . set ( sanitized ) ;
63+ // Reset error state when user starts typing
64+ this . verificationError . set ( false ) ;
65+ // Auto-submit only once
66+ if ( ! this . #previousSubmission( ) ) {
67+ this . onSubmit ( ) ;
68+ }
7269 }
7370
7471 /**
7572 * Submit authentication code
73+ * @param event the form submit event object
7674 */
77- async onSubmit ( ) : Promise < void > {
75+ async onSubmit ( event ?: Event ) : Promise < void > {
76+ event ?. preventDefault ( ) ;
7877 if ( this . disabled ( ) ) return ;
79- const code = this . codeControl . value ;
80- if ( ! code || ! this . codeControl . valid ) return ;
8178 this . #isProcessing. set ( true ) ;
82- this . #previousSubmission = code ;
79+ const code = this . totpForm . code ( ) . value ( ) ;
80+ this . #previousSubmission. set ( code ) ;
8381 const verified = await this . #authService. verifyTotpCode ( code ) ;
8482 if ( verified ) {
8583 this . #notify. showSuccess ( 'Successfully authenticated.' ) ;
0 commit comments