Browse Source

+ user profile, fix snack render bug, add private routes

master
david 2 years ago
parent
commit
0c0141c5b7
  1. 58
      package-lock.json
  2. 1
      package.json
  3. 32
      src/App.js
  4. 44
      src/Components/Appbar.js
  5. 78
      src/Components/AuthForm.js
  6. 0
      src/Components/Home.js
  7. 7
      src/Components/PrivateRoute.js
  8. 53
      src/Components/UserProfile.js
  9. 10
      src/Contexts/AuthContext.js
  10. 5
      src/Contexts/SnackbarContext.js
  11. 19
      src/index.js

58
package-lock.json generated

@ -19,6 +19,7 @@
"@testing-library/user-event": "^13.5.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router-dom": "^6.2.1",
"react-scripts": "5.0.0",
"web-vitals": "^2.1.3"
}
@ -8523,6 +8524,14 @@
"he": "bin/he"
}
},
"node_modules/history": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/history/-/history-5.2.0.tgz",
"integrity": "sha512-uPSF6lAJb3nSePJ43hN3eKj1dTWpN9gMod0ZssbFTIsen+WehTmEadgL+kg78xLJFdRfrrC//SavDzmRVdE+Ig==",
"dependencies": {
"@babel/runtime": "^7.7.6"
}
},
"node_modules/hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
@ -13726,6 +13735,30 @@
"node": ">=0.10.0"
}
},
"node_modules/react-router": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.2.1.tgz",
"integrity": "sha512-2fG0udBtxou9lXtK97eJeET2ki5//UWfQSl1rlJ7quwe6jrktK9FCCc8dQb5QY6jAv3jua8bBQRhhDOM/kVRsg==",
"dependencies": {
"history": "^5.2.0"
},
"peerDependencies": {
"react": ">=16.8"
}
},
"node_modules/react-router-dom": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.2.1.tgz",
"integrity": "sha512-I6Zax+/TH/cZMDpj3/4Fl2eaNdcvoxxHoH1tYOREsQ22OKDYofGebrNm6CTPUcvLvZm63NL/vzCYdjf9CUhqmA==",
"dependencies": {
"history": "^5.2.0",
"react-router": "6.2.1"
},
"peerDependencies": {
"react": ">=16.8",
"react-dom": ">=16.8"
}
},
"node_modules/react-scripts": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.0.tgz",
@ -22735,6 +22768,14 @@
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
},
"history": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/history/-/history-5.2.0.tgz",
"integrity": "sha512-uPSF6lAJb3nSePJ43hN3eKj1dTWpN9gMod0ZssbFTIsen+WehTmEadgL+kg78xLJFdRfrrC//SavDzmRVdE+Ig==",
"requires": {
"@babel/runtime": "^7.7.6"
}
},
"hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
@ -26381,6 +26422,23 @@
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz",
"integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A=="
},
"react-router": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.2.1.tgz",
"integrity": "sha512-2fG0udBtxou9lXtK97eJeET2ki5//UWfQSl1rlJ7quwe6jrktK9FCCc8dQb5QY6jAv3jua8bBQRhhDOM/kVRsg==",
"requires": {
"history": "^5.2.0"
}
},
"react-router-dom": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.2.1.tgz",
"integrity": "sha512-I6Zax+/TH/cZMDpj3/4Fl2eaNdcvoxxHoH1tYOREsQ22OKDYofGebrNm6CTPUcvLvZm63NL/vzCYdjf9CUhqmA==",
"requires": {
"history": "^5.2.0",
"react-router": "6.2.1"
}
},
"react-scripts": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.0.tgz",

1
package.json

@ -14,6 +14,7 @@
"@testing-library/user-event": "^13.5.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router-dom": "^6.2.1",
"react-scripts": "5.0.0",
"web-vitals": "^2.1.3"
},

32
src/App.js

