55 IonPopover ,
66 useIonRouter ,
77 useIonViewDidEnter ,
8+ IonText ,
9+ IonRow ,
10+ IonCol ,
811} from '@ionic/react' ;
912import { useRef , useState } from 'react' ;
1013import classNames from 'classnames' ;
@@ -17,13 +20,15 @@ import { BaseComponentProps } from 'common/components/types';
1720import { RememberMe } from 'common/models/auth' ;
1821import storage from 'common/utils/storage' ;
1922import { StorageKey } from 'common/utils/constants' ;
20- import { useSignIn } from '../api/useSignIn ' ;
23+ import { useSignIn } from 'common/hooks/useAuth ' ;
2124import { useProgress } from 'common/hooks/useProgress' ;
2225import Input from 'common/components/Input/Input' ;
2326import ErrorCard from 'common/components/Card/ErrorCard' ;
2427import Icon from 'common/components/Icon/Icon' ;
2528import HeaderRow from 'common/components/Text/HeaderRow' ;
2629import CheckboxInput from 'common/components/Input/CheckboxInput' ;
30+ import { useSocialSignIn } from 'common/hooks/useAuth' ;
31+ import { getAuthErrorMessage } from 'common/utils/auth-errors' ;
2732
2833/**
2934 * Properties for the `SignInForm` component.
@@ -32,11 +37,11 @@ interface SignInFormProps extends BaseComponentProps {}
3237
3338/**
3439 * Sign in form values.
35- * @param {string } username - A username .
40+ * @param {string } email - User's email .
3641 * @param {string } password - A password.
3742 */
3843interface SignInFormValues {
39- username : string ;
44+ email : string ;
4045 password : string ;
4146 rememberMe : boolean ;
4247}
@@ -51,14 +56,17 @@ const SignInForm = ({ className, testid = 'form-signin' }: SignInFormProps): JSX
5156 const [ error , setError ] = useState < string > ( '' ) ;
5257 const { setIsActive : setShowProgress } = useProgress ( ) ;
5358 const router = useIonRouter ( ) ;
54- const { mutate : signIn } = useSignIn ( ) ;
59+ const { signIn, isLoading } = useSignIn ( ) ;
60+ const { signInWithGoogle, signInWithApple } = useSocialSignIn ( ) ;
5561 const { t } = useTranslation ( ) ;
5662
5763 /**
5864 * Sign in form validation schema.
5965 */
6066 const validationSchema = object < SignInFormValues > ( {
61- username : string ( ) . required ( t ( 'validation.required' ) ) ,
67+ email : string ( )
68+ . email ( t ( 'validation.email' ) )
69+ . required ( t ( 'validation.required' ) ) ,
6270 password : string ( ) . required ( t ( 'validation.required' ) ) ,
6371 rememberMe : boolean ( ) . default ( false ) ,
6472 } ) ;
@@ -70,6 +78,34 @@ const SignInForm = ({ className, testid = 'form-signin' }: SignInFormProps): JSX
7078 focusInput . current ?. setFocus ( ) ;
7179 } ) ;
7280
81+ // Handle sign in with Google
82+ const handleGoogleSignIn = async ( ) => {
83+ try {
84+ setError ( '' ) ;
85+ setShowProgress ( true ) ;
86+ await signInWithGoogle ( ) ;
87+ router . push ( '/tabs' , 'forward' , 'replace' ) ;
88+ } catch ( err ) {
89+ setError ( getAuthErrorMessage ( err ) ) ;
90+ } finally {
91+ setShowProgress ( false ) ;
92+ }
93+ } ;
94+
95+ // Handle sign in with Apple
96+ const handleAppleSignIn = async ( ) => {
97+ try {
98+ setError ( '' ) ;
99+ setShowProgress ( true ) ;
100+ await signInWithApple ( ) ;
101+ router . push ( '/tabs' , 'forward' , 'replace' ) ;
102+ } catch ( err ) {
103+ setError ( getAuthErrorMessage ( err ) ) ;
104+ } finally {
105+ setShowProgress ( false ) ;
106+ }
107+ } ;
108+
73109 return (
74110 < div className = { classNames ( 'ls-signin-form' , className ) } data-testid = { testid } >
75111 { error && (
@@ -83,32 +119,31 @@ const SignInForm = ({ className, testid = 'form-signin' }: SignInFormProps): JSX
83119 < Formik < SignInFormValues >
84120 enableReinitialize = { true }
85121 initialValues = { {
86- username : rememberMe ?. username ?? '' ,
122+ email : rememberMe ?. username ?? '' ,
87123 password : '' ,
88124 rememberMe : ! ! rememberMe ,
89125 } }
90- onSubmit = { ( values , { setSubmitting } ) => {
91- setError ( '' ) ;
92- setShowProgress ( true ) ;
93- signIn ( values . username , {
94- onSuccess : ( ) => {
95- if ( values . rememberMe ) {
96- storage . setJsonItem < RememberMe > ( StorageKey . RememberMe , {
97- username : values . username ,
98- } ) ;
99- } else {
100- storage . removeItem ( StorageKey . RememberMe ) ;
101- }
102- router . push ( '/tabs' , 'forward' , 'replace' ) ;
103- } ,
104- onError : ( err : Error ) => {
105- setError ( err . message ) ;
106- } ,
107- onSettled : ( ) => {
108- setShowProgress ( false ) ;
109- setSubmitting ( false ) ;
110- } ,
111- } ) ;
126+ onSubmit = { async ( values , { setSubmitting } ) => {
127+ try {
128+ setError ( '' ) ;
129+ setShowProgress ( true ) ;
130+ await signIn ( values . email , values . password ) ;
131+
132+ if ( values . rememberMe ) {
133+ storage . setJsonItem < RememberMe > ( StorageKey . RememberMe , {
134+ username : values . email ,
135+ } ) ;
136+ } else {
137+ storage . removeItem ( StorageKey . RememberMe ) ;
138+ }
139+
140+ router . push ( '/tabs' , 'forward' , 'replace' ) ;
141+ } catch ( err ) {
142+ setError ( getAuthErrorMessage ( err ) ) ;
143+ } finally {
144+ setShowProgress ( false ) ;
145+ setSubmitting ( false ) ;
146+ }
112147 } }
113148 validationSchema = { validationSchema }
114149 >
@@ -120,22 +155,23 @@ const SignInForm = ({ className, testid = 'form-signin' }: SignInFormProps): JSX
120155 </ HeaderRow >
121156
122157 < Input
123- name = "username "
124- label = { t ( 'label.username ' , { ns : 'auth' } ) }
158+ name = "email "
159+ label = { t ( 'label.email ' , { ns : 'auth' } ) }
125160 labelPlacement = "stacked"
126- maxlength = { 30 }
127- autocomplete = "off "
161+ maxlength = { 50 }
162+ autocomplete = "email "
128163 className = "ls-signin-form__input"
129164 ref = { focusInput }
130- data-testid = { `${ testid } -field-username` }
165+ data-testid = { `${ testid } -field-email` }
166+ type = "email"
131167 />
132168 < Input
133169 type = "password"
134170 name = "password"
135171 label = { t ( 'label.password' , { ns : 'auth' } ) }
136172 labelPlacement = "stacked"
137173 maxlength = { 30 }
138- autocomplete = "off "
174+ autocomplete = "current-password "
139175 className = "ls-signin-form__input"
140176 data-testid = { `${ testid } -field-password` }
141177 >
@@ -155,33 +191,70 @@ const SignInForm = ({ className, testid = 'form-signin' }: SignInFormProps): JSX
155191 color = "primary"
156192 className = "ls-signin-form__button"
157193 expand = "block"
158- disabled = { isSubmitting || ! dirty }
194+ disabled = { isSubmitting || ! dirty || isLoading }
159195 data-testid = { `${ testid } -button-submit` }
160196 >
161197 { t ( 'signin' , { ns : 'auth' } ) }
162198 </ IonButton >
163199
200+ < IonRow className = "ion-text-center ion-padding" >
201+ < IonCol >
202+ < IonText color = "medium" >
203+ { t ( 'or-signin-with' , { ns : 'auth' } ) }
204+ </ IonText >
205+ </ IonCol >
206+ </ IonRow >
207+
208+ < IonRow >
209+ < IonCol >
210+ < IonButton
211+ expand = "block"
212+ fill = "outline"
213+ color = "medium"
214+ onClick = { handleGoogleSignIn }
215+ disabled = { isLoading }
216+ data-testid = { `${ testid } -button-google` }
217+ >
218+ < Icon icon = "google" slot = "start" />
219+ Google
220+ </ IonButton >
221+ </ IonCol >
222+ < IonCol >
223+ < IonButton
224+ expand = "block"
225+ fill = "outline"
226+ color = "dark"
227+ onClick = { handleAppleSignIn }
228+ disabled = { isLoading }
229+ data-testid = { `${ testid } -button-apple` }
230+ >
231+ < Icon icon = "apple" slot = "start" />
232+ Apple
233+ </ IonButton >
234+ </ IonCol >
235+ </ IonRow >
236+
237+ < IonRow className = "ion-text-center ion-padding-top" >
238+ < IonCol >
239+ < IonText color = "medium" >
240+ { t ( 'no-account' , { ns : 'auth' } ) } { ' ' }
241+ < a href = "/auth/signup" > { t ( 'signup' , { ns : 'auth' } ) } </ a >
242+ </ IonText >
243+ </ IonCol >
244+ </ IonRow >
245+
164246 < IonPopover
165247 trigger = "signinInfo"
166248 triggerAction = "hover"
167249 className = "ls-signin-form-popover"
168250 >
169251 < IonContent className = "ion-padding" >
170252 < p >
171- { t ( 'info-username.part1' , { ns : 'auth' } ) }
172- < a
173- href = "https://jsonplaceholder.typicode.com/users"
174- target = "_blank"
175- rel = "noreferrer"
176- >
177- { t ( 'info-username.part2' , { ns : 'auth' } ) }
178- </ a >
179- . { t ( 'info-username.part3' , { ns : 'auth' } ) } { ' ' }
180- < span className = "inline-code" > Bret</ span > { ' ' }
181- { t ( 'info-username.part4' , { ns : 'auth' } ) } { ' ' }
182- < span className = "inline-code" > Samantha</ span > .
253+ { t ( 'info-email.part1' , { ns : 'auth' } ) }
254+ </ p >
255+ < p >
256+ { t ( 'info-email.part2' , { ns : 'auth' } ) }
183257 </ p >
184- < p > { t ( 'info-username.part5' , { ns : 'auth' } ) } </ p >
185258 </ IonContent >
186259 </ IonPopover >
187260 </ Form >
0 commit comments