1- import React , { useEffect , useState } from 'react' ;
2- import Modal , { RequiredModalProps } from '@/components/elements/Modal' ;
1+ import React , { useContext , useEffect , useState } from 'react' ;
32import { Form , Formik , FormikHelpers } from 'formik' ;
43import { object , string } from 'yup' ;
54import getTwoFactorTokenUrl from '@/api/account/getTwoFactorTokenUrl' ;
@@ -10,16 +9,19 @@ import FlashMessageRender from '@/components/FlashMessageRender';
109import Field from '@/components/elements/Field' ;
1110import tw from 'twin.macro' ;
1211import Button from '@/components/elements/Button' ;
12+ import asModal from '@/hoc/asModal' ;
13+ import ModalContext from '@/context/ModalContext' ;
1314
1415interface Values {
1516 code : string ;
1617}
1718
18- export default ( { onDismissed , ... props } : RequiredModalProps ) => {
19+ const SetupTwoFactorModal = ( ) => {
1920 const [ token , setToken ] = useState ( '' ) ;
2021 const [ loading , setLoading ] = useState ( true ) ;
2122 const [ recoveryTokens , setRecoveryTokens ] = useState < string [ ] > ( [ ] ) ;
2223
24+ const { dismiss, setPropOverrides } = useContext ( ModalContext ) ;
2325 const updateUserData = useStoreActions ( ( actions : Actions < ApplicationStore > ) => actions . user . updateUserData ) ;
2426 const { clearAndAddHttpError } = useStoreActions ( ( actions : Actions < ApplicationStore > ) => actions . flashes ) ;
2527
@@ -33,6 +35,7 @@ export default ({ onDismissed, ...props }: RequiredModalProps) => {
3335 } , [ ] ) ;
3436
3537 const submit = ( { code } : Values , { setSubmitting } : FormikHelpers < Values > ) => {
38+ setPropOverrides ( state => ( { ...state , showSpinnerOverlay : true } ) ) ;
3639 enableAccountTwoFactor ( code )
3740 . then ( tokens => {
3841 setRecoveryTokens ( tokens ) ;
@@ -42,16 +45,25 @@ export default ({ onDismissed, ...props }: RequiredModalProps) => {
4245
4346 clearAndAddHttpError ( { error, key : 'account:two-factor' } ) ;
4447 } )
45- . then ( ( ) => setSubmitting ( false ) ) ;
48+ . then ( ( ) => {
49+ setSubmitting ( false ) ;
50+ setPropOverrides ( state => ( { ...state , showSpinnerOverlay : false } ) ) ;
51+ } ) ;
4652 } ;
4753
48- const dismiss = ( ) => {
49- if ( recoveryTokens . length > 0 ) {
50- updateUserData ( { useTotp : true } ) ;
51- }
54+ useEffect ( ( ) => {
55+ setPropOverrides ( state => ( {
56+ ...state ,
57+ closeOnEscape : ! recoveryTokens . length ,
58+ closeOnBackground : ! recoveryTokens . length ,
59+ } ) ) ;
5260
53- onDismissed ( ) ;
54- } ;
61+ return ( ) => {
62+ if ( recoveryTokens . length > 0 ) {
63+ updateUserData ( { useTotp : true } ) ;
64+ }
65+ } ;
66+ } , [ recoveryTokens ] ) ;
5567
5668 return (
5769 < Formik
@@ -63,79 +75,69 @@ export default ({ onDismissed, ...props }: RequiredModalProps) => {
6375 . matches ( / ^ ( \d ) { 6 } $ / , 'Authenticator code must be 6 digits.' ) ,
6476 } ) }
6577 >
66- { ( { isSubmitting } ) => (
67- < Modal
68- { ...props }
69- top = { false }
70- onDismissed = { dismiss }
71- dismissable = { ! isSubmitting }
72- showSpinnerOverlay = { loading || isSubmitting }
73- closeOnEscape = { ! recoveryTokens }
74- closeOnBackground = { ! recoveryTokens }
75- >
76- { recoveryTokens . length > 0 ?
77- < >
78- < h2 css = { tw `text-2xl mb-4` } > Two-factor authentication enabled</ h2 >
79- < p css = { tw `text-neutral-300` } >
80- Two-factor authentication has been enabled on your account. Should you loose access to
81- this device you'll need to use one of the codes displayed below in order to access your
82- account.
83- </ p >
84- < p css = { tw `text-neutral-300 mt-4` } >
85- < strong > These codes will not be displayed again.</ strong > Please take note of them now
86- by storing them in a secure repository such as a password manager.
87- </ p >
88- < pre css = { tw `text-sm mt-4 rounded font-mono bg-neutral-900 p-4` } >
89- { recoveryTokens . map ( token => < code key = { token } css = { tw `block mb-1` } > { token } </ code > ) }
90- </ pre >
91- < div css = { tw `text-right` } >
92- < Button css = { tw `mt-6` } onClick = { dismiss } >
93- Close
94- </ Button >
78+ { recoveryTokens . length > 0 ?
79+ < >
80+ < h2 css = { tw `text-2xl mb-4` } > Two-factor authentication enabled</ h2 >
81+ < p css = { tw `text-neutral-300` } >
82+ Two-factor authentication has been enabled on your account. Should you loose access to
83+ this device you'll need to use one of the codes displayed below in order to access your
84+ account.
85+ </ p >
86+ < p css = { tw `text-neutral-300 mt-4` } >
87+ < strong > These codes will not be displayed again.</ strong > Please take note of them now
88+ by storing them in a secure repository such as a password manager.
89+ </ p >
90+ < pre css = { tw `text-sm mt-4 rounded font-mono bg-neutral-900 p-4` } >
91+ { recoveryTokens . map ( token => < code key = { token } css = { tw `block mb-1` } > { token } </ code > ) }
92+ </ pre >
93+ < div css = { tw `text-right` } >
94+ < Button css = { tw `mt-6` } onClick = { dismiss } >
95+ Close
96+ </ Button >
97+ </ div >
98+ </ >
99+ :
100+ < Form css = { tw `mb-0` } >
101+ < FlashMessageRender css = { tw `mb-6` } byKey = { 'account:two-factor' } />
102+ < div css = { tw `flex flex-wrap` } >
103+ < div css = { tw `w-full md:flex-1` } >
104+ < div css = { tw `w-32 h-32 md:w-64 md:h-64 bg-neutral-600 p-2 rounded mx-auto` } >
105+ { ! token || ! token . length ?
106+ < img
107+ src = { 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=' }
108+ css = { tw `w-64 h-64 rounded` }
109+ />
110+ :
111+ < img
112+ src = { `https://api.qrserver.com/v1/create-qr-code/?size=500x500&data=${ token } ` }
113+ onLoad = { ( ) => setLoading ( false ) }
114+ css = { tw `w-full h-full shadow-none rounded-none` }
115+ />
116+ }
95117 </ div >
96- </ >
97- :
98- < Form css = { tw `mb-0` } >
99- < FlashMessageRender css = { tw `mb-6` } byKey = { 'account:two-factor' } />
100- < div css = { tw `flex flex-wrap` } >
101- < div css = { tw `w-full md:flex-1` } >
102- < div css = { tw `w-32 h-32 md:w-64 md:h-64 bg-neutral-600 p-2 rounded mx-auto` } >
103- { ! token || ! token . length ?
104- < img
105- src = { 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=' }
106- css = { tw `w-64 h-64 rounded` }
107- />
108- :
109- < img
110- src = { `https://api.qrserver.com/v1/create-qr-code/?size=500x500&data=${ token } ` }
111- onLoad = { ( ) => setLoading ( false ) }
112- css = { tw `w-full h-full shadow-none rounded-none` }
113- />
114- }
115- </ div >
116- </ div >
117- < div css = { tw `w-full mt-6 md:mt-0 md:flex-1 md:flex md:flex-col` } >
118- < div css = { tw `flex-1` } >
119- < Field
120- id = { 'code' }
121- name = { 'code' }
122- type = { 'text' }
123- title = { 'Code From Authenticator' }
124- description = { 'Enter the code from your authenticator device after scanning the QR image.' }
125- autoFocus = { ! loading }
126- />
127- </ div >
128- < div css = { tw `mt-6 md:mt-0 text-right` } >
129- < Button >
130- Setup
131- </ Button >
132- </ div >
133- </ div >
118+ </ div >
119+ < div css = { tw `w-full mt-6 md:mt-0 md:flex-1 md:flex md:flex-col` } >
120+ < div css = { tw `flex-1` } >
121+ < Field
122+ id = { 'code' }
123+ name = { 'code' }
124+ type = { 'text' }
125+ title = { 'Code From Authenticator' }
126+ description = { 'Enter the code from your authenticator device after scanning the QR image.' }
127+ autoFocus = { ! loading }
128+ />
134129 </ div >
135- </ Form >
136- }
137- </ Modal >
138- ) }
130+ < div css = { tw `mt-6 md:mt-0 text-right` } >
131+ < Button >
132+ Setup
133+ </ Button >
134+ </ div >
135+ </ div >
136+ </ div >
137+ </ Form >
138+ }
139139 </ Formik >
140140 ) ;
141141} ;
142+
143+ export default asModal ( ) ( SetupTwoFactorModal ) ;
0 commit comments