diff --git a/ui/src/UserContext.tsx b/ui/src/UserContext.tsx index e896f51..3b5090b 100644 --- a/ui/src/UserContext.tsx +++ b/ui/src/UserContext.tsx @@ -2,7 +2,8 @@ import { Button, ButtonGroup, FloatingLabel, Modal } from "flowbite-react"; import { createContext, Dispatch, SetStateAction, useContext, useEffect, useState } from "react"; import useLocalStorageState from "use-local-storage-state"; import useApiClient from "./useApiClient"; -import { useNavigate } from "react-router-dom"; +import { useMatch, useNavigate } from "react-router-dom"; +import { RxCaretLeft } from "react-icons/rx"; export type User = { id: string; username: string; idToken: string; refreshToken: string }; export type Props = { @@ -23,14 +24,22 @@ export const UserContext = createContext({ export default function UserContextProvider({ children }: React.PropsWithChildren) { const navigate = useNavigate(); + const isWellKnownChangePassword = useMatch("/.well-known/change-password"); const [user, setUser] = useLocalStorageState("user", { defaultValue: undefined }); - const [isLoginOpen, setIsLoginOpen] = useState(false); + // Show modal by default if we're on the ChangePassword URL + const [isLoginOpen, setIsLoginOpen] = useState(!!isWellKnownChangePassword); const [isLoggedIn, setIsLoggedIn] = useState(false); useEffect(() => { setIsLoggedIn(!!user?.idToken); }, [user]); + useEffect(() => { + if (isWellKnownChangePassword) { + setUser(undefined); + } + }, [isWellKnownChangePassword, setUser]); + const showLogin = () => { setIsLoginOpen(true); }; @@ -74,7 +83,11 @@ export default function UserContextProvider({ children }: React.PropsWithChildre const onClose = () => { setIsLoginOpen(false); - navigate("#"); + if (isWellKnownChangePassword) { + navigate("/"); + } else { + navigate("#"); + } }; return ( @@ -115,13 +128,18 @@ function LoginModal({ onClose: () => void; setUser: Dispatch>; }) { - const [type, setType] = useState<"login" | "register" | "validateEmail" | "forgotPassword" | "forgotPasswordConfirm">("login"); + const isWellKnownChangePassword = useMatch("/.well-known/change-password"); + const navigate = useNavigate(); + const apiClient = useApiClient(); + + type Page = "login" | "register" | "validateEmail" | "forgotPassword" | "forgotPasswordConfirm"; + const [page, setPage] = useState(isWellKnownChangePassword ? "forgotPassword" : "login"); + const [username, setUsername] = useState(""); const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [verificationCode, setVerificationCode] = useState(""); const [errorMessage, setErrorMessage] = useState(""); - const apiClient = useApiClient(); const login = (e: React.FormEvent) => { e.preventDefault(); @@ -147,7 +165,7 @@ function LoginModal({ if (res.status >= 400) { res.json().then((err) => { if (err.__type === "UserNotConfirmedException") { - setType("validateEmail"); + setPage("validateEmail"); return; } setErrorMessage(err.message ?? "Failed to login."); @@ -167,6 +185,9 @@ function LoginModal({ apiClient.getCurrentProfile(idToken).then(({ json }) => { setUser({ id: json.userId, username: json.username, idToken: idToken, refreshToken: refreshToken }); onClose(); + if (isWellKnownChangePassword) { + navigate("/"); + } }); } else { setErrorMessage("Failed to login."); @@ -221,7 +242,7 @@ function LoginModal({ if (res.status >= 400) { res.json().then((err) => { if (err.__type === "ExpiredCodeException") { - setType("login"); + setPage("login"); setErrorMessage("Your code has expired, please try to log in again"); return; } @@ -261,7 +282,7 @@ function LoginModal({ }); return; } - setType("forgotPasswordConfirm"); + setPage("forgotPasswordConfirm"); }); }; @@ -286,7 +307,7 @@ function LoginModal({ if (res.status >= 400) { res.json().then((err) => { if (err.__type === "ExpiredCodeException") { - setType("forgotPassword"); + setPage("forgotPassword"); setErrorMessage("The code provided has expired, please request a new code by providing your username again."); return; } @@ -295,22 +316,28 @@ function LoginModal({ }); return; } - setType("login"); + setPage("login"); }); }; + const returnToLogin = ( + setPage("login")}> + Return to Login + + ); + const loginForm = ( <> - - -
(type === "login" ? login(e) : register(e))} autoComplete="on"> + (page === "login" ? login(e) : register(e))} autoComplete="on"> - {type === "register" && ( + {page === "register" && ( setPassword(e.target.value)} required /> -
- setType("forgotPassword")}> + @@ -364,6 +391,7 @@ function LoginModal({ const validateEmailForm = (
+ {returnToLogin} A verification code has been sent to your email. Please enter it to confirm your account. + {returnToLogin}
Please enter the username/email of the account you would like to reset the password for:
@@ -409,6 +438,7 @@ function LoginModal({ const changePasswordForm = ( + {returnToLogin} A verification code has been sent to your email. Please enter it and your new password. { - switch (type) { + switch (page) { case "login": case "register": return loginForm; diff --git a/ui/src/main.tsx b/ui/src/main.tsx index 27bfba6..e1cf081 100644 --- a/ui/src/main.tsx +++ b/ui/src/main.tsx @@ -23,6 +23,7 @@ const router = createBrowserRouter( createRoutesFromElements( }> } /> + } /> } /> } /> } />