Commit eb81d4bf by G

feat: implemented a functional login screen leveraging redux store to…

feat: implemented a functional login screen leveraging redux store to automatically redirect to the home screen once successfully logged in.
parent 168ea949
import { axiosInstance } from "@/axios";
import type { LoginPayload, Token } from "./types";
import type { LoginPayload, Token, UserData } from "./types";
export const login = (data: LoginPayload) => {
return axiosInstance.post<Token>("/login/token/", data);
......@@ -7,5 +7,5 @@ export const login = (data: LoginPayload) => {
// On this one you just provide the bear token and its should return the userData.
export const getUserData = () => {
return axiosInstance.get<Token>("/login/token/");
return axiosInstance.get<UserData>("/user-info/");
};
import { asp as g } from "@asp/asp";
import * as Button from "@components/ButtonNew";
import * as Input from "@components/InputNew";
import * as Modal from "@components/Modal";
import { MaterialIcons } from "@expo/vector-icons";
import Feather from "@expo/vector-icons/Feather";
import { useMutation } from "@tanstack/react-query";
import type { AxiosError } from "axios";
import { useRef, useState } from "react";
import { Text, View } from "react-native";
import { useDispatch } from "react-redux";
import { getUserData, login } from "../api";
import { setToken, setUserData } from "../slice";
export const LoginForm = () => {
const dispatch = useDispatch();
const usernameRef = useRef<string>("");
const passwordRef = useRef<string>("");
const loginMutation = useMutation({
mutationFn: () => login({ username: usernameRef.current, password: passwordRef.current }),
onSuccess: (data) => {
dispatch(setToken(data.data));
useDataMutation.mutate();
},
onError: (error: AxiosError) => {
setError(JSON.stringify(error.response?.data) || error.message);
},
});
const useDataMutation = useMutation({
mutationFn: getUserData,
onSuccess: (data) => {
dispatch(setUserData(data.data));
},
});
const [error, setError] = useState<string>("");
return (
<View
style={[
g.p_lg,
g.gap_lg,
{
backgroundColor: "white",
borderTopLeftRadius: 20,
borderTopRightRadius: 20,
},
]}
>
<Text style={[g.text_5xl, g.font_bold]}>Connexion</Text>
<Text style={{ color: "gray" }}>Bienvenue, vous nous avez manqué !</Text>
<Input.Container>
<Input.Header>Addresse e-mail</Input.Header>
<Input.FieldContainer>
<Feather name="smartphone" size={24} color="black" />
<Input.Field onChangeText={(v) => (usernameRef.current = v)} />
</Input.FieldContainer>
</Input.Container>
<Input.Container>
<Input.Header>Mot de passe</Input.Header>
<Input.FieldContainer>
<MaterialIcons name="lock-outline" size={24} color="black" />
<Input.Field secureTextEntry onChangeText={(v) => (passwordRef.current = v)} />
</Input.FieldContainer>
</Input.Container>
<Button.Container
style={[g.mt_5xl]}
isLoading={loginMutation.isPending}
onPress={() => loginMutation.mutate()}
>
<Button.Label style={{ color: "white" }}>Se connecter</Button.Label>
</Button.Container>
<Button.Container style={[{ backgroundColor: "#e8e8e9aa" }]}>
<Button.Label style={{ color: "black" }}>Créer un compte</Button.Label>
</Button.Container>
{/* MODAL */}
<Modal.SafeView>
<Modal.RnModal visible={!!error} style={{ backgroundColor: "rgba(0, 0, 0, 0.25)" }}>
<Modal.OuterView
style={[g.p_md, g.shadow_elevated, { backgroundColor: "white" }]}
>
<Text>{error}</Text>
<Button.Container onPress={() => setError("")}>
<Button.Label>OK</Button.Label>
</Button.Container>
</Modal.OuterView>
</Modal.RnModal>
</Modal.SafeView>
</View>
);
};
......@@ -18,6 +18,13 @@ export const authSlice = createSlice({
name: "auth",
initialState: initialState,
reducers: {
setToken: (state, action: PayloadAction<Token>) => {
state.token = action.payload;
},
setUserData: (state, action: PayloadAction<UserData>) => {
state.user = action.payload;
state.isAuthenticated = true;
},
login: (state, action: PayloadAction<UserDataAndToken>) => {
state.isAuthenticated = true;
state.user = action.payload.user;
......@@ -29,5 +36,5 @@ export const authSlice = createSlice({
},
});
export const { login, logout } = authSlice.actions;
export const { login, logout, setToken, setUserData } = authSlice.actions;
export const authReducer = authSlice.reducer;
import { useUserAuthenticationContext } from "@/contexts/UserAuthenticationContext";
import { LOG } from "@logger";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import HomeUserNotLoggedIn from "@screens/HomeUserNotLoggedIn";
......@@ -6,6 +5,8 @@ import PaymentResultScreen from "@screens/PaymentResultScreen";
import UserLoginScreen from "@screens/UserLoginScreen";
import WaveQrCodePaymentScreen from "@screens/WaveQrCodePaymentScreen";
import { memo } from "react";
import { useSelector } from "react-redux";
import type { RootState } from "@/redux";
import { AppBottomTabsNavigator } from "./AppBottomTabsNavigator";
import type { ImainStackNavigator } from "./Types";
......@@ -50,6 +51,6 @@ const AppMainStackNavigator: React.FC<IappMainStackNavigatorProps> = ({ isAuthen
export default memo(AppMainStackNavigator);
export const AppMainStackNavigatorAuthWrapper = () => {
const { isAuthenticated } = useUserAuthenticationContext();
const isAuthenticated = useSelector((state: RootState) => state.auth.isAuthenticated);
return <AppMainStackNavigator isAuthenticated={isAuthenticated} />;
};
import { useUserAuthenticationContext } from "@/contexts/UserAuthenticationContext";
import type { MainStackScreenComponentProps } from "@/navigations/Types";
import Button from "@components/Button";
import InputWithTopLabel from "@components/InputWithTopLabel";
import Box from "@components/bases/Box";
import WrapperWithDefaultBeasyBackgroundAndSafeAreaFull from "@components/wrappers/WrapperWithDefaultBeasyBackgroundAndSafeAreaFull";
import { Fontisto } from "@expo/vector-icons";
import { asp as g } from "@asp/asp";
import { LOG } from "@logger";
import Card from "@re-card";
import Text from "@re-text";
import { useCallback, useState } from "react";
import { TouchableOpacity } from "react-native";
import { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { ImageBackground, View } from "react-native";
import { LoginForm } from "@/features/auth/components/LoginForm";
import type { MainStackScreenComponentProps } from "@/navigations/Types";
const log = LOG.extend("UserLoginScreen");
const UserLoginScreen: MainStackScreenComponentProps<"userLoginScreen"> = ({ navigation }) => {
log.debug("UserLoginScreen");
const { login, isAuthenticating } = useUserAuthenticationContext();
// TODO : Remove default value for email and password
const [email, setEmail] = useState("admin");
const [password, setPassword] = useState("admin");
const insets = useSafeAreaInsets();
const submit = useCallback(() => {
login(email, password);
}, [email, password, login]);
return (
<WrapperWithDefaultBeasyBackgroundAndSafeAreaFull>
<Box height={"100%"}>
<Box style={{ height: "20%" }} px={"l"}>
<Box
px={"m"}
justifyContent={"space-between"}
flexDirection={"row"}
alignItems={"center"}
<View style={[g.flex_1]}>
<ImageBackground
source={require("@assets/background.png")}
style={[g.flex_1, g.flex_col, g.justify_end]}
>
<TouchableOpacity onPress={() => navigation.goBack()}>
<Fontisto name="close-a" size={12} color="black" />
</TouchableOpacity>
<TouchableOpacity>
<Text>Mot de passe oublie ?</Text>
</TouchableOpacity>
</Box>
</Box>
<Card variant={"curvedTopContainer"} style={{ marginTop: "auto" }}>
<KeyboardAwareScrollView
// extraScrollHeight={-125}
extraHeight={10}
keyboardOpeningTime={Number.MAX_SAFE_INTEGER}
showsVerticalScrollIndicator={false}
keyboardShouldPersistTaps={"handled"}
>
<Box p={"l"} paddingTop={"x100"} mb={"s"}>
<Box mb={"l"}>
<Text fontSize={40} fontWeight={"bold"} variant={"black"}>
Connexion
</Text>
<Text color={"gray"}>Bienvenue, vous nous avez manqué !</Text>
</Box>
<Box gap={"m"}>
<InputWithTopLabel
label="Email"
// value={email}
autoCorrect={false}
textContentType="emailAddress"
onChangeText={(email) => setEmail(email)}
/>
<InputWithTopLabel
label="Mot de passe"
secureTextEntry={true}
textContentType="oneTimeCode"
// value={password}
onChangeText={(text) => setPassword(text)}
/>
</Box>
</Box>
<Box p={"s"}>
<Button
variant={"full"}
textVariants={"primary"}
label="Se connecter"
onPress={() => {
submit();
}}
isLoading={isAuthenticating}
/>
<Button
variant={"lightGray"}
textVariants={"black"}
label="Creer un compte"
onPress={() => {}}
/>
</Box>
</KeyboardAwareScrollView>
</Card>
</Box>
<Box
position={"absolute"}
bottom={0}
height={insets.bottom}
backgroundColor={"white"}
width={"100%"}
/>
</WrapperWithDefaultBeasyBackgroundAndSafeAreaFull>
<LoginForm />
</ImageBackground>
</View>
);
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment