diff options
author | eug-vs <eug-vs@keemail.me> | 2020-08-10 00:02:24 +0300 |
---|---|---|
committer | eug-vs <eug-vs@keemail.me> | 2020-08-10 00:02:24 +0300 |
commit | fd6e663a1bcc43cfc49bda99ccbfab380489324b (patch) | |
tree | ea25ea2c81db0f64e10123dbadd73f361b904da2 | |
parent | 359ec6a68ea92b3d1eecf020742157eb3be90b9f (diff) | |
download | which-ui-fd6e663a1bcc43cfc49bda99ccbfab380489324b.tar.gz |
feat!: add useLocalStorage hook
-rw-r--r-- | src/components/Feed/Feed.tsx | 3 | ||||
-rw-r--r-- | src/components/PollCard/PollCard.tsx | 2 | ||||
-rw-r--r-- | src/hooks/APIClient.ts | 6 | ||||
-rw-r--r-- | src/hooks/useAuth.tsx | 65 | ||||
-rw-r--r-- | src/hooks/useLocalStorage.ts | 16 | ||||
-rw-r--r-- | src/pages/FeedPage/FeedPage.tsx | 2 | ||||
-rw-r--r-- | src/pages/HomePage/HomePage.tsx | 4 | ||||
-rw-r--r-- | src/pages/Page.tsx | 12 | ||||
-rw-r--r-- | src/pages/ProfilePage/ProfileInfo.tsx | 7 | ||||
-rw-r--r-- | src/pages/ProfilePage/ProfilePage.tsx | 6 | ||||
-rw-r--r-- | src/requests.ts | 2 |
11 files changed, 67 insertions, 58 deletions
diff --git a/src/components/Feed/Feed.tsx b/src/components/Feed/Feed.tsx index b857cc0..5805e35 100644 --- a/src/components/Feed/Feed.tsx +++ b/src/components/Feed/Feed.tsx @@ -16,12 +16,11 @@ interface RenderPropTypes { const Feed: React.FC<PropTypes> = ({ polls }) => { - const RenderItem: React.FC<RenderPropTypes> = ({ index, style, key }) => { const poll = polls[index]; return ( // To re-render on list resize, add this info to key - <div key={key + polls.length} style={style}> + <div key={key + polls.length} style={style}> <PollCard initialPoll={poll} /> </div> ); diff --git a/src/components/PollCard/PollCard.tsx b/src/components/PollCard/PollCard.tsx index 98ae001..2378945 100644 --- a/src/components/PollCard/PollCard.tsx +++ b/src/components/PollCard/PollCard.tsx @@ -58,7 +58,7 @@ const PollCard: React.FC<PropTypes> = ({ initialPoll }) => { const date: string = new Date(poll.createdAt).toLocaleString('default', DATE_FORMAT); const handleVote = (which: Which) => { - if (!isAuthenticated()) { + if (!isAuthenticated) { enqueueSnackbar('Unauthorized users can not vote in polls', { variant: 'error' }); diff --git a/src/hooks/APIClient.ts b/src/hooks/APIClient.ts index ce11134..7f7d170 100644 --- a/src/hooks/APIClient.ts +++ b/src/hooks/APIClient.ts @@ -9,9 +9,9 @@ const arrayOptions = { revalidateOnMount: true }; -export const useUser = (username: string) => { +export const useUser = (username: string | null) => { return useSWR( - `/users?username=${username}`, + username && `/users?username=${username}`, (url: string) => get(url).then(response => response.data[0]) ); }; @@ -21,5 +21,5 @@ export const useProfile = (id: string) => { }; export const useFeed = () => { - return useSWR(`/feed`, fetcher, arrayOptions); + return useSWR('/feed', fetcher, arrayOptions); }; diff --git a/src/hooks/useAuth.tsx b/src/hooks/useAuth.tsx index 55e142c..ed1e428 100644 --- a/src/hooks/useAuth.tsx +++ b/src/hooks/useAuth.tsx @@ -1,67 +1,60 @@ import React, { - useState, useEffect, useContext, createContext + useEffect, useCallback, useMemo, useContext, createContext } from 'react'; import { User } from 'which-types'; -import { post, get } from '../requests'; +import { post } from '../requests'; +import { useUser } from './APIClient'; +import useLocalStorage from './useLocalStorage'; interface ContextType { user: User | null, - setUser: (user: User) => void; login: (username: string, password: string, remember?: boolean) => Promise<boolean>; logout: () => void; - isAuthenticated: () => boolean; + isAuthenticated: boolean; } const authContext = createContext<ContextType>({ user: null, - setUser: () => {}, login: async () => false, logout: () => {}, - isAuthenticated: () => false + isAuthenticated: false }); const useProvideAuth = () => { - const [user, setUser] = useState<User | null>(null); + const [remember, setRemember] = useLocalStorage('remember'); + const [username, setUsername] = useLocalStorage('username'); + const [token, setToken] = useLocalStorage('token'); + const { data: user } = useUser(username); - const login: ContextType['login'] = (username, password, remember = true) => { + const isAuthenticated = useMemo(() => Boolean(username), [username]); + + const logout = useCallback(() => { + setToken(null); + setUsername(null); + }, [setToken, setUsername]); + + useEffect(() => { + // If should not remember, logout + if (!remember) logout(); + }, [remember, logout]); + + + const login: ContextType['login'] = (name, password, shouldRemember = true) => { return post('/authentication', { strategy: 'local', - username, + username: name, password }).then(response => { - const me = response.data.user; - const token = response.data.accessToken; - setUser(me); - localStorage.setItem('userId', me._id); - localStorage.setItem('token', token); - if (!remember) localStorage.setItem('shouldClear', 'true'); + setToken(response.data.accessToken); + setUsername(name); + setRemember(shouldRemember ? 'true' : null); return true; }).catch(() => false); }; - const logout = () => { - setUser(null); - localStorage.removeItem('userId'); - localStorage.removeItem('token'); - }; - - 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 + user, login, logout, token, isAuthenticated }; }; diff --git a/src/hooks/useLocalStorage.ts b/src/hooks/useLocalStorage.ts new file mode 100644 index 0000000..faf1411 --- /dev/null +++ b/src/hooks/useLocalStorage.ts @@ -0,0 +1,16 @@ +import { useState, useCallback } from 'react'; + +type Value = string | null; +type Setter = (value: Value) => void; + +export default (key: string): [Value, Setter] => { + const [state, setState] = useState<Value>(() => localStorage.getItem(key) || null); + + const update: Setter = useCallback(value => { + if (value) localStorage.setItem(key, value); + else localStorage.removeItem(key); + setState(value); + }, [key]); + + return [state, update]; +}; diff --git a/src/pages/FeedPage/FeedPage.tsx b/src/pages/FeedPage/FeedPage.tsx index 8e7fb55..da0fb2a 100644 --- a/src/pages/FeedPage/FeedPage.tsx +++ b/src/pages/FeedPage/FeedPage.tsx @@ -17,7 +17,7 @@ const FeedPage: React.FC = () => { return ( <Container maxWidth="sm" disableGutters> - {isAuthenticated() && <PollSubmission addPoll={addPoll} />} + {isAuthenticated && <PollSubmission addPoll={addPoll} />} <Feed polls={data} /> </Container> ); diff --git a/src/pages/HomePage/HomePage.tsx b/src/pages/HomePage/HomePage.tsx index 17e377a..b1dc506 100644 --- a/src/pages/HomePage/HomePage.tsx +++ b/src/pages/HomePage/HomePage.tsx @@ -92,7 +92,7 @@ const HomePage: React.FC = () => { Here you can share your thougts about Which with us! Note that you can ony leave feedback once per application version (there will be plenty of them later). </p> - {isAuthenticated() ? <ReviewForm /> : ( + {isAuthenticated ? <ReviewForm /> : ( <> <p> You must be authorized to leave feedback.</p> <Button @@ -142,7 +142,7 @@ const HomePage: React.FC = () => { <Button variant="contained" color="primary" size="large" onClick={handleLetsGo}> {'let\'s go!'} </Button> - {!isAuthenticated() && ( + {!isAuthenticated && ( <Button variant="outlined" color="primary" diff --git a/src/pages/Page.tsx b/src/pages/Page.tsx index 49c941a..a77a98e 100644 --- a/src/pages/Page.tsx +++ b/src/pages/Page.tsx @@ -5,12 +5,12 @@ import { SnackbarProvider } from 'notistack'; import { Switch, Route } from 'react-router-dom'; import Loading from '../components/Loading/Loading'; -const ProfilePage = React.lazy(() => import( './ProfilePage/ProfilePage')); -const FeedPage = React.lazy(() => import( './FeedPage/FeedPage')); -const LoginPage = React.lazy(() => import( './LoginPage/LoginPage')); -const RegistrationPage = React.lazy(() => import( './RegistrationPage/RegistrationPage')); -const HomePage = React.lazy(() => import( './HomePage/HomePage')); -const NotificationsPage = React.lazy(() => import( './NotificationsPage/NotificationsPage')); +const ProfilePage = React.lazy(() => import('./ProfilePage/ProfilePage')); +const FeedPage = React.lazy(() => import('./FeedPage/FeedPage')); +const LoginPage = React.lazy(() => import('./LoginPage/LoginPage')); +const RegistrationPage = React.lazy(() => import('./RegistrationPage/RegistrationPage')); +const HomePage = React.lazy(() => import('./HomePage/HomePage')); +const NotificationsPage = React.lazy(() => import('./NotificationsPage/NotificationsPage')); const useStyles = makeStyles(theme => ({ diff --git a/src/pages/ProfilePage/ProfileInfo.tsx b/src/pages/ProfilePage/ProfileInfo.tsx index 8b1447a..9eee4c1 100644 --- a/src/pages/ProfilePage/ProfileInfo.tsx +++ b/src/pages/ProfilePage/ProfileInfo.tsx @@ -86,7 +86,7 @@ const ProfileInfo: React.FC<PropTypes> = ({ }) => { const classes = useStyles(); const [input, setInput] = useState(false); - const { setUser } = useAuth(); + const { user } = useAuth(); const dateSince = new Date(userInfo?.createdAt || '').toLocaleDateString(); const handleClick = () => { @@ -94,10 +94,9 @@ const ProfileInfo: React.FC<PropTypes> = ({ }; const patchAvatar = (url: string) => { - const id = localStorage.getItem('userId'); + const id = user?._id; patch(`/users/${id}`, { avatarUrl: url }).then(res => { setUserInfo(res.data); - setUser(res.data); }); }; @@ -106,7 +105,7 @@ const ProfileInfo: React.FC<PropTypes> = ({ { !userInfo ? <Skeleton animation="wave" variant="circle" width={150} height={150} className={classes.avatar} /> - : userInfo?._id === localStorage.getItem('userId') + : userInfo?._id === user?._id ? ( <div> <MoreMenu /> diff --git a/src/pages/ProfilePage/ProfilePage.tsx b/src/pages/ProfilePage/ProfilePage.tsx index 293b766..9e00784 100644 --- a/src/pages/ProfilePage/ProfilePage.tsx +++ b/src/pages/ProfilePage/ProfilePage.tsx @@ -21,7 +21,7 @@ const ProfilePage: React.FC = () => { if (!username) { if (user) history.push(`/profile/${user.username}`); else history.push('/login'); - }; + } }, [username, history, user]); @@ -31,7 +31,9 @@ const ProfilePage: React.FC = () => { const { left, right } = current.contents; return total + left.votes + right.votes; }, 0 - ), [polls]); + ), + [polls] + ); return ( <Container maxWidth="sm" disableGutters> diff --git a/src/requests.ts b/src/requests.ts index e1f82b4..8ec8e3d 100644 --- a/src/requests.ts +++ b/src/requests.ts @@ -14,7 +14,7 @@ requests.interceptors.request.use(config => { requests.interceptors.response.use(response => response, error => { if (error.message === 'Request failed with status code 401' && localStorage.getItem('token')) { - localStorage.setItem('shouldClear', 'true'); + localStorage.removeItem('remember'); window.location.reload(); } return Promise.reject(error); |