Loading mobile/src/auth/errors/reset_password_error.ts 0 → 100644 +12 −0 Original line number Diff line number Diff line export enum ChangePaswordError { PASSWORD_MISMATCH, UNDEFINED } export class ResetPasswordError extends Error { public readonly type: ChangePaswordError; constructor(type: ChangePaswordError, message?: string,) { super(message); this.type = type; } } No newline at end of file mobile/src/auth/hooks/useResetPassword.ts 0 → 100644 +77 −0 Original line number Diff line number Diff line import { useForm } from "react-hook-form"; import { useDataContext } from "../../common/contexts/data_context"; import { useState } from "react"; import { ApiRequestStatus } from "../../common/constants/api_request_states"; import { router } from "expo-router"; import { ChangePaswordError, ResetPasswordError } from "../errors/reset_password_error"; export interface ResetPasswordFormValues { email: string; newPassword: string; newPasswordConfirmation: string; code: string; } export const useResetPassword = () => { const { authRepository } = useDataContext(); const { control, handleSubmit, getValues, setError, setValue } = useForm<ResetPasswordFormValues>({ defaultValues: { email: "", newPassword: "", newPasswordConfirmation: "", code: "", } }); const [status, setStatus] = useState(ApiRequestStatus.IDLE); const setLoading = async () => { setStatus(ApiRequestStatus.LOADING); }; const getResetCode = async () => { await setLoading(); if (!getValues().email) { console.log(getValues()) setError("email", { type: "manual", message: "Email is required" }); throw new Error("Email is required"); } await authRepository!.getResetCode(getValues().email); setStatus(ApiRequestStatus.SUCCESS); }; const validSubmit = async (data: ResetPasswordFormValues) => { try { await setLoading(); if (data.newPassword !== data.newPasswordConfirmation) { setError("newPasswordConfirmation", { type: "manual", message: "Passwords do not match" }); return; } await authRepository!.resetPassword({ newPassword: data.newPassword, code: data.code, email: data.email }) router.replace("/login"); } catch (error) { if (error instanceof ResetPasswordError && error.type == ChangePaswordError.UNDEFINED) { throw new Error(); } } }; const errorSubmit = async () => { throw new Error(); }; const onSubmit = async () => { await handleSubmit(validSubmit, errorSubmit)(); }; return { control, getResetCode, setValue, onSubmit, status }; } mobile/src/auth/hooks/useResetPasswordControl.tsx 0 → 100644 +83 −0 Original line number Diff line number Diff line import { useEffect, useState } from "react"; import { ResetPasswordForm } from "../components/reset_password_form"; import { CodeForm } from "../components/code_form"; import { PasswordForm } from "../components/password_form"; import { useResetPassword } from "./useResetPassword"; interface ResetPasswordSlide { slide: JSX.Element; callback: () => Promise<void>; onError?: () => void; } export const useResetPasswordControl = () => { const { control, getResetCode, setValue, onSubmit, status: requestStatus, } = useResetPassword(); const slides: ResetPasswordSlide[] = [ { slide: <ResetPasswordForm control={control} />, callback: async () => { await getResetCode(); }, }, { slide: <CodeForm setValue={setValue} />, callback: async () => { console.log("Reset Password 1"); }, }, { slide: <PasswordForm control={control} />, callback: async () => { onSubmit(); }, }, ]; const [step, setStep] = useState(0); const [slide, setSlide] = useState(slides[step]); const [isLast, setIsLast] = useState(step === slides.length - 1); const [isFirst, setIsFirst] = useState(step === 0); const onNext = async () => { try { await slide.callback(); setStep(step + 1); } catch (error) { console.log(error); slide.onError && slide.onError(); } }; const onPrevious = () => { setStep(step - 1); }; const onFinish = () => { try { slide.callback(); } catch (e) { slide.onError && slide.onError(); } }; useEffect(() => { setSlide(slides[step]); setIsFirst(step === 0); setIsLast(step === slides.length - 1); }, [step]); return { onNext, onFinish, slide: slide.slide, isFirst, isLast, onPrevious, requestStatus, }; }; mobile/src/auth/pages/reset_password_page.tsx +12 −162 Original line number Diff line number Diff line import { useEffect, useState, useCallback } from "react"; import { SlideControl } from "../../common/components/slide_control"; import { ResetPasswordForm } from "../components/reset_password_form"; import { View, StyleSheet } from "react-native"; import { CodeForm } from "../components/code_form"; import { useDataContext } from "../../common/contexts/data_context"; import { useForm } from "react-hook-form"; import { ApiRequestStatus } from "../../common/constants/api_request_states"; import { PasswordForm } from "../components/password_form"; import { router } from "expo-router"; import { FullPageLoader } from "../../common/components/full_page_loader"; interface ResetPasswordSlide { slide: JSX.Element; callback: () => Promise<void>; onError?: () => void; } export interface ResetPasswordFormValues { email: string; newPassword: string; newPasswordConfirmation: string; code: string; } export enum ChangePaswordError { PASSWORD_MISMATCH, UNDEFINED } export class ResetPasswordError extends Error { public readonly type: ChangePaswordError; constructor( type: ChangePaswordError, message? :string,){ super(message); this.type = type; } } export const useResetPassword = () => { const { authRepository } = useDataContext(); const { control, handleSubmit, getValues, setError, setValue } = useForm<ResetPasswordFormValues>({ defaultValues: { email: "", newPassword: "", newPasswordConfirmation: "", code: "", } }); const [status, setStatus] = useState(ApiRequestStatus.IDLE); const setLoading = async () => { setStatus(ApiRequestStatus.LOADING); }; const getResetCode = async () => { await setLoading(); if (!getValues().email) { console.log(getValues()) setError("email", { type: "manual", message: "Email is required" }); throw new Error("Email is required"); } await authRepository!.getResetCode(getValues().email); setStatus(ApiRequestStatus.SUCCESS); }; const validSubmit = async (data: ResetPasswordFormValues) => { try { await setLoading(); if (data.newPassword !== data.newPasswordConfirmation) { setError("newPasswordConfirmation", { type: "manual", message: "Passwords do not match" }); return; } await authRepository!.resetPassword({ newPassword: data.newPassword, code: data.code, email: data.email }) router.replace("/login"); } catch(error) { if (error instanceof ResetPasswordError && error.type == ChangePaswordError.UNDEFINED) { throw new Error(); } } }; const errorSubmit = async () => { throw new Error(); }; const onSubmit = async () => { await handleSubmit(validSubmit, errorSubmit)(); }; return { control, getResetCode, setValue, onSubmit, status }; } const useResetPasswordControl = () => { const { control, getResetCode, setValue, onSubmit, status: requestStatus } = useResetPassword(); const slides: ResetPasswordSlide[] = [ { slide: <ResetPasswordForm control={control}/>, callback: async () => { await getResetCode(); } }, { slide: <CodeForm setValue={setValue}/>, callback: async () => { console.log("Reset Password 1"); } }, { slide: <PasswordForm control={control}/>, callback: async () => { onSubmit(); } } ]; const [step, setStep] = useState(0); const [slide, setSlide] = useState(slides[step]); const [isLast, setIsLast] = useState(step === slides.length - 1); const [isFirst, setIsFirst] = useState(step === 0); const onNext = async () => { try { await slide.callback(); setStep(step + 1); } catch (error) { console.log(error); slide.onError && slide.onError(); } }; const onPrevious = () => { setStep(step - 1); }; const onFinish = () => { try { slide.callback(); } catch (e) { slide.onError && slide.onError(); } }; useEffect(() => { setSlide(slides[step]); setIsFirst(step === 0); setIsLast(step === slides.length - 1); }, [step]); return { onNext, onFinish, slide: slide.slide, isFirst, isLast, onPrevious, requestStatus }; }; import { useResetPasswordControl } from "../hooks/useResetPasswordControl"; export const ResetPasswordPage = () => { const { onNext, onFinish, slide, isFirst, isLast, onPrevious, requestStatus } = useResetPasswordControl(); const { onNext, onFinish, slide, isFirst, isLast, onPrevious, requestStatus, } = useResetPasswordControl(); if (requestStatus === ApiRequestStatus.LOADING) { return <FullPageLoader/> return <FullPageLoader />; } return ( <View style={styles.mainContainer}> Loading Loading
mobile/src/auth/errors/reset_password_error.ts 0 → 100644 +12 −0 Original line number Diff line number Diff line export enum ChangePaswordError { PASSWORD_MISMATCH, UNDEFINED } export class ResetPasswordError extends Error { public readonly type: ChangePaswordError; constructor(type: ChangePaswordError, message?: string,) { super(message); this.type = type; } } No newline at end of file
mobile/src/auth/hooks/useResetPassword.ts 0 → 100644 +77 −0 Original line number Diff line number Diff line import { useForm } from "react-hook-form"; import { useDataContext } from "../../common/contexts/data_context"; import { useState } from "react"; import { ApiRequestStatus } from "../../common/constants/api_request_states"; import { router } from "expo-router"; import { ChangePaswordError, ResetPasswordError } from "../errors/reset_password_error"; export interface ResetPasswordFormValues { email: string; newPassword: string; newPasswordConfirmation: string; code: string; } export const useResetPassword = () => { const { authRepository } = useDataContext(); const { control, handleSubmit, getValues, setError, setValue } = useForm<ResetPasswordFormValues>({ defaultValues: { email: "", newPassword: "", newPasswordConfirmation: "", code: "", } }); const [status, setStatus] = useState(ApiRequestStatus.IDLE); const setLoading = async () => { setStatus(ApiRequestStatus.LOADING); }; const getResetCode = async () => { await setLoading(); if (!getValues().email) { console.log(getValues()) setError("email", { type: "manual", message: "Email is required" }); throw new Error("Email is required"); } await authRepository!.getResetCode(getValues().email); setStatus(ApiRequestStatus.SUCCESS); }; const validSubmit = async (data: ResetPasswordFormValues) => { try { await setLoading(); if (data.newPassword !== data.newPasswordConfirmation) { setError("newPasswordConfirmation", { type: "manual", message: "Passwords do not match" }); return; } await authRepository!.resetPassword({ newPassword: data.newPassword, code: data.code, email: data.email }) router.replace("/login"); } catch (error) { if (error instanceof ResetPasswordError && error.type == ChangePaswordError.UNDEFINED) { throw new Error(); } } }; const errorSubmit = async () => { throw new Error(); }; const onSubmit = async () => { await handleSubmit(validSubmit, errorSubmit)(); }; return { control, getResetCode, setValue, onSubmit, status }; }
mobile/src/auth/hooks/useResetPasswordControl.tsx 0 → 100644 +83 −0 Original line number Diff line number Diff line import { useEffect, useState } from "react"; import { ResetPasswordForm } from "../components/reset_password_form"; import { CodeForm } from "../components/code_form"; import { PasswordForm } from "../components/password_form"; import { useResetPassword } from "./useResetPassword"; interface ResetPasswordSlide { slide: JSX.Element; callback: () => Promise<void>; onError?: () => void; } export const useResetPasswordControl = () => { const { control, getResetCode, setValue, onSubmit, status: requestStatus, } = useResetPassword(); const slides: ResetPasswordSlide[] = [ { slide: <ResetPasswordForm control={control} />, callback: async () => { await getResetCode(); }, }, { slide: <CodeForm setValue={setValue} />, callback: async () => { console.log("Reset Password 1"); }, }, { slide: <PasswordForm control={control} />, callback: async () => { onSubmit(); }, }, ]; const [step, setStep] = useState(0); const [slide, setSlide] = useState(slides[step]); const [isLast, setIsLast] = useState(step === slides.length - 1); const [isFirst, setIsFirst] = useState(step === 0); const onNext = async () => { try { await slide.callback(); setStep(step + 1); } catch (error) { console.log(error); slide.onError && slide.onError(); } }; const onPrevious = () => { setStep(step - 1); }; const onFinish = () => { try { slide.callback(); } catch (e) { slide.onError && slide.onError(); } }; useEffect(() => { setSlide(slides[step]); setIsFirst(step === 0); setIsLast(step === slides.length - 1); }, [step]); return { onNext, onFinish, slide: slide.slide, isFirst, isLast, onPrevious, requestStatus, }; };
mobile/src/auth/pages/reset_password_page.tsx +12 −162 Original line number Diff line number Diff line import { useEffect, useState, useCallback } from "react"; import { SlideControl } from "../../common/components/slide_control"; import { ResetPasswordForm } from "../components/reset_password_form"; import { View, StyleSheet } from "react-native"; import { CodeForm } from "../components/code_form"; import { useDataContext } from "../../common/contexts/data_context"; import { useForm } from "react-hook-form"; import { ApiRequestStatus } from "../../common/constants/api_request_states"; import { PasswordForm } from "../components/password_form"; import { router } from "expo-router"; import { FullPageLoader } from "../../common/components/full_page_loader"; interface ResetPasswordSlide { slide: JSX.Element; callback: () => Promise<void>; onError?: () => void; } export interface ResetPasswordFormValues { email: string; newPassword: string; newPasswordConfirmation: string; code: string; } export enum ChangePaswordError { PASSWORD_MISMATCH, UNDEFINED } export class ResetPasswordError extends Error { public readonly type: ChangePaswordError; constructor( type: ChangePaswordError, message? :string,){ super(message); this.type = type; } } export const useResetPassword = () => { const { authRepository } = useDataContext(); const { control, handleSubmit, getValues, setError, setValue } = useForm<ResetPasswordFormValues>({ defaultValues: { email: "", newPassword: "", newPasswordConfirmation: "", code: "", } }); const [status, setStatus] = useState(ApiRequestStatus.IDLE); const setLoading = async () => { setStatus(ApiRequestStatus.LOADING); }; const getResetCode = async () => { await setLoading(); if (!getValues().email) { console.log(getValues()) setError("email", { type: "manual", message: "Email is required" }); throw new Error("Email is required"); } await authRepository!.getResetCode(getValues().email); setStatus(ApiRequestStatus.SUCCESS); }; const validSubmit = async (data: ResetPasswordFormValues) => { try { await setLoading(); if (data.newPassword !== data.newPasswordConfirmation) { setError("newPasswordConfirmation", { type: "manual", message: "Passwords do not match" }); return; } await authRepository!.resetPassword({ newPassword: data.newPassword, code: data.code, email: data.email }) router.replace("/login"); } catch(error) { if (error instanceof ResetPasswordError && error.type == ChangePaswordError.UNDEFINED) { throw new Error(); } } }; const errorSubmit = async () => { throw new Error(); }; const onSubmit = async () => { await handleSubmit(validSubmit, errorSubmit)(); }; return { control, getResetCode, setValue, onSubmit, status }; } const useResetPasswordControl = () => { const { control, getResetCode, setValue, onSubmit, status: requestStatus } = useResetPassword(); const slides: ResetPasswordSlide[] = [ { slide: <ResetPasswordForm control={control}/>, callback: async () => { await getResetCode(); } }, { slide: <CodeForm setValue={setValue}/>, callback: async () => { console.log("Reset Password 1"); } }, { slide: <PasswordForm control={control}/>, callback: async () => { onSubmit(); } } ]; const [step, setStep] = useState(0); const [slide, setSlide] = useState(slides[step]); const [isLast, setIsLast] = useState(step === slides.length - 1); const [isFirst, setIsFirst] = useState(step === 0); const onNext = async () => { try { await slide.callback(); setStep(step + 1); } catch (error) { console.log(error); slide.onError && slide.onError(); } }; const onPrevious = () => { setStep(step - 1); }; const onFinish = () => { try { slide.callback(); } catch (e) { slide.onError && slide.onError(); } }; useEffect(() => { setSlide(slides[step]); setIsFirst(step === 0); setIsLast(step === slides.length - 1); }, [step]); return { onNext, onFinish, slide: slide.slide, isFirst, isLast, onPrevious, requestStatus }; }; import { useResetPasswordControl } from "../hooks/useResetPasswordControl"; export const ResetPasswordPage = () => { const { onNext, onFinish, slide, isFirst, isLast, onPrevious, requestStatus } = useResetPasswordControl(); const { onNext, onFinish, slide, isFirst, isLast, onPrevious, requestStatus, } = useResetPasswordControl(); if (requestStatus === ApiRequestStatus.LOADING) { return <FullPageLoader/> return <FullPageLoader />; } return ( <View style={styles.mainContainer}> Loading