diff options
author | eug-vs <eug-vs@keemail.me> | 2020-06-29 23:59:15 +0300 |
---|---|---|
committer | eug-vs <eug-vs@keemail.me> | 2020-06-29 23:59:15 +0300 |
commit | 1f646377c35b65b97d6eeebb1e88f6d8307e1ef0 (patch) | |
tree | 56fbda6b594350656891644734d0bf740bfcbb0d /src | |
parent | b301bf24c5037403a1e5fc32fc8c10794941b528 (diff) | |
download | which-ui-1f646377c35b65b97d6eeebb1e88f6d8307e1ef0.tar.gz |
feat!: create useAuth hook
Diffstat (limited to 'src')
-rw-r--r-- | src/hooks/useAuth.tsx | 75 | ||||
-rw-r--r-- | src/index.tsx | 45 | ||||
-rw-r--r-- | src/pages/AuthPage/AuthPage.tsx | 11 | ||||
-rw-r--r-- | src/pages/AuthPage/SignInForm.tsx | 10 | ||||
-rw-r--r-- | src/pages/AuthPage/SignUpForm.tsx | 9 | ||||
-rw-r--r-- | src/pages/FeedPage/FeedPage.tsx | 7 | ||||
-rw-r--r-- | src/pages/FeedPage/PollSubmission.tsx | 9 | ||||
-rw-r--r-- | src/pages/ProfilePage/MoreMenu.tsx | 11 | ||||
-rw-r--r-- | src/pages/ProfilePage/ProfileInfo.tsx | 24 | ||||
-rw-r--r-- | src/pages/ProfilePage/ProfilePage.tsx | 10 |
10 files changed, 118 insertions, 93 deletions
diff --git a/src/hooks/useAuth.tsx b/src/hooks/useAuth.tsx new file mode 100644 index 0000000..4f76593 --- /dev/null +++ b/src/hooks/useAuth.tsx @@ -0,0 +1,75 @@ +import React, { useState, useEffect, useContext, createContext } from 'react'; +import { User } from 'which-types'; +import { post, get } from '../requests'; + + +interface ContextType { + user: User | null, + setUser: (user: User) => void; + login: (username: string, password: string, remember?: boolean) => Promise<boolean>; + logout: () => void; + isAuthenticated: () => boolean; +} + +const authContext = createContext<ContextType>({ + user: null, + setUser: () => {}, + login: async () => false, + logout: () => {}, + isAuthenticated: () => false +}); + +const useProvideAuth = () => { + const [user, setUser] = useState<User | null>(null); + + const login = (username: string, password: string, remember = true): Promise<boolean> => { + return post('/authentication', { + strategy: 'local', + username, + password + }).then(response => { + const me = response.data.user; + const token = response.data.accessToken; + setUser(me); + // navigate('profile', me._id); + localStorage.setItem('userId', me._id); + localStorage.setItem('token', token); + if (!remember) localStorage.setItem('shouldClear', 'true'); + return true; + }).catch(() => false); + }; + + const logout = () => { + setUser(null); + localStorage.removeItem('userId'); + localStorage.removeItem('token'); + // navigate('auth'); + }; + + const isAuthenticated = () => Boolean(user); + + useEffect(() => { + if (localStorage.getItem('shouldClear')) { + localStorage.clear(); + } + const userId = localStorage.getItem('userId'); + if (userId) { + get(`/users/${userId}`).then(response => { + setUser(response.data); + }); + } + }, []); + + return { user, setUser, login, logout, isAuthenticated }; +}; + +export const AuthProvider: React.FC = ({ children }) => { + const auth = useProvideAuth(); + const { Provider } = authContext; + return <Provider value={auth}>{children}</Provider> +}; + +export const useAuth = () => { + return useContext(authContext); +} + diff --git a/src/index.tsx b/src/index.tsx index 02f7969..f9114c3 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -17,6 +17,7 @@ import AuthPage from './pages/AuthPage/AuthPage'; import { Page } from './types'; import { get, post } from './requests'; import ScrollTopArrow from './components/ScrollTopArrow/ScrollTopArrow'; +import { useAuth, AuthProvider } from './hooks/useAuth'; const theme = createMuiTheme({ @@ -37,9 +38,9 @@ const useStyles = makeStyles({ }); const App: React.FC = () => { - const [user, setUser] = React.useState<User | undefined>(); const [page, setPage] = useState<Page>({ prefix: 'feed', id: '' }); const classes = useStyles(); + const user = { _id: '', avatarUrl: '' }; const navigate = (prefix: string, id?: string): void => { if (prefix === 'profile' && !id && !user) { @@ -55,42 +56,6 @@ const App: React.FC = () => { } }; - const logIn = (username: string, password: string, remember = true): Promise<boolean> => { - return post('/authentication', { - strategy: 'local', - username, - password - }).then(response => { - const me = response.data.user; - const token = response.data.accessToken; - setUser(me); - navigate('profile', me._id); - localStorage.setItem('userId', me._id); - localStorage.setItem('token', token); - if (!remember) localStorage.setItem('shouldClear', 'true'); - return true; - }).catch(() => false); - }; - - const logOut = () => { - setUser(undefined); - localStorage.removeItem('userId'); - localStorage.removeItem('token'); - navigate('auth'); - }; - - useEffect(() => { - if (localStorage.getItem('shouldClear')) { - localStorage.clear(); - } - const userId = localStorage.getItem('userId'); - if (userId) { - get(`/users/${userId}`).then(response => { - setUser(response.data); - }); - } - }, []); - return ( <ThemeProvider theme={theme}> <CssBaseline /> @@ -98,14 +63,12 @@ const App: React.FC = () => { <div className={classes.root}> { page.prefix === 'profile' && ( <ProfilePage - logOut={logOut} id={page.id} navigate={navigate} - setUser={setUser} /> ) } - { page.prefix === 'feed' && <FeedPage navigate={navigate} user={user} /> } - { page.prefix === 'auth' && <AuthPage logIn={logIn} /> } + { page.prefix === 'feed' && <FeedPage navigate={navigate} /> } + { page.prefix === 'auth' && <AuthPage /> } </div> <ScrollTopArrow /> </ThemeProvider> diff --git a/src/pages/AuthPage/AuthPage.tsx b/src/pages/AuthPage/AuthPage.tsx index d2c2eec..ad93463 100644 --- a/src/pages/AuthPage/AuthPage.tsx +++ b/src/pages/AuthPage/AuthPage.tsx @@ -3,11 +3,6 @@ import { makeStyles } from '@material-ui/core/styles'; import SignInForm from './SignInForm'; import SignUpForm from './SignUpForm'; - -interface PropTypes { - logIn: (name: string, password: string, remember?: boolean) => Promise<boolean>; -} - const useStyles = makeStyles({ formTransfer: { display: 'flex', @@ -20,7 +15,7 @@ const useStyles = makeStyles({ } }); -const AuthPage: React.FC<PropTypes> = ({ logIn }) => { +const AuthPage: React.FC = () => { const [auth, setAuth] = useState<'signIn' | 'signUp'>('signIn'); const classes = useStyles(); @@ -35,8 +30,8 @@ const AuthPage: React.FC<PropTypes> = ({ logIn }) => { return ( <> - {auth === 'signIn' && <SignInForm logIn={logIn} />} - {auth === 'signUp' && <SignUpForm logIn={logIn} />} + {auth === 'signIn' && <SignInForm />} + {auth === 'signUp' && <SignUpForm />} <div className={classes.formTransfer}> <div>{footerInfo[auth][0]}</div> <span diff --git a/src/pages/AuthPage/SignInForm.tsx b/src/pages/AuthPage/SignInForm.tsx index 1dad153..662a312 100644 --- a/src/pages/AuthPage/SignInForm.tsx +++ b/src/pages/AuthPage/SignInForm.tsx @@ -6,10 +6,7 @@ import { FormControlLabel, Switch } from '@material-ui/core'; - -interface PropTypes { - logIn: (name: string, password: string, remember?: boolean) => Promise<boolean>; -} +import { useAuth } from '../../hooks/useAuth'; const useStyles = makeStyles(theme => ({ root: { @@ -28,12 +25,13 @@ const useStyles = makeStyles(theme => ({ } })); -const SignInForm: React.FC<PropTypes> = ({ logIn }) => { +const SignInForm: React.FC = () => { const [error, setError] = useState<boolean>(false); const [remember, setRemember] = useState<boolean>(true); const classes = useStyles(); const nameRef = useRef<HTMLInputElement>(); const passwordRef = useRef<HTMLInputElement>(); + const { login } = useAuth(); const handleCheck = () => { setRemember(!remember); @@ -43,7 +41,7 @@ const SignInForm: React.FC<PropTypes> = ({ logIn }) => { const name = nameRef.current?.value; const password = passwordRef.current?.value; if (name && password) { - logIn(name, password, remember).then(success => { + login(name, password, remember).then(success => { if (!success) setError(true); }); } diff --git a/src/pages/AuthPage/SignUpForm.tsx b/src/pages/AuthPage/SignUpForm.tsx index 25b79ff..af7a0f8 100644 --- a/src/pages/AuthPage/SignUpForm.tsx +++ b/src/pages/AuthPage/SignUpForm.tsx @@ -3,10 +3,8 @@ import { makeStyles } from '@material-ui/core/styles'; import TextField from '@material-ui/core/TextField'; import Button from '@material-ui/core/Button'; import { post } from '../../requests'; +import { useAuth } from '../../hooks/useAuth'; -interface PropTypes { - logIn: (name: string, password: string) => Promise<boolean>; -} const useStyles = makeStyles(theme => ({ root: { @@ -25,12 +23,13 @@ const useStyles = makeStyles(theme => ({ } })); -const SignUpForm: React.FC<PropTypes> = ({ logIn }) => { +const SignUpForm: React.FC = () => { const [error, setError] = useState<boolean>(false); const classes = useStyles(); const usernameRef = useRef<HTMLInputElement>(); const emailRef = useRef<HTMLInputElement>(); const passwordRef = useRef<HTMLInputElement>(); + const { login } = useAuth(); const onClick = () => { const username = usernameRef.current?.value; @@ -38,7 +37,7 @@ const SignUpForm: React.FC<PropTypes> = ({ logIn }) => { const email = emailRef.current?.value; if (username && password) { post('/users', { username, password, email }).then(() => { - logIn(username, password); + login(username, password); }); } else setError(true); }; diff --git a/src/pages/FeedPage/FeedPage.tsx b/src/pages/FeedPage/FeedPage.tsx index 0017275..6561991 100644 --- a/src/pages/FeedPage/FeedPage.tsx +++ b/src/pages/FeedPage/FeedPage.tsx @@ -4,15 +4,16 @@ import { Poll, User } from 'which-types'; import Feed from '../../components/Feed/Feed'; import { get } from '../../requests'; import PollSubmission from './PollSubmission'; +import { useAuth } from '../../hooks/useAuth'; interface PropTypes { navigate: (prefix: string, id: string) => void; - user: User | undefined; } -const FeedPage: React.FC<PropTypes> = ({ navigate, user }) => { +const FeedPage: React.FC<PropTypes> = ({ navigate }) => { const [polls, setPolls] = useState<Poll[]>([]); + const { isAuthenticated } = useAuth(); useEffect(() => { get('/feed').then(response => { @@ -28,7 +29,7 @@ const FeedPage: React.FC<PropTypes> = ({ navigate, user }) => { return ( <> - {user && <PollSubmission user={user} addPoll={addPoll} />} + {isAuthenticated() && <PollSubmission addPoll={addPoll} />} <Feed polls={polls} navigate={navigate} /> </> ); diff --git a/src/pages/FeedPage/PollSubmission.tsx b/src/pages/FeedPage/PollSubmission.tsx index 16c8350..4e06254 100644 --- a/src/pages/FeedPage/PollSubmission.tsx +++ b/src/pages/FeedPage/PollSubmission.tsx @@ -12,9 +12,9 @@ import PollSubmissionImage from './PollSubmissionImage'; import UserStrip from '../../components/UserStrip/UserStrip'; import { post } from '../../requests'; import { Contents } from './types'; +import { useAuth } from '../../hooks/useAuth'; interface PropTypes{ - user: User; addPoll: (poll: Poll) => void; } @@ -30,10 +30,11 @@ const emptyContents: Contents = { right: { url: '' } }; -const PollSubmission: React.FC<PropTypes> = ({ user, addPoll }) => { +const PollSubmission: React.FC<PropTypes> = ({ addPoll }) => { const classes = useStyles(); const [expanded, setExpanded] = useState(false); const [contents, setContents] = useState<Contents>(emptyContents); + const { user } = useAuth(); const readyToSubmit = contents.left.url && contents.right.url; @@ -47,7 +48,7 @@ const PollSubmission: React.FC<PropTypes> = ({ user, addPoll }) => { const handleClick = () => { if (expanded && readyToSubmit) { - post('/polls/', { authorId: user._id, contents }).then(response => { + post('/polls/', { contents }).then(response => { addPoll(response.data); }); setContents({ ...emptyContents }); @@ -59,7 +60,7 @@ const PollSubmission: React.FC<PropTypes> = ({ user, addPoll }) => { <ClickAwayListener onClickAway={handleClickAway}> <Card> <Collapse in={expanded} timeout="auto" unmountOnExit> - <UserStrip user={user} info="" navigate={() => {}} /> + {user && <UserStrip user={user} info="" navigate={() => {}} />} <Divider /> <div className={classes.root}> <PollSubmissionImage url={contents.left.url} setUrl={setUrl('left')} /> diff --git a/src/pages/ProfilePage/MoreMenu.tsx b/src/pages/ProfilePage/MoreMenu.tsx index bf3347b..7c17f1e 100644 --- a/src/pages/ProfilePage/MoreMenu.tsx +++ b/src/pages/ProfilePage/MoreMenu.tsx @@ -4,10 +4,7 @@ import Menu from '@material-ui/core/Menu'; import MenuItem from '@material-ui/core/MenuItem'; import MoreHorizIcon from '@material-ui/icons/MoreHoriz'; import { makeStyles } from '@material-ui/core'; - -interface PropTypes { - logOut: () => void; -} +import { useAuth } from '../../hooks/useAuth'; const ITEM_HEIGHT = 48; @@ -19,9 +16,11 @@ const useStyles = makeStyles({ } }); -const MoreMenu: React.FC<PropTypes> = ({ logOut }) => { +const MoreMenu: React.FC = () => { const classes = useStyles(); const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(null); + const { logout } = useAuth(); + const open = Boolean(anchorEl); const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => { @@ -56,7 +55,7 @@ const MoreMenu: React.FC<PropTypes> = ({ logOut }) => { } }} > - <MenuItem onClick={logOut}>Log out</MenuItem> + <MenuItem onClick={logout}>Log out</MenuItem> </Menu> </div> </div> diff --git a/src/pages/ProfilePage/ProfileInfo.tsx b/src/pages/ProfilePage/ProfileInfo.tsx index 9fe5912..9557a5e 100644 --- a/src/pages/ProfilePage/ProfileInfo.tsx +++ b/src/pages/ProfilePage/ProfileInfo.tsx @@ -8,15 +8,14 @@ import MoreMenu from './MoreMenu'; import Highlight from './Highlight'; import UploadImage from '../../components/UploadImage/UploadImage'; import { patch } from '../../requests'; +import { useAuth } from '../../hooks/useAuth'; interface PropTypes { - user: User | undefined; - logOut: () => void; savedPolls: number; totalVotes: number; - setUserInfo: (a: User) => void; - setUser: (a:User) => void; + userInfo: User | undefined; + setUserInfo: (userInfo: User) => void; } const useStyles = makeStyles(theme => ({ @@ -78,13 +77,14 @@ const useStyles = makeStyles(theme => ({ const ProfileInfo: React.FC<PropTypes> = ({ - user, logOut, savedPolls, totalVotes, setUserInfo, setUser + savedPolls, totalVotes, setUserInfo, userInfo }) => { const classes = useStyles(); const [input, setInput] = useState(false); + const { setUser } = useAuth(); - const dateSince = new Date(user?.createdAt || '').toLocaleDateString(); + const dateSince = new Date(userInfo?.createdAt || '').toLocaleDateString(); const handleClick = () => { setInput(!input); @@ -101,10 +101,10 @@ const ProfileInfo: React.FC<PropTypes> = ({ return ( <div className={classes.root}> { - user?._id === localStorage.getItem('userId') + userInfo?._id === localStorage.getItem('userId') ? ( <div> - <MoreMenu logOut={logOut} /> + <MoreMenu /> <div className={classes.avatarContainer}> <Badge overlap="circle" @@ -118,17 +118,17 @@ const ProfileInfo: React.FC<PropTypes> = ({ </div> )} > - <Avatar className={classes.avatar} src={user?.avatarUrl} /> + <Avatar className={classes.avatar} src={userInfo?.avatarUrl} /> </Badge> </div> <UploadImage isOpen={input} setIsOpen={setInput} callback={patchAvatar} /> </div> ) - : <Avatar className={classes.avatar} src={user?.avatarUrl} /> + : <Avatar className={classes.avatar} src={userInfo?.avatarUrl} /> } <Typography variant="h5" className={classes.name}> - {user?.username} - {user?.verified && <VerifiedIcon className={classes.verified} color="primary" />} + {userInfo?.username} + {userInfo?.verified && <VerifiedIcon className={classes.verified} color="primary" />} </Typography> <div className={classes.profileMenu}> <Highlight text="Polls" value={savedPolls} /> diff --git a/src/pages/ProfilePage/ProfilePage.tsx b/src/pages/ProfilePage/ProfilePage.tsx index b0ac103..ad2da46 100644 --- a/src/pages/ProfilePage/ProfilePage.tsx +++ b/src/pages/ProfilePage/ProfilePage.tsx @@ -6,15 +6,11 @@ import Feed from '../../components/Feed/Feed'; import { get } from '../../requests'; interface PropTypes { - logOut: () => void; navigate: (prefix: string, id: string) => void; id: string; - setUser:(a:User)=>void; } -const ProfilePage: React.FC<PropTypes> = ({ - logOut, id, navigate, setUser -}) => { +const ProfilePage: React.FC<PropTypes> = ({ id, navigate }) => { const [userInfo, setUserInfo] = useState<User>(); const [polls, setPolls] = useState<Poll[]>([]); const [totalVotes, setTotalVotes] = useState<number>(0); @@ -40,10 +36,8 @@ const ProfilePage: React.FC<PropTypes> = ({ return ( <> <ProfileInfo - user={userInfo} + userInfo={userInfo} setUserInfo={setUserInfo} - setUser={setUser} - logOut={logOut} savedPolls={polls.length} totalVotes={totalVotes} /> |