From 29fbcd1290215946b4cde1130d5ab6ec0e0df631 Mon Sep 17 00:00:00 2001 From: Filipe Deschamps Date: Tue, 7 Jun 2022 15:02:21 -0700 Subject: [PATCH 1/4] feat(use-user): cache user state to `localStorage` --- pages/interface/components/Header/index.js | 10 ++-- pages/interface/hooks/useUser/index.js | 54 +++++++++++++++++++++- pages/login/index.public.js | 11 +++++ 3 files changed, 69 insertions(+), 6 deletions(-) diff --git a/pages/interface/components/Header/index.js b/pages/interface/components/Header/index.js index b07fcf28c..d5b0d2577 100644 --- a/pages/interface/components/Header/index.js +++ b/pages/interface/components/Header/index.js @@ -5,24 +5,24 @@ import { useUser } from 'pages/interface/index.js'; export default function HeaderComponent() { const router = useRouter(); - const { user, isLoading } = useUser(); + const { user, isLoading, loginStatus } = useUser(); return (
- + TabNews - + Status - {!isLoading && !user.username && ( + {loginStatus === 'logged-out' && ( <> @@ -37,7 +37,7 @@ export default function HeaderComponent() { )} - {!isLoading && user.username && ( + {loginStatus === 'logged-in' && ( <> diff --git a/pages/interface/hooks/useUser/index.js b/pages/interface/hooks/useUser/index.js index 332bfe77c..c61a16c13 100644 --- a/pages/interface/hooks/useUser/index.js +++ b/pages/interface/hooks/useUser/index.js @@ -1,15 +1,67 @@ import useSWR from 'swr'; +import { useState, useEffect } from 'react'; + +async function fetcher(url) { + const response = await fetch(url); + const responseBody = await response.json(); + + if (response.status === 401 || response.status === 403) { + const error = new Error(responseBody.message); + error.status = response.status; + throw error; + } + + return responseBody; +} + +const userEndpoint = '/api/v1/user'; export default function useUser() { - const { data, isLoading, isValidating, error } = useSWR('/api/v1/user', { + const [user, setUser] = useState(); + + // Stage 1 = true (always revalidate) + // Stage 2 = false (revalidate only if user stored in localStorage) + const [shouldRevalidate, setShouldRevalidate] = useState(true); + + const [loginStatus, setLoginStatus] = useState('loading'); + + useEffect(() => { + const userStored = localStorage.getItem('user'); + + if (userStored) { + setLoginStatus('logged-in'); + setUser(JSON.parse(userStored)); + setShouldRevalidate(true); + } else { + setLoginStatus('logged-out'); + } + }, []); + + const { data, isLoading, isValidating, error } = useSWR(shouldRevalidate ? userEndpoint : false, fetcher, { + fallbackData: user, revalidateOnFocus: false, revalidateOnReconnect: false, + onSuccess, + onError, }); + function onSuccess(data) { + localStorage.setItem('user', JSON.stringify(data)); + setLoginStatus('logged-in'); + } + + function onError(error) { + if (error.status === 401 || error.status === 403) { + localStorage.removeItem('user'); + setLoginStatus('logged-out'); + } + } + return { user: data, isLoading: isLoading, isValidating: isValidating, error: error, + loginStatus: loginStatus, }; } diff --git a/pages/login/index.public.js b/pages/login/index.public.js index 715f12a7e..3c92259db 100644 --- a/pages/login/index.public.js +++ b/pages/login/index.public.js @@ -62,6 +62,17 @@ function LoginForm() { const responseBody = await response.json(); if (response.status === 201) { + const userResponse = await fetch(`/api/v1/user`, { + method: 'GET', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + }); + + const userResponseBody = await userResponse.json(); + localStorage.setItem('user', JSON.stringify(userResponseBody)); + if (router.query?.redirect) { router.push(router.query.redirect); } else { From 3fc92f3042286f58fb80d8311c13ffd50f5ac5c2 Mon Sep 17 00:00:00 2001 From: aprendendofelipe <77860630+aprendendofelipe@users.noreply.github.com> Date: Wed, 8 Jun 2022 21:03:44 -0300 Subject: [PATCH 2/4] feat(use-user): for now remove `localStorage.getItem` --- pages/interface/components/Content/index.js | 5 ++-- pages/interface/components/Header/index.js | 10 +++----- pages/interface/hooks/useUser/index.js | 28 ++------------------- pages/login/index.public.js | 11 -------- 4 files changed, 8 insertions(+), 46 deletions(-) diff --git a/pages/interface/components/Content/index.js b/pages/interface/components/Content/index.js index a5850ccf2..fb998991e 100644 --- a/pages/interface/components/Content/index.js +++ b/pages/interface/components/Content/index.js @@ -192,8 +192,7 @@ export default function Content({ content, mode = 'view', viewFrame = false }) { - {user && - (user.id === contentObject.owner_id || user.features?.includes('update:content:others')) && + {(user?.id === contentObject.owner_id || user?.features?.includes('update:content:others')) && ViewModeOptionsMenu()} @@ -254,7 +253,7 @@ export default function Content({ content, mode = 'view', viewFrame = false }) { const handleSubmit = useCallback( async (event) => { event.preventDefault(); - if (!user.username) { + if (!user?.username) { router.push('/login'); return; } diff --git a/pages/interface/components/Header/index.js b/pages/interface/components/Header/index.js index d5b0d2577..1d67a1379 100644 --- a/pages/interface/components/Header/index.js +++ b/pages/interface/components/Header/index.js @@ -1,11 +1,9 @@ -import { useRouter } from 'next/router'; -import { Header, Box, Button, ActionMenu, ActionList } from '@primer/react'; +import { Header, Box, ActionMenu, ActionList } from '@primer/react'; import { CgTab } from 'react-icons/cg'; import { useUser } from 'pages/interface/index.js'; export default function HeaderComponent() { - const router = useRouter(); - const { user, isLoading, loginStatus } = useUser(); + const { user, isLoading } = useUser(); return (
@@ -22,7 +20,7 @@ export default function HeaderComponent() { - {loginStatus === 'logged-out' && ( + {!isLoading && !user?.username && ( <> @@ -37,7 +35,7 @@ export default function HeaderComponent() { )} - {loginStatus === 'logged-in' && ( + {user?.username && ( <> diff --git a/pages/interface/hooks/useUser/index.js b/pages/interface/hooks/useUser/index.js index c61a16c13..d496a641a 100644 --- a/pages/interface/hooks/useUser/index.js +++ b/pages/interface/hooks/useUser/index.js @@ -1,5 +1,4 @@ import useSWR from 'swr'; -import { useState, useEffect } from 'react'; async function fetcher(url) { const response = await fetch(url); @@ -17,28 +16,8 @@ async function fetcher(url) { const userEndpoint = '/api/v1/user'; export default function useUser() { - const [user, setUser] = useState(); - - // Stage 1 = true (always revalidate) - // Stage 2 = false (revalidate only if user stored in localStorage) - const [shouldRevalidate, setShouldRevalidate] = useState(true); - - const [loginStatus, setLoginStatus] = useState('loading'); - - useEffect(() => { - const userStored = localStorage.getItem('user'); - - if (userStored) { - setLoginStatus('logged-in'); - setUser(JSON.parse(userStored)); - setShouldRevalidate(true); - } else { - setLoginStatus('logged-out'); - } - }, []); - - const { data, isLoading, isValidating, error } = useSWR(shouldRevalidate ? userEndpoint : false, fetcher, { - fallbackData: user, + const { data, isLoading, isValidating, error } = useSWR(userEndpoint, fetcher, { + shouldRetryOnError: false, revalidateOnFocus: false, revalidateOnReconnect: false, onSuccess, @@ -47,13 +26,11 @@ export default function useUser() { function onSuccess(data) { localStorage.setItem('user', JSON.stringify(data)); - setLoginStatus('logged-in'); } function onError(error) { if (error.status === 401 || error.status === 403) { localStorage.removeItem('user'); - setLoginStatus('logged-out'); } } @@ -62,6 +39,5 @@ export default function useUser() { isLoading: isLoading, isValidating: isValidating, error: error, - loginStatus: loginStatus, }; } diff --git a/pages/login/index.public.js b/pages/login/index.public.js index 3c92259db..715f12a7e 100644 --- a/pages/login/index.public.js +++ b/pages/login/index.public.js @@ -62,17 +62,6 @@ function LoginForm() { const responseBody = await response.json(); if (response.status === 201) { - const userResponse = await fetch(`/api/v1/user`, { - method: 'GET', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - }, - }); - - const userResponseBody = await userResponse.json(); - localStorage.setItem('user', JSON.stringify(userResponseBody)); - if (router.query?.redirect) { router.push(router.query.redirect); } else { From 9042fd975a4641497604d0de07e686a80bce8938 Mon Sep 17 00:00:00 2001 From: Filipe Deschamps Date: Thu, 9 Jun 2022 09:55:31 -0700 Subject: [PATCH 3/4] feat(use-user): only saves to `localStorage` useful and non-sensitive information --- pages/interface/hooks/useUser/index.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pages/interface/hooks/useUser/index.js b/pages/interface/hooks/useUser/index.js index d496a641a..bb7075a08 100644 --- a/pages/interface/hooks/useUser/index.js +++ b/pages/interface/hooks/useUser/index.js @@ -25,7 +25,14 @@ export default function useUser() { }); function onSuccess(data) { - localStorage.setItem('user', JSON.stringify(data)); + localStorage.setItem( + 'user', + JSON.stringify({ + id: data.id, + username: data.username, + features: data.features, + }) + ); } function onError(error) { From 4869f3f92b51b76475337cea7abc3fb5f7b6dd74 Mon Sep 17 00:00:00 2001 From: Filipe Deschamps Date: Thu, 9 Jun 2022 10:08:38 -0700 Subject: [PATCH 4/4] build(node): pin Node version to `16.15.0` --- .github/workflows/ci.yml | 4 ++-- .nvmrc | 2 +- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0d620cbb3..804a7ec37 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: - uses: actions/setup-node@v2 with: - node-version: 16 + node-version: '16.15.0' cache: 'npm' - run: npm ci - run: npm run dev & npx jest --runInBand @@ -41,7 +41,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-node@v2 with: - node-version: 16 + node-version: '16.15.0' cache: 'npm' - run: npm ci - run: npm run lint diff --git a/.nvmrc b/.nvmrc index 6f7f377bf..7fd023741 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v16 +v16.15.0 diff --git a/package.json b/package.json index c2a679510..80ce68e21 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "migration:seed": "node infra/scripts/seed-database.js" }, "engines": { - "node": "^16" + "node": "16.15.0" }, "name": "tabnews.com.br", "description": "Conteúdos para quem vive de programação e tecnologia.",