parent
ad114b52eb
commit
7144d07832
7 changed files with 268 additions and 22 deletions
@ -0,0 +1,70 @@ |
|||||||
|
import React, { useState } from 'react'; |
||||||
|
import { WindowState } from "../Windows"; |
||||||
|
import { Box, Paper, Typography, TextField, Button } from "@material-ui/core"; |
||||||
|
import { useHistory, useLocation } from 'react-router'; |
||||||
|
import { useAuth, Auth } from '../../../lib/useAuth'; |
||||||
|
|
||||||
|
export interface LoginWindowState extends WindowState { } |
||||||
|
export enum LoginWindowStateActions { } |
||||||
|
export function LoginWindowReducer(state: LoginWindowState, action: any) { } |
||||||
|
|
||||||
|
export default function LoginWindow(props: {}) { |
||||||
|
let history: any = useHistory(); |
||||||
|
let location: any = useLocation(); |
||||||
|
let auth: Auth = useAuth(); |
||||||
|
let { from } = location.state || { from: { pathname: "/" } }; |
||||||
|
|
||||||
|
const [email, setEmail] = useState<string>(""); |
||||||
|
const [password, setPassword] = useState<string>(""); |
||||||
|
|
||||||
|
const onSubmit = (event: any) => { |
||||||
|
event.preventDefault(); |
||||||
|
auth.signin(email, password) |
||||||
|
.then(() => { |
||||||
|
history.replace(from); |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
return <Box width="100%" justifyContent="center" display="flex" flexWrap="wrap"> |
||||||
|
<Box |
||||||
|
m={1} |
||||||
|
mt={4} |
||||||
|
> |
||||||
|
<Paper> |
||||||
|
<Box p={3}> |
||||||
|
<Typography variant="h5">Sign in</Typography> |
||||||
|
<form noValidate onSubmit={onSubmit}> |
||||||
|
<TextField |
||||||
|
variant="outlined" |
||||||
|
margin="normal" |
||||||
|
required |
||||||
|
fullWidth |
||||||
|
id="email" |
||||||
|
label="Email" |
||||||
|
name="email" |
||||||
|
autoFocus |
||||||
|
onInput={(e: any) => setEmail(e.target.value)} |
||||||
|
/> |
||||||
|
<TextField |
||||||
|
variant="outlined" |
||||||
|
margin="normal" |
||||||
|
required |
||||||
|
fullWidth |
||||||
|
id="password" |
||||||
|
label="Password" |
||||||
|
name="password" |
||||||
|
type="password" |
||||||
|
onInput={(e: any) => setPassword(e.target.value)} |
||||||
|
/> |
||||||
|
<Button |
||||||
|
type="submit" |
||||||
|
fullWidth |
||||||
|
variant="outlined" |
||||||
|
color="primary" |
||||||
|
>Sign in</Button> |
||||||
|
</form> |
||||||
|
</Box> |
||||||
|
</Paper> |
||||||
|
</Box> |
||||||
|
</Box> |
||||||
|
} |
@ -0,0 +1,140 @@ |
|||||||
|
// Note: Based on https://usehooks.com/useAuth/
|
||||||
|
|
||||||
|
// import React from "react";
|
||||||
|
// import { ProvideAuth } from "./use-auth.js";
|
||||||
|
// function App(props) {
|
||||||
|
// return (
|
||||||
|
// <ProvideAuth>
|
||||||
|
// {/*
|
||||||
|
// Route components here, depending on how your app is structured.
|
||||||
|
// If using Next.js this would be /pages/_app.js
|
||||||
|
// */}
|
||||||
|
// </ProvideAuth>
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Any component that wants auth state
|
||||||
|
// import React from "react";
|
||||||
|
// import { useAuth } from "./use-auth.js";
|
||||||
|
|
||||||
|
// function Navbar(props) {
|
||||||
|
// // Get auth state and re-render anytime it changes
|
||||||
|
// const auth = useAuth();
|
||||||
|
|
||||||
|
// return (
|
||||||
|
// <NavbarContainer>
|
||||||
|
// <Logo />
|
||||||
|
// <Menu>
|
||||||
|
// <Link to="/about">About</Link>
|
||||||
|
// <Link to="/contact">Contact</Link>
|
||||||
|
// {auth.user ? (
|
||||||
|
// <Fragment>
|
||||||
|
// <Link to="/account">Account ({auth.user.email})</Link>
|
||||||
|
// <Button onClick={() => auth.signout()}>Signout</Button>
|
||||||
|
// </Fragment>
|
||||||
|
// ) : (
|
||||||
|
// <Link to="/signin">Signin</Link>
|
||||||
|
// )}
|
||||||
|
// </Menu>
|
||||||
|
// </NavbarContainer>
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Hook (use-auth.js)
|
||||||
|
|
||||||
|
import React, { useState, useEffect, useContext, createContext } from "react"; |
||||||
|
import * as serverApi from '../api'; |
||||||
|
|
||||||
|
export interface AuthUser { |
||||||
|
id: number, |
||||||
|
} |
||||||
|
|
||||||
|
export interface Auth { |
||||||
|
user: AuthUser | null, |
||||||
|
signout: () => void, |
||||||
|
signin: (email: string, password: string) => Promise<AuthUser>, |
||||||
|
signup: (email: string, password: string) => void, |
||||||
|
}; |
||||||
|
|
||||||
|
const authContext = createContext<Auth>({ |
||||||
|
user: null, |
||||||
|
signout: () => { }, |
||||||
|
signin: (email: string, password: string) => { |
||||||
|
throw new Error("Auth object not initialized."); |
||||||
|
}, |
||||||
|
signup: (email: string, password: string) => { |
||||||
|
throw new Error("Auth object not initialized."); |
||||||
|
}, |
||||||
|
}); |
||||||
|
|
||||||
|
export function ProvideAuth(props: { children: any }) { |
||||||
|
const auth = useProvideAuth(); |
||||||
|
return <authContext.Provider value={auth}>{props.children}</authContext.Provider>; |
||||||
|
} |
||||||
|
|
||||||
|
export const useAuth = () => { |
||||||
|
return useContext(authContext); |
||||||
|
}; |
||||||
|
|
||||||
|
function useProvideAuth() { |
||||||
|
const [user, setUser] = useState<AuthUser | null>(null); |
||||||
|
|
||||||
|
// FIXME: password shouldn't be encoded into the URL.
|
||||||
|
const signin = (email: string, password: string) => { |
||||||
|
return (async () => { |
||||||
|
const urlBase = (process.env.REACT_APP_BACKEND || "") + serverApi.LoginEndpoint; |
||||||
|
const url = `${urlBase}?username=${encodeURIComponent(email)}&password=${encodeURIComponent(password)}`; |
||||||
|
|
||||||
|
const response = await fetch(url, { method: "POST" }); |
||||||
|
const json = await response.json(); |
||||||
|
if(!("userId" in json)) { |
||||||
|
throw new Error("No UserID received from login."); |
||||||
|
} |
||||||
|
|
||||||
|
const user = { |
||||||
|
id: json.userId |
||||||
|
} |
||||||
|
setUser(user); |
||||||
|
return user; |
||||||
|
})(); |
||||||
|
}; |
||||||
|
|
||||||
|
const signup = (email: string, password: string) => { |
||||||
|
return (async () => { |
||||||
|
const requestOpts = { |
||||||
|
method: 'POST', |
||||||
|
headers: { 'Content-Type': 'application/json' }, |
||||||
|
body: JSON.stringify({ |
||||||
|
email: email, |
||||||
|
password: password, |
||||||
|
}) |
||||||
|
}; |
||||||
|
|
||||||
|
return (async () => { |
||||||
|
const response = await fetch((process.env.REACT_APP_BACKEND || "") + serverApi.RegisterUserEndpoint, requestOpts) |
||||||
|
if(!response.ok) { |
||||||
|
throw new Error("Failed to register user.") |
||||||
|
} |
||||||
|
})(); |
||||||
|
})(); |
||||||
|
}; |
||||||
|
|
||||||
|
const signout = () => { |
||||||
|
return (async () => { |
||||||
|
const url = (process.env.REACT_APP_BACKEND || "") + serverApi.LogoutEndpoint; |
||||||
|
const response = await fetch(url, { method: "POST" }); |
||||||
|
if(!response.ok) { |
||||||
|
throw new Error("Failed to log out."); |
||||||
|
} |
||||||
|
setUser(null); |
||||||
|
})(); |
||||||
|
}; |
||||||
|
|
||||||
|
// Return the user object and auth methods
|
||||||
|
return { |
||||||
|
user, |
||||||
|
signin, |
||||||
|
signup, |
||||||
|
signout, |
||||||
|
}; |
||||||
|
} |
Loading…
Reference in new issue