288 lines
8.3 KiB
JavaScript
288 lines
8.3 KiB
JavaScript
import { useRoute } from '@react-navigation/native'
|
|
import React, { useEffect, useMemo, useState, useGlobal } from 'reactn'
|
|
import { Text, View } from 'react-native'
|
|
import { checkIfPasswordIsStrongEnough, handleFirebaseError } from '../actions/signupActions'
|
|
import { background } from '../assets'
|
|
import GradientButton from '../components/GradientButton'
|
|
import { Input } from '../components/Input'
|
|
import ItemContainer from '../components/ItemContainer/ItemContainer'
|
|
import firebase from '../config/firebase'
|
|
import Page from '../layouts/Page'
|
|
import { isWeb } from '../hooks/useLayoutType'
|
|
import { Routes } from '../navigation'
|
|
import { navigate } from '../navigation/NavigationService'
|
|
import { Palette } from '../styles'
|
|
import { FONT_FAMILY } from '../styles/Fonts'
|
|
|
|
const PASSWORD_ERROR_MESSAGE =
|
|
'Ton mot de passe doit contenir au moins 6 caractères et combiner plusieurs types de caractères.'
|
|
const PASSWORD_MISMATCH_MESSAGE = 'Les mots de passe ne correspondent pas.'
|
|
const INVALID_LINK_MESSAGE =
|
|
"Ce lien de réinitialisation n'est plus valide. Demande un nouveau mot de passe."
|
|
|
|
const ResetPassword = () => {
|
|
const route = useRoute()
|
|
const oobCode = (route?.params?.oobCode || '').trim()
|
|
|
|
const [, setTooltip] = useGlobal('_tooltip')
|
|
const [, setIsLoading] = useGlobal('_isLoading')
|
|
|
|
const [email, setEmail] = useState(route?.params?.email || '')
|
|
const [password, setPassword] = useState('')
|
|
const [confirmPassword, setConfirmPassword] = useState('')
|
|
const [passwordError, setPasswordError] = useState('')
|
|
const [confirmError, setConfirmError] = useState('')
|
|
const [codeError, setCodeError] = useState('')
|
|
const [isVerifyingCode, setIsVerifyingCode] = useState(true)
|
|
const [isSubmitting, setIsSubmitting] = useState(false)
|
|
|
|
const trimmedPassword = useMemo(() => (password || '').trim(), [password])
|
|
const trimmedConfirmPassword = useMemo(() => (confirmPassword || '').trim(), [confirmPassword])
|
|
|
|
const isPasswordValid = useMemo(() => {
|
|
if (!trimmedPassword.length) return false
|
|
return checkIfPasswordIsStrongEnough({ password: trimmedPassword })
|
|
}, [trimmedPassword])
|
|
|
|
const doPasswordsMatch = useMemo(() => {
|
|
if (!trimmedPassword.length || !trimmedConfirmPassword.length) return false
|
|
return trimmedPassword === trimmedConfirmPassword
|
|
}, [trimmedPassword, trimmedConfirmPassword])
|
|
|
|
useEffect(() => {
|
|
const verifyCode = async () => {
|
|
if (!oobCode) {
|
|
setCodeError(INVALID_LINK_MESSAGE)
|
|
setTooltip({ text: INVALID_LINK_MESSAGE, type: 'error' })
|
|
setIsVerifyingCode(false)
|
|
return
|
|
}
|
|
|
|
try {
|
|
setIsLoading(true)
|
|
const resetEmail = await firebase.auth().verifyPasswordResetCode(oobCode)
|
|
setEmail(resetEmail)
|
|
setCodeError('')
|
|
} catch (error) {
|
|
console.log('ResetPassword verify error', error?.message)
|
|
const message =
|
|
error?.code && error.code.startsWith('auth/')
|
|
? handleFirebaseError(error.code)
|
|
: error?.message || INVALID_LINK_MESSAGE
|
|
setCodeError(message || INVALID_LINK_MESSAGE)
|
|
setTooltip({ text: message || INVALID_LINK_MESSAGE, type: 'error' })
|
|
} finally {
|
|
setIsLoading(false)
|
|
setIsVerifyingCode(false)
|
|
}
|
|
}
|
|
|
|
verifyCode()
|
|
}, [oobCode, setIsLoading, setTooltip])
|
|
|
|
useEffect(() => {
|
|
if (!password?.length) {
|
|
setPasswordError('')
|
|
return
|
|
}
|
|
|
|
if (!isPasswordValid) {
|
|
setPasswordError(PASSWORD_ERROR_MESSAGE)
|
|
} else {
|
|
setPasswordError('')
|
|
}
|
|
}, [password, isPasswordValid])
|
|
|
|
useEffect(() => {
|
|
if (!confirmPassword?.length) {
|
|
setConfirmError('')
|
|
return
|
|
}
|
|
|
|
if (!doPasswordsMatch) {
|
|
setConfirmError(PASSWORD_MISMATCH_MESSAGE)
|
|
} else {
|
|
setConfirmError('')
|
|
}
|
|
}, [confirmPassword, doPasswordsMatch])
|
|
|
|
const handleSubmit = async () => {
|
|
if (!isPasswordValid) {
|
|
setPasswordError(PASSWORD_ERROR_MESSAGE)
|
|
return
|
|
}
|
|
if (!doPasswordsMatch) {
|
|
setConfirmError(PASSWORD_MISMATCH_MESSAGE)
|
|
return
|
|
}
|
|
|
|
try {
|
|
setIsSubmitting(true)
|
|
setIsLoading(true)
|
|
await firebase.auth().confirmPasswordReset(oobCode, trimmedPassword)
|
|
setTooltip({
|
|
text: 'Ton mot de passe a été mis à jour. Tu peux te connecter.',
|
|
type: 'success',
|
|
})
|
|
navigate(Routes.Login)
|
|
} catch (error) {
|
|
console.log('ResetPassword submit error', error?.message)
|
|
const message =
|
|
error?.code && error.code.startsWith('auth/')
|
|
? handleFirebaseError(error.code)
|
|
: error?.message || 'Impossible de mettre à jour le mot de passe.'
|
|
setTooltip({ text: message, type: 'error' })
|
|
} finally {
|
|
setIsLoading(false)
|
|
setIsSubmitting(false)
|
|
}
|
|
}
|
|
|
|
const renderForm = () => (
|
|
<>
|
|
<View style={{ gap: 2 }}>
|
|
<Text
|
|
style={{
|
|
fontSize: 22,
|
|
color: Palette.white,
|
|
fontFamily: FONT_FAMILY.InterSemiBold,
|
|
}}
|
|
>
|
|
Définis un nouveau mot de passe
|
|
</Text>
|
|
<Text
|
|
style={{
|
|
fontSize: 14,
|
|
color: Palette.gray,
|
|
fontFamily: FONT_FAMILY.InterRegular,
|
|
}}
|
|
>
|
|
Utilise au moins 6 caractères pour sécuriser ton compte.
|
|
</Text>
|
|
{email ? (
|
|
<Text
|
|
style={{
|
|
marginTop: 8,
|
|
fontSize: 12,
|
|
color: Palette.gray,
|
|
fontFamily: FONT_FAMILY.InterRegular,
|
|
}}
|
|
>
|
|
Compte concerné : {email}
|
|
</Text>
|
|
) : null}
|
|
</View>
|
|
|
|
<View style={{ gap: 16 }}>
|
|
<Input
|
|
placeholder="Nouveau mot de passe"
|
|
label="Nouveau mot de passe"
|
|
type="password"
|
|
value={password}
|
|
setValue={setPassword}
|
|
isBlur
|
|
/>
|
|
{passwordError ? (
|
|
<Text
|
|
style={{
|
|
fontSize: 12,
|
|
color: Palette.red,
|
|
fontFamily: FONT_FAMILY.InterRegular,
|
|
}}
|
|
>
|
|
{passwordError}
|
|
</Text>
|
|
) : null}
|
|
<Input
|
|
placeholder="Confirmer le mot de passe"
|
|
label="Confirmer le mot de passe"
|
|
type="password"
|
|
value={confirmPassword}
|
|
setValue={setConfirmPassword}
|
|
isBlur
|
|
/>
|
|
{confirmError ? (
|
|
<Text
|
|
style={{
|
|
fontSize: 12,
|
|
color: Palette.red,
|
|
fontFamily: FONT_FAMILY.InterRegular,
|
|
}}
|
|
>
|
|
{confirmError}
|
|
</Text>
|
|
) : null}
|
|
</View>
|
|
</>
|
|
)
|
|
|
|
const renderCodeError = () => (
|
|
<View style={{ gap: 12 }}>
|
|
<Text
|
|
style={{
|
|
fontSize: 18,
|
|
color: Palette.white,
|
|
fontFamily: FONT_FAMILY.InterSemiBold,
|
|
}}
|
|
>
|
|
Lien invalide ou expiré
|
|
</Text>
|
|
<Text
|
|
style={{
|
|
fontSize: 14,
|
|
color: Palette.gray,
|
|
fontFamily: FONT_FAMILY.InterRegular,
|
|
}}
|
|
>
|
|
{codeError || INVALID_LINK_MESSAGE}
|
|
</Text>
|
|
</View>
|
|
)
|
|
|
|
const handleFallbackNavigation = () => {
|
|
navigate(Routes.ForgotPassword)
|
|
}
|
|
|
|
const buttonDisabled = codeError
|
|
? isSubmitting
|
|
: isVerifyingCode || isSubmitting || !isPasswordValid || !doPasswordsMatch
|
|
|
|
const buttonLabel = codeError
|
|
? 'Demander un nouveau lien'
|
|
: isSubmitting
|
|
? 'Mise à jour...'
|
|
: isVerifyingCode
|
|
? 'Vérification...'
|
|
: 'Mettre à jour le mot de passe'
|
|
|
|
const buttonAction = codeError ? handleFallbackNavigation : handleSubmit
|
|
|
|
return (
|
|
<Page
|
|
width={isWeb ? 600 : null}
|
|
backgroundImg={isWeb ? background.loginBgWeb : background.homeBG}
|
|
headerType="NAVIGATION"
|
|
title="Réinitialisation"
|
|
>
|
|
<View style={{ flex: 1, paddingTop: 20 }}>
|
|
<ItemContainer height={360} disableKeyboardHeight>
|
|
<View style={{ gap: 32, paddingTop: 5, paddingHorizontal: 5 }}>
|
|
{codeError ? renderCodeError() : renderForm()}
|
|
<GradientButton
|
|
title={buttonLabel}
|
|
containerStyle={{
|
|
width: '80%',
|
|
alignSelf: 'center',
|
|
}}
|
|
onPress={buttonAction}
|
|
disabled={buttonDisabled}
|
|
/>
|
|
</View>
|
|
</ItemContainer>
|
|
</View>
|
|
</Page>
|
|
)
|
|
}
|
|
|
|
export default ResetPassword
|