Skip to content

Instantly share code, notes, and snippets.

@jarkin13
Last active March 6, 2020 23:41
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jarkin13/a95233b8606777bdc1c547853bdc72cd to your computer and use it in GitHub Desktop.
Save jarkin13/a95233b8606777bdc1c547853bdc72cd to your computer and use it in GitHub Desktop.
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;
// 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,
}
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} />
&nbsp;Sign in with Google
</Button>
<Button
size="large"
className={classes.facebookButton}
onClick={() => Auth.federatedSignIn({provider: 'Facebook'})}
>
<img src={facebook} alt="facebook" className={classes.facebookIcon} />
&nbsp;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;
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