SignIn.tsx
⎇
Raw
1import type { Accessor, Setter } from "solid-js";
2import { ClearSVG } from "./SVG";
3import "./SignIn.css";
4import { toast } from "../utils";
5import { LoadingSpinner } from "./LoadingScreen";
6import { Modal } from "./Modal";
7
8interface SignInProps {
9 showSignIn: Accessor<boolean>;
10 setShowSignIn: Setter<boolean>;
11 onLoggedIn: () => void;
12}
13
14export function SignIn(props: SignInProps) {
15 let formElement!: HTMLFormElement;
16 let usernameInput!: HTMLInputElement;
17 let passwordInput!: HTMLInputElement;
18 let loadingElement!: HTMLDivElement;
19 let submitButton!: HTMLButtonElement;
20
21 function clearInputs() {
22 usernameInput.value = "";
23 passwordInput.value = "";
24 }
25
26 async function doSignIn(e: Event, close: () => void) {
27 e.preventDefault();
28 usernameInput.setCustomValidity("");
29 if (!formElement.reportValidity()) return;
30 if (usernameInput.value.includes(":")) {
31 usernameInput.setCustomValidity('Username can\'t contain a colon ":"');
32 usernameInput.reportValidity();
33 return;
34 }
35 loadingElement.style.display = "block";
36 submitButton.disabled = true;
37 try {
38 const response = await fetch("/login", {
39 method: "POST",
40 body: `${usernameInput.value}:${passwordInput.value}`,
41 });
42 if (response.status === 200) {
43 toast("Login successful", "success");
44 props.onLoggedIn();
45 close();
46 } else if (response.status === 401) {
47 toast("Login failed: wrong credentials", "error");
48 } else {
49 toast(`Login failed: unknown error, code: ${response.status}`, "error", await (await response.blob()).text());
50 }
51 } finally {
52 loadingElement.style.display = "none";
53 submitButton.disabled = false;
54 }
55 }
56
57 return (
58 <Modal show={props.showSignIn} setShow={props.setShowSignIn} onClose={clearInputs}>
59 {(close) => (
60 <form ref={formElement} onSubmit={(e) => doSignIn(e, close)}>
61 <div class="signin-header title">
62 Sign in
63 <button type="button" title="Close dialog" onClick={close}>
64 <ClearSVG />
65 </button>
66 </div>
67 <hr />
68 <label for="username-input">Username:</label>
69 <input ref={usernameInput} id="username-input" type="text" minlength={1} required value="" />
70 <label for="password-input">Password:</label>
71 <input ref={passwordInput} id="password-input" type="password" minlength={1} required value="" />
72 <button ref={submitButton} type="submit" class="signin-button" title="Submit sign in">
73 Submit
74 <div ref={loadingElement} style={{ display: "none", width: "24px", height: "24px" }}>
75 <LoadingSpinner center />
76 </div>
77 </button>
78 </form>
79 )}
80 </Modal>
81 );
82}
83