improved auth methods
This commit is contained in:
@@ -1,12 +1,11 @@
|
|||||||
import {Fragment, useState} from 'react'
|
import {Fragment, useState} from 'react'
|
||||||
import {Dialog, DialogTitle, TextField, Button, CircularProgress} from '@mui/material'
|
import {Dialog, DialogTitle, TextField, Button, CircularProgress} from '@mui/material'
|
||||||
import axios from '../api'
|
|
||||||
import {useAuth} from '../contexts/AuthContext'
|
import {useAuth} from '../contexts/AuthContext'
|
||||||
|
|
||||||
const textFieldSx = {mx: 2, my: 0.5}
|
const textFieldSx = {mx: 2, my: 0.5}
|
||||||
|
|
||||||
export default function AuthModal({open, close, register, toggleRegister}) {
|
export default function AuthModal({open, close, isRegisterMode, toggleRegister}) {
|
||||||
const {setIsLoggedIn, setToken, setAccount} = useAuth()
|
const {login, register} = useAuth()
|
||||||
|
|
||||||
const [formData, setFormData] = useState({})
|
const [formData, setFormData] = useState({})
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
@@ -22,16 +21,10 @@ export default function AuthModal({open, close, register, toggleRegister}) {
|
|||||||
setError('')
|
setError('')
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const requestPath = register ? '/auth/register' : '/auth/login'
|
isRegisterMode ? await register(formData) : await login(formData)
|
||||||
const response = await axios.post(requestPath, formData)
|
|
||||||
|
|
||||||
setToken(response.data.token)
|
|
||||||
setAccount(response.data.data)
|
|
||||||
setIsLoggedIn(true)
|
|
||||||
close()
|
close()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
setError(error)
|
||||||
setError(error?.response?.data?.message ?? error.message)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
@@ -42,7 +35,7 @@ export default function AuthModal({open, close, register, toggleRegister}) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onClose={close}>
|
<Dialog open={open} onClose={close}>
|
||||||
{register ? (
|
{isRegisterMode ? (
|
||||||
<RegisterForm formData={formData} handleChange={handleChange} />
|
<RegisterForm formData={formData} handleChange={handleChange} />
|
||||||
) : (
|
) : (
|
||||||
<LoginForm formData={formData} handleChange={handleChange} />
|
<LoginForm formData={formData} handleChange={handleChange} />
|
||||||
@@ -57,13 +50,13 @@ export default function AuthModal({open, close, register, toggleRegister}) {
|
|||||||
) : (
|
) : (
|
||||||
<Button
|
<Button
|
||||||
onClick={clickSubmit}
|
onClick={clickSubmit}
|
||||||
disabled={register ? disabledRegisterButton : disabledLoginButton}>
|
disabled={isRegisterMode ? disabledRegisterButton : disabledLoginButton}>
|
||||||
{register ? 'Register' : 'Login'}
|
{isRegisterMode ? 'Register' : 'Login'}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Button onClick={toggleRegister}>
|
<Button onClick={toggleRegister}>
|
||||||
{register ? 'I already have an account' : "I don't have an account"}
|
{isRegisterMode ? 'I already have an account' : "I don't have an account"}
|
||||||
</Button>
|
</Button>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
)
|
)
|
||||||
@@ -78,7 +71,7 @@ function LoginForm({formData, handleChange}) {
|
|||||||
label='Username'
|
label='Username'
|
||||||
name='username'
|
name='username'
|
||||||
type='text'
|
type='text'
|
||||||
value={formData['username'] ?? ''}
|
value={formData['username'] || ''}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
variant='filled'
|
variant='filled'
|
||||||
sx={textFieldSx}
|
sx={textFieldSx}
|
||||||
@@ -88,7 +81,7 @@ function LoginForm({formData, handleChange}) {
|
|||||||
label='Password'
|
label='Password'
|
||||||
name='password'
|
name='password'
|
||||||
type='password'
|
type='password'
|
||||||
value={formData['password'] ?? ''}
|
value={formData['password'] || ''}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
variant='filled'
|
variant='filled'
|
||||||
sx={textFieldSx}
|
sx={textFieldSx}
|
||||||
@@ -107,7 +100,7 @@ function RegisterForm({formData, handleChange}) {
|
|||||||
label='Username'
|
label='Username'
|
||||||
name='username'
|
name='username'
|
||||||
type='text'
|
type='text'
|
||||||
value={formData['username'] ?? ''}
|
value={formData['username'] || ''}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
variant='filled'
|
variant='filled'
|
||||||
sx={textFieldSx}
|
sx={textFieldSx}
|
||||||
@@ -117,7 +110,7 @@ function RegisterForm({formData, handleChange}) {
|
|||||||
label='Password'
|
label='Password'
|
||||||
name='password'
|
name='password'
|
||||||
type='password'
|
type='password'
|
||||||
value={formData['password'] ?? ''}
|
value={formData['password'] || ''}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
variant='filled'
|
variant='filled'
|
||||||
sx={textFieldSx}
|
sx={textFieldSx}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export default function Header() {
|
|||||||
|
|
||||||
<IconButton onClick={openPopover}>
|
<IconButton onClick={openPopover}>
|
||||||
<OnlineIndicator online={isLoggedIn}>
|
<OnlineIndicator online={isLoggedIn}>
|
||||||
<Avatar src={account?.username ?? ''} alt={account?.username ?? ''} />
|
<Avatar src={account?.username || ''} alt={account?.username || ''} />
|
||||||
</OnlineIndicator>
|
</OnlineIndicator>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ export default function Header() {
|
|||||||
<AuthModal
|
<AuthModal
|
||||||
open={authModal}
|
open={authModal}
|
||||||
close={() => setAuthModal(false)}
|
close={() => setAuthModal(false)}
|
||||||
register={register}
|
isRegisterMode={register}
|
||||||
toggleRegister={() => setRegister((prev) => !prev)}
|
toggleRegister={() => setRegister((prev) => !prev)}
|
||||||
/>
|
/>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
@@ -11,14 +11,71 @@ export function useAuth() {
|
|||||||
|
|
||||||
// export the provider (handle all the logic here)
|
// export the provider (handle all the logic here)
|
||||||
export function AuthProvider({children}) {
|
export function AuthProvider({children}) {
|
||||||
const [token, setToken] = useState(localStorage.getItem('token') ?? null)
|
|
||||||
const [account, setAccount] = useState(null)
|
|
||||||
const [isLoggedIn, setIsLoggedIn] = useState(false)
|
const [isLoggedIn, setIsLoggedIn] = useState(false)
|
||||||
|
const [account, setAccount] = useState(null)
|
||||||
|
const [token, setToken] = useState(localStorage.getItem('token') || null)
|
||||||
|
|
||||||
|
const register = (formData = {}) =>
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
axios
|
||||||
|
.post('/auth/register', formData)
|
||||||
|
.then(({data: {data, token}}) => {
|
||||||
|
setAccount(data)
|
||||||
|
setToken(token)
|
||||||
|
setIsLoggedIn(true)
|
||||||
|
resolve(true)
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(error)
|
||||||
|
reject(error?.response?.data?.message || error.message)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const login = (formData = {}) =>
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
axios
|
||||||
|
.post('/auth/login', formData)
|
||||||
|
.then(({data: {data, token}}) => {
|
||||||
|
setAccount(data)
|
||||||
|
setToken(token)
|
||||||
|
setIsLoggedIn(true)
|
||||||
|
resolve(true)
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(error)
|
||||||
|
reject(error?.response?.data?.message || error.message)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
const logout = () => {
|
const logout = () => {
|
||||||
setToken(null)
|
|
||||||
setAccount(null)
|
|
||||||
setIsLoggedIn(false)
|
setIsLoggedIn(false)
|
||||||
|
setAccount(null)
|
||||||
|
setToken(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAccount = async () => {
|
||||||
|
try {
|
||||||
|
const headers = {headers: {authorization: `Bearer ${token}`}}
|
||||||
|
const response = await axios.get('/auth/account', headers)
|
||||||
|
|
||||||
|
setAccount(response.data.data)
|
||||||
|
setIsLoggedIn(true)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
if (error?.response?.statusCode === 401) setToken(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getTokenPayload = () => {
|
||||||
|
if (!token) {
|
||||||
|
console.warn(`Token is ${null}/${undefined}`)
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const informativePart = token.split('.')[1]
|
||||||
|
const payload = JSON.parse(window.atob(informativePart))
|
||||||
|
|
||||||
|
return payload
|
||||||
}
|
}
|
||||||
|
|
||||||
// This side effect keeps local storage updated with recent token value,
|
// This side effect keeps local storage updated with recent token value,
|
||||||
@@ -32,28 +89,23 @@ export function AuthProvider({children}) {
|
|||||||
}, [token])
|
}, [token])
|
||||||
|
|
||||||
// This side effect runs only if we have a token, but no account or logged-in boolean.
|
// This side effect runs only if we have a token, but no account or logged-in boolean.
|
||||||
// This "if" statement applies only when refreshed, or re-opened the browser,
|
// This "if" statement is "true" only when refreshed, or re-opened the browser,
|
||||||
// if true, it will then ask the backend for the account information (and will get them if the token hasn't expired)
|
// if true, it will then ask the backend for the account information (and will get them if the token hasn't expired)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isLoggedIn && !account && token) {
|
if (!isLoggedIn && !account && token) getAccount()
|
||||||
;(async () => {
|
|
||||||
try {
|
|
||||||
const headers = {headers: {authorization: `Bearer ${token}`}}
|
|
||||||
const response = await axios.get('/auth/account', headers)
|
|
||||||
|
|
||||||
setAccount(response.data.data)
|
|
||||||
setIsLoggedIn(true)
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error)
|
|
||||||
if (error?.response?.statusCode === 401) setToken(null)
|
|
||||||
}
|
|
||||||
})()
|
|
||||||
}
|
|
||||||
}, [isLoggedIn, account, token]) // eslint-disable-line react-hooks/exhaustive-deps
|
}, [isLoggedIn, account, token]) // eslint-disable-line react-hooks/exhaustive-deps
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AuthContext.Provider
|
<AuthContext.Provider
|
||||||
value={{isLoggedIn, setIsLoggedIn, token, setToken, account, setAccount, logout}}>
|
value={{
|
||||||
|
isLoggedIn,
|
||||||
|
account,
|
||||||
|
token,
|
||||||
|
register,
|
||||||
|
login,
|
||||||
|
logout,
|
||||||
|
getTokenPayload,
|
||||||
|
}}>
|
||||||
{children}
|
{children}
|
||||||
</AuthContext.Provider>
|
</AuthContext.Provider>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
:root {
|
:root {
|
||||||
--online: #44b700;
|
--online: #44b700;
|
||||||
--offline: rgb(183, 68, 0);
|
--offline: #b74400;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
|
|||||||
Reference in New Issue
Block a user