1- import React , { useEffect , useState } from 'react' ;
2- import { Dialog , DialogProps } from '@/components/elements/dialog' ;
1+ import React , { useContext , useEffect , useState } from 'react' ;
2+ import { Dialog , DialogWrapperContext } from '@/components/elements/dialog' ;
33import getTwoFactorTokenData , { TwoFactorTokenData } from '@/api/account/getTwoFactorTokenData' ;
44import { useFlashKey } from '@/plugins/useFlash' ;
55import tw from 'twin.macro' ;
@@ -11,39 +11,28 @@ import CopyOnClick from '@/components/elements/CopyOnClick';
1111import Tooltip from '@/components/elements/tooltip/Tooltip' ;
1212import enableAccountTwoFactor from '@/api/account/enableAccountTwoFactor' ;
1313import FlashMessageRender from '@/components/FlashMessageRender' ;
14- import RecoveryTokensDialog from '@/components/dashboard/forms/RecoveryTokensDialog' ;
1514import { Actions , useStoreActions } from 'easy-peasy' ;
1615import { ApplicationStore } from '@/state' ;
16+ import asDialog from '@/hoc/asDialog' ;
1717
18- type SetupTOTPModalProps = DialogProps ;
18+ interface Props {
19+ onTokens : ( tokens : string [ ] ) => void ;
20+ }
1921
20- export default ( { open , onClose } : SetupTOTPModalProps ) => {
22+ const ConfigureTwoFactorForm = ( { onTokens } : Props ) => {
2123 const [ submitting , setSubmitting ] = useState ( false ) ;
2224 const [ value , setValue ] = useState ( '' ) ;
23- const [ tokens , setTokens ] = useState < string [ ] > ( [ ] ) ;
2425 const [ token , setToken ] = useState < TwoFactorTokenData | null > ( null ) ;
2526 const { clearAndAddHttpError } = useFlashKey ( 'account:two-step' ) ;
2627 const updateUserData = useStoreActions ( ( actions : Actions < ApplicationStore > ) => actions . user . updateUserData ) ;
2728
28- useEffect ( ( ) => {
29- if ( ! open ) return ;
29+ const { close } = useContext ( DialogWrapperContext ) ;
3030
31+ useEffect ( ( ) => {
3132 getTwoFactorTokenData ( )
3233 . then ( setToken )
33- . then ( ( ) => updateUserData ( { useTotp : true } ) )
3434 . catch ( ( error ) => clearAndAddHttpError ( error ) ) ;
35- } , [ open ] ) ;
36-
37- useEffect ( ( ) => {
38- if ( ! open ) return ;
39-
40- return ( ) => {
41- setToken ( null ) ;
42- setValue ( '' ) ;
43- setSubmitting ( false ) ;
44- clearAndAddHttpError ( undefined ) ;
45- } ;
46- } , [ open ] ) ;
35+ } , [ ] ) ;
4736
4837 const submit = ( ) => {
4938 if ( submitting ) return ;
@@ -52,76 +41,68 @@ export default ({ open, onClose }: SetupTOTPModalProps) => {
5241 clearAndAddHttpError ( ) ;
5342
5443 enableAccountTwoFactor ( value )
55- . then ( setTokens )
56- . catch ( clearAndAddHttpError )
57- . then ( ( ) => setSubmitting ( false ) ) ;
44+ . then ( ( tokens ) => {
45+ updateUserData ( { useTotp : true } ) ;
46+ onTokens ( tokens ) ;
47+ } )
48+ . catch ( ( error ) => {
49+ clearAndAddHttpError ( error ) ;
50+ setSubmitting ( false ) ;
51+ } ) ;
5852 } ;
5953
6054 return (
6155 < >
62- < RecoveryTokensDialog tokens = { tokens } open = { open && tokens . length > 0 } onClose = { onClose } />
63- < Dialog
64- open = { open && ! tokens . length }
65- onClose = { onClose }
66- title = { 'Enable Two-Step Verification' }
67- preventExternalClose = { submitting }
68- description = {
69- "Help protect your account from unauthorized access. You'll be prompted for a verification code each time you sign in."
70- }
56+ < FlashMessageRender byKey = { 'account:two-step' } className = { 'mt-4' } />
57+ < div
58+ className = { 'flex items-center justify-center w-56 h-56 p-2 bg-gray-800 rounded-lg shadow mx-auto mt-6' }
7159 >
72- < FlashMessageRender byKey = { 'account:two-step' } className = { 'mt-4' } />
73- < div
74- className = {
75- 'flex items-center justify-center w-56 h-56 p-2 bg-gray-800 rounded-lg shadow mx-auto mt-6'
76- }
60+ { ! token ? (
61+ < Spinner />
62+ ) : (
63+ < QRCode renderAs = { 'svg' } value = { token . image_url_data } css = { tw `w-full h-full shadow-none rounded` } />
64+ ) }
65+ </ div >
66+ < CopyOnClick text = { token ?. secret } >
67+ < p className = { 'font-mono text-sm text-gray-100 text-center mt-2' } >
68+ { token ?. secret . match ( / .{ 1 , 4 } / g) ! . join ( ' ' ) || 'Loading...' }
69+ </ p >
70+ </ CopyOnClick >
71+ < div className = { 'mt-6' } >
72+ < p >
73+ Scan the QR code above using the two-step authentication app of your choice. Then, enter the 6-digit
74+ code generated into the field below.
75+ </ p >
76+ </ div >
77+ < Input . Text
78+ variant = { Input . Text . Variants . Loose }
79+ value = { value }
80+ onChange = { ( e ) => setValue ( e . currentTarget . value ) }
81+ className = { 'mt-4' }
82+ placeholder = { '000000' }
83+ type = { 'text' }
84+ inputMode = { 'numeric' }
85+ autoComplete = { 'one-time-code' }
86+ pattern = { '\\d{6}' }
87+ />
88+ < Dialog . Footer >
89+ < Button . Text onClick = { close } > Cancel</ Button . Text >
90+ < Tooltip
91+ disabled = { value . length === 6 }
92+ content = { ! token ? 'Waiting for QR code to load...' : 'You must enter the 6-digit code to continue.' }
93+ delay = { 100 }
7794 >
78- { ! token ? (
79- < Spinner />
80- ) : (
81- < QRCode
82- renderAs = { 'svg' }
83- value = { token . image_url_data }
84- css = { tw `w-full h-full shadow-none rounded` }
85- />
86- ) }
87- </ div >
88- < CopyOnClick text = { token ?. secret } >
89- < p className = { 'font-mono text-sm text-gray-100 text-center mt-2' } >
90- { token ?. secret . match ( / .{ 1 , 4 } / g) ! . join ( ' ' ) || 'Loading...' }
91- </ p >
92- </ CopyOnClick >
93- < div className = { 'mt-6' } >
94- < p >
95- Scan the QR code above using the two-step authentication app of your choice. Then, enter the
96- 6-digit code generated into the field below.
97- </ p >
98- </ div >
99- < Input . Text
100- variant = { Input . Text . Variants . Loose }
101- value = { value }
102- onChange = { ( e ) => setValue ( e . currentTarget . value ) }
103- className = { 'mt-4' }
104- placeholder = { '000000' }
105- type = { 'text' }
106- inputMode = { 'numeric' }
107- autoComplete = { 'one-time-code' }
108- pattern = { '\\d{6}' }
109- />
110- < Dialog . Footer >
111- < Button . Text onClick = { onClose } > Cancel</ Button . Text >
112- < Tooltip
113- disabled = { value . length === 6 }
114- content = {
115- ! token ? 'Waiting for QR code to load...' : 'You must enter the 6-digit code to continue.'
116- }
117- delay = { 100 }
118- >
119- < Button disabled = { ! token || value . length !== 6 } onClick = { submit } >
120- Enable
121- </ Button >
122- </ Tooltip >
123- </ Dialog . Footer >
124- </ Dialog >
95+ < Button disabled = { ! token || value . length !== 6 } onClick = { submit } >
96+ Enable
97+ </ Button >
98+ </ Tooltip >
99+ </ Dialog . Footer >
125100 </ >
126101 ) ;
127102} ;
103+
104+ export default asDialog ( {
105+ title : 'Enable Two-Step Verification' ,
106+ description :
107+ "Help protect your account from unauthorized access. You'll be prompted for a verification code each time you sign in." ,
108+ } ) ( ConfigureTwoFactorForm ) ;
0 commit comments