Last active
March 6, 2020 23:41
-
-
Save jarkin13/a95233b8606777bdc1c547853bdc72cd to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useEffect } from "react"; | |
import { HashRouter, Route, Switch, Redirect } from "react-router-dom"; | |
// components | |
import Layout from "./Layout"; | |
// pages | |
import Error from "../pages/error"; | |
import Login from "../pages/login"; | |
import Started from "../pages/start"; | |
// context | |
import { useUserState, useUserDispatch } from "../context/UserContext"; | |
import { setExpTime, signinUser } from "../context/user/Authenticated"; | |
// amplify config | |
import Amplify, { Hub } from 'aws-amplify'; | |
import awsconfig from '../aws-exports'; | |
import { REDIRECT_URL } from '../helpers/envConfig'; | |
const oauth = { | |
domain: awsconfig.oauth.domain, | |
scope: awsconfig.oauth.scope, | |
redirectSignIn: REDIRECT_URL, | |
redirectSignOut: REDIRECT_URL, | |
responseType: awsconfig.oauth.responseType, | |
}; | |
var configUpdate = awsconfig; | |
configUpdate.oauth = oauth; | |
Amplify.configure(configUpdate); | |
function App() { | |
// global | |
const { isAuthenticated, started } = useUserState(); | |
const dispatch = useUserDispatch(); | |
useEffect(() => { | |
Hub.listen('auth', (data) => { | |
const { payload } = data; | |
if (payload.event === 'signIn') { | |
localStorage.setItem('id_token', 1); | |
const setTime = setExpTime(10, false); | |
localStorage.setItem("check_auth", JSON.stringify(setTime)); | |
signinUser(payload.data, dispatch); | |
} | |
}); | |
}, [dispatch]); | |
return ( | |
<HashRouter> | |
<Switch> | |
<Route exact path="/" render={() => <Redirect to="/login" />} /> | |
<Route | |
exact | |
path="/app" | |
render={() => <Redirect to="/app/dashboard" />} | |
/> | |
<PrivateRoute path="/app" component={Layout} /> | |
<PrivateRoute path="/get-started" component={Started} /> | |
<PublicRoute path="/login" component={Login} /> | |
<Route component={Error} /> | |
</Switch> | |
</HashRouter> | |
); | |
// ####################################################################### | |
function PrivateRoute({ component, ...rest }) { | |
return ( | |
<Route | |
{...rest} | |
render={props => | |
isAuthenticated ? ( | |
React.createElement(component, props) | |
) : ( | |
<Redirect | |
to={{ | |
pathname: "/login", | |
state: { | |
from: props.location, | |
}, | |
}} | |
/> | |
) | |
} | |
/> | |
); | |
} | |
function PublicRoute({ component, ...rest }) { | |
return ( | |
<Route | |
{...rest} | |
render={((props) => { | |
return ( | |
isAuthenticated ? ( | |
started ? ( | |
<Redirect | |
to={{ | |
pathname: "/app/dashboard", | |
}} | |
/> | |
) : ( | |
<Redirect | |
to={{ | |
pathname: "/get-started", | |
state: { | |
from: props.location, | |
}, | |
}} | |
/> | |
) | |
) : ( | |
React.createElement(component, props) | |
) | |
) | |
})} | |
/> | |
); | |
} | |
} | |
export default App; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// AWS | |
import { Auth } from "aws-amplify"; | |
import { API, graphqlOperation } from "aws-amplify"; | |
import { getUser } from "../../graphql/queries"; | |
import { createUser } from "../../graphql/mutations"; | |
// Image | |
import userImage from "../../images/user-placeholder.png"; | |
function reducer(state, action) { | |
switch(action.type) { | |
case "LOADING": | |
return { ...state, loading: true, error: false }; | |
case "SUCCESS": | |
return { ...state, loading: false, error: false }; | |
case "ERROR": | |
return { ...state, loading: false, error: action.err }; | |
default: | |
return state; | |
} | |
} | |
const setExpTime = (time, minutes = true) => { | |
const exptime = new Date(); | |
console.log(minutes); | |
if (minutes) { | |
exptime.setMinutes(exptime.getMinutes() + time); | |
} else { | |
exptime.setHours(exptime.getHours() + time); | |
} | |
return exptime; | |
} | |
async function checkExpired() { | |
const checkAuth = localStorage.getItem("check_auth"); | |
const checkAuthTime = JSON.parse(checkAuth); | |
if ( checkAuth === null ) { | |
signOut(); | |
return; | |
} | |
if ( checkAuth !== null && new Date(checkAuthTime) < new Date()) { | |
console.log('session up'); | |
signOut(); | |
return; | |
} | |
} | |
async function checkUser(dispatch) { | |
console.log('check'); | |
console.log(localStorage.getItem("check_auth")); | |
try { | |
await checkExpired(); | |
const data = await Auth.currentAuthenticatedUser(); | |
dispatch({ type: "SET_USER", user: data }); | |
} catch (err) { | |
localStorage.removeItem("id_token"); | |
localStorage.removeItem("check_auth"); | |
} | |
} | |
async function signinUser(data, dispatch) { | |
try { | |
const record = await API.graphql(graphqlOperation(getUser, { owner: data.username })); | |
let started = true; | |
if (!record.data.getUser || record.data.getUser === null) { | |
const currentUser = await Auth.currentAuthenticatedUser(); | |
await API.graphql(graphqlOperation(createUser, { input: { | |
email: currentUser.attributes.email, | |
image: userImage, | |
status: "IN_PROGRESS" | |
} })); | |
started = false; | |
} | |
if (record.data.getUser.status === "IN_PROGRESS") { | |
started = false; | |
} | |
dispatch({ type: "SET_USER", user: data, started: started }); | |
} catch (err) { | |
// REDIRECT USER TO DASHBOARD | |
console.log('err', err); | |
setImmediate(() => dispatch({ type: "SET_USER", user: data })); | |
} | |
} | |
async function signUp(email, password, first, last, setConfirm, setIsLoading, setError) { | |
setError(false); | |
setIsLoading(true); | |
setConfirm(false); | |
try { | |
await Auth.signUp({ | |
username: email, | |
password, | |
attributes: { | |
'given_name': first, | |
'family_name': last, | |
'picture': userImage | |
} | |
}); | |
console.log('sign up success!') | |
setError(null); | |
setIsLoading(false); | |
setConfirm(true); | |
} catch (err) { | |
console.log('error signing up...', err); | |
setError(err.message); | |
setIsLoading(false); | |
setConfirm(false); | |
} | |
} | |
async function confirmSignUp(email, password, code, dispatch, setIsLoading, setError) { | |
setError(false); | |
setIsLoading(true); | |
try { | |
await Auth.confirmSignUp(email, code); | |
await Auth.signIn(email, password); | |
setError(null); | |
setIsLoading(false); | |
} catch (err) { | |
setError(err.message); | |
setIsLoading(false); | |
} | |
} | |
async function loginUser(login, password, setStatus) { | |
setStatus({ type: "LOADING" }); | |
try { | |
await Auth.signIn(login, password); | |
} catch (err) { | |
console.log(err); | |
setStatus({ type: "ERROR", err: err.message }); | |
} | |
} | |
async function signOut() { | |
try { | |
await Auth.signOut(); | |
localStorage.removeItem("id_token"); | |
localStorage.removeItem("check_auth"); | |
} catch (err) { | |
console.log('signout error', err); | |
} | |
} | |
export { | |
setExpTime, | |
reducer, | |
checkUser, | |
signinUser, | |
signUp, | |
confirmSignUp, | |
loginUser, | |
signOut, | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useState, useReducer } from "react"; | |
import { | |
CircularProgress, | |
Typography, | |
Button, | |
TextField, | |
Fade, | |
} from "@material-ui/core"; | |
// styles | |
import useStyles from "../styles"; | |
// logo | |
import google from "../../../images/google.svg"; | |
import facebook from "../../../images/facebook.svg"; | |
// auth | |
import { Auth } from "aws-amplify"; | |
// context | |
import { loginUser, reducer } from "../../../context/user/Authenticated"; | |
const LoginTab = (props) => { | |
const classes = useStyles(); | |
const [status, setStatus] = useReducer(reducer, { | |
error: false, | |
loading: false, | |
}); | |
const [passwordValue, setPasswordValue] = useState(""); | |
const [loginValue, setLoginValue] = useState(""); | |
return ( | |
<> | |
<Button | |
size="large" | |
className={classes.googleButton} | |
onClick={() => Auth.federatedSignIn({provider: 'Google'})}> | |
<img src={google} alt="google" className={classes.googleIcon} /> | |
Sign in with Google | |
</Button> | |
<Button | |
size="large" | |
className={classes.facebookButton} | |
onClick={() => Auth.federatedSignIn({provider: 'Facebook'})} | |
> | |
<img src={facebook} alt="facebook" className={classes.facebookIcon} /> | |
Sign in with Facebook | |
</Button> | |
<div className={classes.formDividerContainer}> | |
<div className={classes.formDivider} /> | |
<Typography className={classes.formDividerWord}>or</Typography> | |
<div className={classes.formDivider} /> | |
</div> | |
<Fade in={status.error ? true : false}> | |
<Typography color="secondary" className={classes.errorMessage}> | |
{status.error && | |
<>{status.error}</> | |
} | |
</Typography> | |
</Fade> | |
<TextField | |
id="email" | |
InputProps={{ | |
classes: { | |
underline: classes.textFieldUnderline, | |
input: classes.textField, | |
}, | |
}} | |
value={loginValue} | |
onChange={e => setLoginValue(e.target.value)} | |
margin="normal" | |
placeholder="Email Adress" | |
type="email" | |
fullWidth | |
/> | |
<TextField | |
id="password" | |
InputProps={{ | |
classes: { | |
underline: classes.textFieldUnderline, | |
input: classes.textField, | |
}, | |
}} | |
value={passwordValue} | |
onChange={e => setPasswordValue(e.target.value)} | |
margin="normal" | |
placeholder="Password" | |
type="password" | |
fullWidth | |
/> | |
<div className={classes.formButtons}> | |
{status.loading ? ( | |
<CircularProgress size={26} className={classes.loginLoader} /> | |
) : ( | |
<Button | |
disabled={ | |
loginValue.length === 0 || passwordValue.length === 0 | |
} | |
onClick={() => | |
loginUser( | |
loginValue, | |
passwordValue, | |
setStatus | |
) | |
} | |
variant="contained" | |
color="primary" | |
size="large" | |
> | |
Login | |
</Button> | |
)} | |
<Button | |
color="primary" | |
size="large" | |
className={classes.forgetButton} | |
> | |
Forget Password | |
</Button> | |
</div> | |
</> | |
) | |
} | |
export default LoginTab; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from "react"; | |
// Auth | |
import { checkUser, signOut } from "./user/Authenticated"; | |
const UserStateContext = React.createContext(); | |
const UserDispatchContext = React.createContext(); | |
const initialState = { | |
user: null, | |
isAuthenticated: false, | |
expired: null, | |
started: false, | |
}; | |
function userReducer(state, action) { | |
switch (action.type) { | |
case "SET_USER": | |
return { ...state, user: action.user, isAuthenticated: true, started: action.started }; | |
case "SET_STARTED": | |
return { ...state, started: true } | |
default: | |
return state; | |
} | |
} | |
function UserProvider({ children }) { | |
const [state, dispatch] = React.useReducer(userReducer, { | |
user: null, | |
isAuthenticated: !!localStorage.getItem("id_token"), | |
expired: localStorage.getItem("check_auth"), | |
}); | |
React.useEffect(() => { | |
checkUser(dispatch); | |
return function cleanUp() { | |
dispatch(); | |
} | |
}, []); | |
return ( | |
<UserStateContext.Provider value={state}> | |
<UserDispatchContext.Provider value={dispatch}> | |
{children} | |
</UserDispatchContext.Provider> | |
</UserStateContext.Provider> | |
); | |
} | |
function useUserState() { | |
const context = React.useContext(UserStateContext); | |
if (context === undefined) { | |
throw new Error("useUserState must be used within a UserProvider"); | |
} | |
return context; | |
} | |
function useUserDispatch() { | |
const context = React.useContext(UserDispatchContext); | |
if (context === undefined) { | |
throw new Error("useUserDispatch must be used within a UserProvider"); | |
} | |
return context; | |
} | |
export { | |
userReducer, | |
UserProvider, | |
useUserState, | |
useUserDispatch, | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment