342 lines
9.5 KiB
JavaScript
342 lines
9.5 KiB
JavaScript
/* eslint-disable react/display-name */
|
|
import * as AppleAuthentication from 'expo-apple-authentication'
|
|
import React, { useCallback, useState } from 'react'
|
|
import { ActivityIndicator, Pressable, StyleSheet, Text, View } from 'react-native'
|
|
import Svg, { Path } from 'react-native-svg'
|
|
import { useGlobal } from 'reactn'
|
|
import { handleFirebaseError } from '../actions/signupActions.js'
|
|
import { background } from '../assets'
|
|
import GradientButton from '../components/GradientButton.js'
|
|
import { Input } from '../components/Input.js'
|
|
import ItemContainer from '../components/ItemContainer/ItemContainer'
|
|
import firebase from '../config/firebase'
|
|
import useSocialAuth from '../hooks/useSocialAuth'
|
|
import { isWeb } from '../hooks/useLayoutType'
|
|
import Page from '../layouts/Page.js'
|
|
import { Routes } from '../navigation'
|
|
import { navigate } from '../navigation/NavigationService.js'
|
|
import { FONT_FAMILY } from '../styles/Fonts.js'
|
|
import Palette from '../styles/Palette.js'
|
|
|
|
export default ({ navigation }) => {
|
|
const [, setTooltip] = useGlobal('_tooltip')
|
|
const [email, setEmail] = useState(__DEV__ ? 'az@az.az' : '')
|
|
const [password, setPassword] = useState(__DEV__ ? 'Minuit33' : '')
|
|
const [loading, setLoading] = useState(false)
|
|
const afterLoginNavigate = useCallback(async () => {
|
|
const uid = firebase.auth().currentUser?.uid
|
|
if (!uid) throw new Error('Aucun utilisateur après connexion')
|
|
navigation.reset({ index: 0, routes: [{ name: Routes.BottomTab }] })
|
|
}, [navigation])
|
|
|
|
const {
|
|
signInWithGoogle,
|
|
signInWithApple,
|
|
isAppleSignInAvailable,
|
|
isLoading: socialLoading,
|
|
} = useSocialAuth({
|
|
onSuccess: afterLoginNavigate,
|
|
})
|
|
|
|
const isBusy = loading || socialLoading
|
|
|
|
const onLogin = async () => {
|
|
try {
|
|
setLoading(true)
|
|
await firebase.auth().signInWithEmailAndPassword(email.trim(), password)
|
|
await afterLoginNavigate()
|
|
} catch (e) {
|
|
console.log('Login error', e?.message)
|
|
const message =
|
|
e?.code && e.code.startsWith('auth/')
|
|
? handleFirebaseError(e.code)
|
|
: e?.message || 'Connexion impossible'
|
|
setTooltip({ text: message, type: 'error' })
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Page
|
|
width={isWeb ? 600 : null}
|
|
backgroundImg={isWeb ? background.loginBgWeb : background.homeBG}
|
|
headerType="NAVIGATION"
|
|
title={isWeb ? '' : 'Connexion'}
|
|
hideBackButton
|
|
>
|
|
<View style={styles.screen}>
|
|
<ItemContainer height={isWeb ? 520 : 550} disableKeyboardHeight style={styles.loginCard}>
|
|
<View style={styles.cardContent}>
|
|
<View style={styles.greetingSection}>
|
|
<Text style={styles.greetingTitle}>{'Bonjour !'}</Text>
|
|
<Text style={styles.greetingSubtitle}>
|
|
Nous sommes heureux de te revoir parmi nous. Prêt·e à reprendre le rythme ?
|
|
</Text>
|
|
</View>
|
|
<View style={styles.formFields}>
|
|
<Input
|
|
placeholder="Adresse mail"
|
|
label="Adresse mail"
|
|
type="email"
|
|
value={email}
|
|
setValue={setEmail}
|
|
isBlur
|
|
/>
|
|
<View style={styles.passwordField}>
|
|
<Input
|
|
placeholder="Mot de passe"
|
|
label="Mot de passe"
|
|
type="password"
|
|
value={password}
|
|
setValue={setPassword}
|
|
isBlur
|
|
/>
|
|
<Pressable
|
|
onPress={() => navigate(Routes.ForgotPassword)}
|
|
style={({ pressed }) => [styles.linkPressable, pressed && styles.linkPressed]}
|
|
>
|
|
<Text style={styles.linkText}>Mot de passe oublié ?</Text>
|
|
</Pressable>
|
|
</View>
|
|
</View>
|
|
<GradientButton
|
|
title="Se connecter"
|
|
containerStyle={{
|
|
width: '90%',
|
|
alignSelf: 'center',
|
|
}}
|
|
onPress={onLogin}
|
|
disabled={isBusy || !email || !password}
|
|
/>
|
|
<View style={styles.socialWrapper}>
|
|
{isAppleSignInAvailable ? (
|
|
<AppleAuthButton
|
|
onPress={signInWithApple}
|
|
disabled={isBusy}
|
|
loading={socialLoading}
|
|
/>
|
|
) : null}
|
|
<GoogleAuthButton
|
|
onPress={signInWithGoogle}
|
|
disabled={isBusy}
|
|
loading={socialLoading}
|
|
/>
|
|
</View>
|
|
</View>
|
|
</ItemContainer>
|
|
<View style={styles.bottomFooter}>
|
|
<Text style={styles.bottomFooterText}>
|
|
Tu n'as pas de compte ?{' '}
|
|
<Text style={styles.bottomFooterLink} onPress={() => navigate(Routes.Register)}>
|
|
Créer un compte
|
|
</Text>
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
</Page>
|
|
)
|
|
}
|
|
|
|
const AppleAuthButton = ({ onPress, disabled, loading }) => {
|
|
if (isWeb) {
|
|
return null
|
|
}
|
|
|
|
return (
|
|
<View style={styles.nativeButtonWrapper}>
|
|
<AppleAuthentication.AppleAuthenticationButton
|
|
buttonType={AppleAuthentication.AppleAuthenticationButtonType.CONTINUE}
|
|
buttonStyle={AppleAuthentication.AppleAuthenticationButtonStyle.WHITE}
|
|
style={styles.nativeButton}
|
|
cornerRadius={12}
|
|
onPress={onPress}
|
|
disabled={disabled}
|
|
/>
|
|
{loading ? (
|
|
<View style={styles.nativeLoadingOverlay}>
|
|
<ActivityIndicator size="small" color={Palette.black} />
|
|
</View>
|
|
) : null}
|
|
</View>
|
|
)
|
|
}
|
|
|
|
const GoogleAuthButton = ({ onPress, disabled, loading }) => (
|
|
<Pressable
|
|
accessibilityRole="button"
|
|
onPress={onPress}
|
|
disabled={disabled}
|
|
style={({ pressed }) => [
|
|
styles.socialButton,
|
|
pressed && !disabled ? styles.socialButtonPressed : null,
|
|
disabled ? styles.socialButtonDisabled : null,
|
|
]}
|
|
>
|
|
<View style={styles.socialButtonContent}>
|
|
<View style={styles.socialIconContainer}>
|
|
<GoogleLogo />
|
|
</View>
|
|
<Text style={styles.socialButtonText}>Continuer avec Google</Text>
|
|
{loading ? (
|
|
<ActivityIndicator size="small" color="#4285F4" />
|
|
) : (
|
|
<View style={styles.socialRightSpacer} />
|
|
)}
|
|
</View>
|
|
</Pressable>
|
|
)
|
|
|
|
const styles = StyleSheet.create({
|
|
screen: {
|
|
flex: 1,
|
|
width: '100%',
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
},
|
|
loginCard: {
|
|
width: '100%',
|
|
maxWidth: 520,
|
|
},
|
|
cardContent: {
|
|
gap: 28,
|
|
paddingTop: 8,
|
|
paddingHorizontal: 5,
|
|
width: '100%',
|
|
},
|
|
greetingSection: {
|
|
gap: 6,
|
|
width: '100%',
|
|
},
|
|
greetingTitle: {
|
|
fontSize: 22,
|
|
color: Palette.white,
|
|
fontFamily: FONT_FAMILY.InterSemiBold,
|
|
},
|
|
greetingSubtitle: {
|
|
fontSize: 14,
|
|
color: Palette.gray,
|
|
fontFamily: FONT_FAMILY.InterRegular,
|
|
lineHeight: 20,
|
|
},
|
|
formFields: {
|
|
gap: 20,
|
|
width: '100%',
|
|
},
|
|
passwordField: {
|
|
gap: 8,
|
|
width: '100%',
|
|
},
|
|
linkPressable: {
|
|
alignSelf: 'flex-end',
|
|
},
|
|
linkPressed: {
|
|
opacity: 0.6,
|
|
},
|
|
linkText: {
|
|
fontSize: 12,
|
|
color: Palette.white,
|
|
fontFamily: FONT_FAMILY.InterRegular,
|
|
textAlign: 'right',
|
|
},
|
|
bottomFooter: {
|
|
marginTop: 16,
|
|
paddingHorizontal: 24,
|
|
},
|
|
bottomFooterText: {
|
|
fontSize: 14,
|
|
color: Palette.white,
|
|
fontFamily: FONT_FAMILY.HelveticaNeueRegular,
|
|
textAlign: 'center',
|
|
},
|
|
bottomFooterLink: {
|
|
fontFamily: FONT_FAMILY.InterSemiBold,
|
|
color: Palette.white,
|
|
},
|
|
socialWrapper: {
|
|
width: '100%',
|
|
gap: 16,
|
|
alignItems: 'center',
|
|
},
|
|
socialButton: {
|
|
alignSelf: 'center',
|
|
borderRadius: 12,
|
|
width: '90%',
|
|
backgroundColor: Palette.white,
|
|
borderWidth: 1,
|
|
borderColor: '#E3E7EF',
|
|
paddingVertical: 12,
|
|
paddingHorizontal: 20,
|
|
shadowColor: 'rgba(12, 6, 34, 0.08)',
|
|
shadowOffset: { width: 0, height: 8 },
|
|
shadowOpacity: 0.3,
|
|
shadowRadius: 16,
|
|
elevation: 2,
|
|
},
|
|
socialButtonPressed: {
|
|
backgroundColor: '#F4F7FF',
|
|
},
|
|
socialButtonDisabled: {
|
|
opacity: 0.7,
|
|
},
|
|
socialButtonContent: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between',
|
|
gap: 12,
|
|
},
|
|
socialIconContainer: {
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
width: 28,
|
|
height: 28,
|
|
},
|
|
socialButtonText: {
|
|
flex: 1,
|
|
textAlign: 'center',
|
|
fontSize: 15,
|
|
color: '#202124',
|
|
fontFamily: FONT_FAMILY.InterSemiBold,
|
|
},
|
|
socialRightSpacer: {
|
|
width: 24,
|
|
height: 24,
|
|
},
|
|
nativeButtonWrapper: {
|
|
alignSelf: 'center',
|
|
width: '90%',
|
|
height: 50,
|
|
justifyContent: 'center',
|
|
},
|
|
nativeButton: {
|
|
width: '100%',
|
|
height: 50,
|
|
},
|
|
nativeLoadingOverlay: {
|
|
...StyleSheet.absoluteFillObject,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
},
|
|
})
|
|
|
|
const GoogleLogo = () => (
|
|
<Svg width={24} height={24} viewBox="0 0 18 18">
|
|
<Path
|
|
fill="#4285F4"
|
|
d="M17.64 9.2c0-.64-.06-1.25-.16-1.84H9v3.48h4.84a4.14 4.14 0 0 1-1.8 2.71v2.26h2.9c1.68-1.55 2.7-3.83 2.7-6.61Z"
|
|
/>
|
|
<Path
|
|
fill="#34A853"
|
|
d="M9 18c2.43 0 4.47-.8 5.96-2.18l-2.9-2.26c-.8.54-1.83.86-3.06.86a5.31 5.31 0 0 1-5.02-3.69H.96v2.32A9 9 0 0 0 9 18Z"
|
|
/>
|
|
<Path
|
|
fill="#FBBC05"
|
|
d="M3.98 10.73c-.18-.54-.28-1.12-.28-1.73s.1-1.19.28-1.73V4.95H.96A9 9 0 0 0 0 9c0 1.45.35 2.82.96 4.05l3.02-2.32Z"
|
|
/>
|
|
<Path
|
|
fill="#EA4335"
|
|
d="M9 3.54c1.32 0 2.5.45 3.43 1.33l2.57-2.57C13.47.9 11.43 0 9 0A9 9 0 0 0 .96 4.95l3.02 2.32A5.31 5.31 0 0 1 9 3.54Z"
|
|
/>
|
|
</Svg>
|
|
)
|