home, subscription and other fixes

This commit is contained in:
Thomas Demirdjian
2025-11-06 17:33:07 +01:00
parent 5f777f3b6d
commit 4365f1e46d
31 changed files with 1579 additions and 348 deletions
+298
View File
@@ -0,0 +1,298 @@
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;