@ -1,22 +1,24 @@
import { Container, Button } from '@mui/material';
import { Container } from '@mui/material';
import { AuthForm } from './Components/AuthForm';
import { useAuth } from './Contexts/AuthContext';
function App() {
const { user, signOut } = useAuth();
console.log(user);
const handleSignOut = (e) => {
e.preventDefault();
signOut();
};
import { Appbar } from './Components/Appbar';
import { Routes, Route } from 'react-router-dom';
import { PrivateRoute } from './Components/PrivateRoute';
function App() {
return (
<>
<Container maxWidth="xs">
{!user ? (
<AuthForm />
) : (
<Button onClick={(e) => handleSignOut(e)}>wewlad signout</Button>
)}
<Container maxWidth="xs" height="100vh">
<Routes>
<Route
path="/"
element={
<PrivateRoute>
<Appbar />
</PrivateRoute>
}
/>
<Route path="/auth" element={<AuthForm />} />
</Routes>
</Container>
</>
);

44
src/Components/Appbar.js

@ -0,0 +1,44 @@
import { useState } from 'react';
import { Box, AppBar, Typography, Toolbar, IconButton } from '@mui/material';
import AccountCircle from '@mui/icons-material/AccountCircle';
import { UserProfile } from './UserProfile';
export const Appbar = () => {
const [showDrawer, setShowDrawer] = useState(false);
const toggleDrawer = () => (event) => {
if (
event.type === 'keydown' &&
(event.key === 'Tab' || event.key === 'Shift')
) {
return;
}
setShowDrawer((showDrawer) => !showDrawer);
};
return (
<Box>
<AppBar>
<Toolbar>
<Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
supachat
</Typography>
<div>
<IconButton
size="large"
aria-label="account of current user"
aria-controls="menu-appbar"
aria-haspopup="true"
onClick={toggleDrawer()}
color="inherit"
>
<AccountCircle />
</IconButton>
<UserProfile toggleDrawer={toggleDrawer} showDrawer={showDrawer} />
</div>
</Toolbar>
</AppBar>
</Box>
);
};

78
src/Components/AuthForm.js

@ -1,4 +1,4 @@
import { useState } from 'react';
import { useState, useEffect } from 'react';
import {
Box,
Tabs,
@ -12,6 +12,7 @@ import {
import LoadingButton from '@mui/lab/LoadingButton';
import { useSnackbar } from '../Contexts/SnackbarContext';
import { useAuth } from '../Contexts/AuthContext';
import { useNavigate } from 'react-router-dom';
export function AuthForm({ setSession }) {
const [tabindex, setTabIndex] = useState(0);
@ -78,46 +79,69 @@ export function AuthForm({ setSession }) {
<AuthInputs tabIndex={0} setSession={setSession} />
</TabPanel>
<TabPanel value={tabindex} index={1}>
<AuthInputs tabIndex={1} />
<AuthInputs tabIndex={1} setTabIndex={setTabIndex} />
</TabPanel>
</Box>
</Box>
);
}
const AuthInputs = ({ tabIndex }) => {
const AuthInputs = ({ tabIndex, setTabIndex }) => {
const initial = { email: '', password: '', confirmPassword: '' };
const [credentials, setCredentials] = useState(initial);
const [loading, setLoading] = useState(false);
const { showSnackBar } = useSnackbar();
const { signIn, signUp } = useAuth();
const { signIn, signUp, user } = useAuth();
const navigate = useNavigate();
const handleValueChanged = ({ target: { name, value } }) => {
setCredentials({ ...credentials, [name]: value });
};
const handleSubmit = async (e) => {
e.preventDefault();
if (credentials.email === '' || credentials.password === '')
return showSnackBar('error', 'missing inputs');
if (tabIndex === 1 && credentials.password !== credentials.confirmPassword)
return showSnackBar('error', `passwords don't match`);
useEffect(() => {
if (user) navigate('/');
}, [user, navigate]);
const { user, error } =
tabIndex === 0
? await signIn(credentials.email, credentials.password)
: await signUp(credentials.email, credentials.password);
if (error) return showSnackBar('error', error.message);
if (user) {
tabIndex === 0
? showSnackBar('success', `logged in!`)
: showSnackBar(
'info',
`verification email has been sent to ${credentials.email} pls check this before signing in`
);
const handleSubmit = async (e) => {
try {
e.preventDefault();
setLoading(true);
if (credentials.email === '' || credentials.password === '') {
setLoading(false);
return showSnackBar('error', 'missing inputs');
}
if (
tabIndex === 1 &&
credentials.password !== credentials.confirmPassword
) {
setLoading(false);
return showSnackBar('error', `passwords don't match`);
}
const { user, error } =
tabIndex === 0
? await signIn(credentials.email, credentials.password)
: await signUp(credentials.email, credentials.password);
if (error) {
setLoading(false);
return showSnackBar('error', error.message);
}
if (user) {
tabIndex === 0
? showSnackBar('success', `logged in!`)
: showSnackBar(
'info',
`verification email sent to ${credentials.email}`
);
}
tabIndex === 1 && setTabIndex(0);
navigate('/');
} catch (error) {
console.log(error);
}
};
@ -165,7 +189,7 @@ const AuthInputs = ({ tabIndex }) => {
<LoadingButton
fullWidth
variant="outlined"
loading={false}
loading={loading}
onClick={(e) => handleSubmit(e)}
type="submit"
>

0
src/Components/Home.js

7
src/Components/PrivateRoute.js

@ -0,0 +1,7 @@
import { Navigate } from 'react-router-dom';
import { useAuth } from '../Contexts/AuthContext';
export const PrivateRoute = ({ children }) => {
const { user } = useAuth();
return user ? children : <Navigate to="/auth" replace />;
};

53
src/Components/UserProfile.js

@ -0,0 +1,53 @@
import {
Drawer,
Button,
IconButton,
Toolbar,
Box,
TextField,
Divider,
Stack,
} from '@mui/material';
import { useAuth } from '../Contexts/AuthContext';
import { useSnackbar } from '../Contexts/SnackbarContext';
import CloseIcon from '@mui/icons-material/Close';
export const UserProfile = ({ showDrawer, toggleDrawer }) => {
const { user, signOut } = useAuth();
const { showSnackBar } = useSnackbar();
const handleSignOut = (e) => {
e.preventDefault();
signOut();
showSnackBar('info', 'user logged out');
};
return (
<Drawer
PaperProps={{
sx: {
// width: 1
width: 350,
},
}}
anchor="right"
open={showDrawer}
onClose={toggleDrawer()}
>
<Toolbar sx={{ flexDirection: 'row' }}>
<Box sx={{ flexGrow: 1 }}>user profile</Box>
<IconButton onClick={toggleDrawer()}>
<CloseIcon />
</IconButton>
</Toolbar>
<Divider />
<Stack m={2} spacing={1}>
<TextField fullWidth label="email" value={user.email} />
<Button fullWidth onClick={(e) => handleSignOut(e)}>
signout
</Button>
</Stack>
</Drawer>
);
};

10
src/Contexts/AuthContext.js

@ -5,16 +5,18 @@ const AuthContext = createContext();
const AuthProvider = ({ children }) => {
const [user, setUser] = useState();
const [authLoading, setAuthLoading] = useState(true);
useEffect(() => {
const session = supabase.auth.session();
setUser(session?.user ?? null);
const { data: listener } = supabase.auth.onAuthStateChange(
async (event, session) => {
(event, session) => {
setUser(session?.user ?? null);
}
);
setAuthLoading(false);
return () => {
listener?.unsubscribe();
@ -52,7 +54,11 @@ const AuthProvider = ({ children }) => {
const value = { signUp, signIn, signOut, user };
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
return (
<AuthContext.Provider value={value}>
{!authLoading && children}
</AuthContext.Provider>
);
};
const useAuth = () => {

5
src/Contexts/SnackbarContext.js

@ -18,7 +18,10 @@ const SnackbarProvider = ({ children }) => {
const handleSnackClose = (event, reason) => {
if (reason === 'clickaway') return;
setSnack(initialSnack);
setSnack({
...snack,
show: false,
});
};
return (

19
src/index.js

@ -5,6 +5,7 @@ import { createTheme, ThemeProvider } from '@mui/material/styles';
import { CssBaseline } from '@mui/material';
import { SnackbarProvider } from './Contexts/SnackbarContext';
import { AuthProvider } from './Contexts/AuthContext';
import { BrowserRouter } from 'react-router-dom';
const theme = createTheme({
palette: {
@ -14,14 +15,16 @@ const theme = createTheme({
ReactDOM.render(
<React.StrictMode>
<ThemeProvider theme={theme}>
<AuthProvider>
<SnackbarProvider>
<CssBaseline />
<App />
</SnackbarProvider>
</AuthProvider>
</ThemeProvider>
<BrowserRouter>
<ThemeProvider theme={theme}>
<CssBaseline />
<AuthProvider>
<SnackbarProvider>
<App />
</SnackbarProvider>
</AuthProvider>
</ThemeProvider>
</BrowserRouter>
</React.StrictMode>,
document.getElementById('root')
);

Loading…
Cancel
Save