1- import React from 'react' ;
1+ import React , { useRef } from 'react' ;
22import { Link , RouteComponentProps } from 'react-router-dom' ;
3- import login from '@/api/auth/login' ;
3+ import login , { LoginData } from '@/api/auth/login' ;
44import LoginFormContainer from '@/components/auth/LoginFormContainer' ;
55import FlashMessageRender from '@/components/FlashMessageRender' ;
6- import { Actions , useStoreActions } from 'easy-peasy' ;
6+ import { ActionCreator , Actions , useStoreActions , useStoreState } from 'easy-peasy' ;
77import { ApplicationStore } from '@/state' ;
88import { FormikProps , withFormik } from 'formik' ;
99import { object , string } from 'yup' ;
1010import Field from '@/components/elements/Field' ;
1111import { httpErrorToHuman } from '@/api/http' ;
12-
13- interface Values {
14- username : string ;
15- password : string ;
16- }
12+ import { FlashMessage } from '@/state/flashes' ;
13+ import ReCAPTCHA from 'react-google-recaptcha' ;
1714
1815type OwnProps = RouteComponentProps & {
19- clearFlashes : any ;
20- addFlash : any ;
16+ clearFlashes : ActionCreator < void > ;
17+ addFlash : ActionCreator < FlashMessage > ;
2118}
2219
23- const LoginContainer = ( { isSubmitting } : OwnProps & FormikProps < Values > ) => (
24- < React . Fragment >
25- < h2 className = { 'text-center text-neutral-100 font-medium py-4' } >
26- Login to Continue
27- </ h2 >
28- < FlashMessageRender className = { 'mb-2' } />
29- < LoginFormContainer >
30- < label htmlFor = { 'username' } > Username or Email</ label >
31- < Field
32- type = { 'text' }
33- id = { 'username' }
34- name = { 'username' }
35- className = { 'input' }
36- />
37- < div className = { 'mt-6' } >
38- < label htmlFor = { 'password' } > Password</ label >
20+ const LoginContainer = ( { isSubmitting, setFieldValue, values, submitForm, handleSubmit } : OwnProps & FormikProps < LoginData > ) => {
21+ const ref = useRef < ReCAPTCHA | null > ( null ) ;
22+ const { enabled : recaptchaEnabled , siteKey } = useStoreState < ApplicationStore , any > ( state => state . settings . data ! . recaptcha ) ;
23+
24+ const submit = ( e : React . FormEvent < HTMLFormElement > ) => {
25+ e . preventDefault ( ) ;
26+
27+ if ( ref . current && ! values . recaptchaData ) {
28+ return ref . current . execute ( ) ;
29+ }
30+
31+ handleSubmit ( e ) ;
32+ } ;
33+
34+ console . log ( values . recaptchaData ) ;
35+
36+ return (
37+ < React . Fragment >
38+ { ref . current && ref . current . render ( ) }
39+ < h2 className = { 'text-center text-neutral-100 font-medium py-4' } >
40+ Login to Continue
41+ </ h2 >
42+ < FlashMessageRender className = { 'mb-2' } />
43+ < LoginFormContainer onSubmit = { submit } >
44+ < label htmlFor = { 'username' } > Username or Email</ label >
3945 < Field
40- type = { 'password ' }
41- id = { 'password ' }
42- name = { 'password ' }
46+ type = { 'text ' }
47+ id = { 'username ' }
48+ name = { 'username ' }
4349 className = { 'input' }
4450 />
45- </ div >
46- < div className = { 'mt-6' } >
47- < button
48- type = { 'submit' }
49- className = { 'btn btn-primary btn-jumbo' }
50- >
51- { isSubmitting ?
52- < span className = { 'spinner white' } > </ span >
53- :
54- 'Login'
55- }
56- </ button >
57- </ div >
58- < div className = { 'mt-6 text-center' } >
59- < Link
60- to = { '/auth/password' }
61- className = { 'text-xs text-neutral-500 tracking-wide no-underline uppercase hover:text-neutral-600' }
62- >
63- Forgot password?
64- </ Link >
65- </ div >
66- </ LoginFormContainer >
67- </ React . Fragment >
68- ) ;
51+ < div className = { 'mt-6' } >
52+ < label htmlFor = { 'password' } > Password</ label >
53+ < Field
54+ type = { 'password' }
55+ id = { 'password' }
56+ name = { 'password' }
57+ className = { 'input' }
58+ />
59+ </ div >
60+ < div className = { 'mt-6' } >
61+ < button
62+ type = { 'submit' }
63+ className = { 'btn btn-primary btn-jumbo' }
64+ >
65+ { isSubmitting ?
66+ < span className = { 'spinner white' } > </ span >
67+ :
68+ 'Login'
69+ }
70+ </ button >
71+ </ div >
72+ { recaptchaEnabled &&
73+ < ReCAPTCHA
74+ ref = { ref }
75+ size = { 'invisible' }
76+ sitekey = { siteKey || '_invalid_key' }
77+ onChange = { token => {
78+ ref . current && ref . current . reset ( ) ;
79+ setFieldValue ( 'recaptchaData' , token ) ;
80+ submitForm ( ) ;
81+ } }
82+ onExpired = { ( ) => setFieldValue ( 'recaptchaData' , null ) }
83+ />
84+ }
85+ < div className = { 'mt-6 text-center' } >
86+ < Link
87+ to = { '/auth/password' }
88+ className = { 'text-xs text-neutral-500 tracking-wide no-underline uppercase hover:text-neutral-600' }
89+ >
90+ Forgot password?
91+ </ Link >
92+ </ div >
93+ </ LoginFormContainer >
94+ </ React . Fragment >
95+ ) ;
96+ } ;
6997
70- const EnhancedForm = withFormik < OwnProps , Values > ( {
98+ const EnhancedForm = withFormik < OwnProps , LoginData > ( {
7199 displayName : 'LoginContainerForm' ,
72100
73101 mapPropsToValues : ( props ) => ( {
74102 username : '' ,
75103 password : '' ,
104+ recaptchaData : null ,
76105 } ) ,
77106
78107 validationSchema : ( ) => object ( ) . shape ( {
79108 username : string ( ) . required ( 'A username or email must be provided.' ) ,
80109 password : string ( ) . required ( 'Please enter your account password.' ) ,
81110 } ) ,
82111
83- handleSubmit : ( { username , password } , { props, setSubmitting } ) => {
112+ handleSubmit : ( values , { props, setFieldValue , setSubmitting } ) => {
84113 props . clearFlashes ( ) ;
85- login ( username , password )
114+ login ( values )
86115 . then ( response => {
87116 if ( response . complete ) {
88117 // @ts -ignore
@@ -96,6 +125,7 @@ const EnhancedForm = withFormik<OwnProps, Values>({
96125 console . error ( error ) ;
97126
98127 setSubmitting ( false ) ;
128+ setFieldValue ( 'recaptchaData' , null ) ;
99129 props . addFlash ( { type : 'error' , title : 'Error' , message : httpErrorToHuman ( error ) } ) ;
100130 } ) ;
101131 } ,
0 commit comments