cutt/src/Login.tsx

126 lines
3.3 KiB
TypeScript

import { useEffect, useState } from "react";
import { currentUser, login, User } from "./api";
import Header from "./Header";
import { useLocation, useNavigate } from "react-router";
import { Eye, EyeSlash } from "./Icons";
export interface LoginProps {
onLogin: (user: User) => void;
}
export const Login = ({ onLogin }: LoginProps) => {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
const [loading, setLoading] = useState(false);
const [visible, setVisible] = useState(false);
const navigate = useNavigate();
const location = useLocation();
async function doLogin() {
setLoading(true);
setError("");
const timeout = new Promise((r) => setTimeout(r, 1000));
let user: User;
try {
await login({ username, password });
user = await currentUser();
} catch (e) {
await timeout;
setError("failed");
setLoading(false);
return;
}
await timeout;
onLogin(user);
}
function handleSubmit(e: React.FormEvent) {
e.preventDefault();
doLogin();
}
useEffect(() => {
if (location.state) {
const queryUsername = location.state.username;
const queryPassword = location.state.password;
if (queryUsername) setUsername(queryUsername);
if (queryPassword) setPassword(queryPassword);
navigate(location.pathname, { replace: true });
}
}, []);
return (
<>
<Header />
<form onSubmit={handleSubmit}>
<div
style={{
position: "relative",
display: "flex",
alignItems: "end",
}}
>
<div
style={{
width: "100%",
marginRight: "8px",
display: "flex",
justifyContent: "center",
flexDirection: "column",
}}
>
<div>
<input
type="text"
id="username"
name="username"
placeholder="username"
required
value={username}
onChange={(evt) => {
setError("");
setUsername(evt.target.value);
}}
/>
</div>
<div>
<input
type={visible ? "text" : "password"}
id="password"
name="password"
placeholder="password"
minLength={8}
value={password}
required
onChange={(evt) => {
setError("");
setPassword(evt.target.value);
}}
/>
</div>
</div>
<div
style={{
position: "absolute",
right: "-1em",
margin: "auto 4px",
background: "unset",
fontSize: "x-large",
cursor: "pointer",
}}
onClick={() => setVisible(!visible)}
>
{visible ? <Eye /> : <EyeSlash />}
</div>
</div>
{error && <span style={{ color: "red" }}>{error}</span>}
<button type="submit" value="login" style={{ fontSize: "small" }}>
login
</button>
{loading && <span className="loader" />}
</form>
</>
);
};