Files
musicland/src/screens/ResetPassword.js
T
2026-01-12 16:01:32 +01:00

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