From b6dacddb735d8c79fb8c221050fc85d8f6119cab Mon Sep 17 00:00:00 2001 From: Jim O'Donnell Date: Mon, 23 Oct 2023 14:20:03 +0100 Subject: [PATCH] Rewrite usePanoptesUser with useSWR - use `useSWR` to fetch the user object from Panoptes. - persist the returned data in local storage. - reset the current Panoptes session when auth changes. --- .../app-root/src/helpers/fetchPanoptesUser.js | 2 +- .../app-root/src/hooks/usePanoptesUser.js | 70 +++++++++++-------- 2 files changed, 41 insertions(+), 31 deletions(-) diff --git a/packages/app-root/src/helpers/fetchPanoptesUser.js b/packages/app-root/src/helpers/fetchPanoptesUser.js index 989fe754b3b..caa89f36765 100644 --- a/packages/app-root/src/helpers/fetchPanoptesUser.js +++ b/packages/app-root/src/helpers/fetchPanoptesUser.js @@ -5,7 +5,7 @@ import { auth as authHelpers } from '@zooniverse/panoptes-js' Get a Panoptes user from a Panoptes JSON Web Token (JWT), if we have one, or from the Panoptes API otherwise. */ -export default async function fetchPanoptesUser(storedUser) { +export default async function fetchPanoptesUser({ user: storedUser }) { try { const jwt = await auth.checkBearerToken() /* diff --git a/packages/app-root/src/hooks/usePanoptesUser.js b/packages/app-root/src/hooks/usePanoptesUser.js index ed069c7cad1..4203507a95e 100644 --- a/packages/app-root/src/hooks/usePanoptesUser.js +++ b/packages/app-root/src/hooks/usePanoptesUser.js @@ -1,56 +1,66 @@ import auth from 'panoptes-client/lib/auth' -import { useEffect, useState } from 'react' +import { useEffect } from 'react' +import useSWR from 'swr' import { fetchPanoptesUser } from '../helpers' const isBrowser = typeof window !== 'undefined' +const SWROptions = { + revalidateIfStale: true, + revalidateOnMount: true, + revalidateOnFocus: true, + revalidateOnReconnect: true, + refreshInterval: 0 +} + if (isBrowser) { auth.checkCurrent() } const localStorage = isBrowser ? window.localStorage : null const storedUserJSON = localStorage?.getItem('panoptes-user') -let user = storedUserJSON && JSON.parse(storedUserJSON) +let storedUser = storedUserJSON && JSON.parse(storedUserJSON) /* Null users crash the ZooHeader component. Set them to undefined for now. */ -if (user === null) { - user = undefined +if (storedUser === null) { + storedUser = undefined } export default function usePanoptesUser() { - const [error, setError] = useState(null) - const [loading, setLoading] = useState(true) - - useEffect(function () { - async function checkUserSession() { - setLoading(true) - try { - const panoptesUser = await fetchPanoptesUser(user) - if (panoptesUser) { - localStorage?.setItem('panoptes-user', JSON.stringify(panoptesUser)) - user = panoptesUser - } else { - user = undefined - localStorage?.removeItem('panoptes-user') - } - } catch (error) { - setError(error) - } - setLoading(false) - } + const key = { + user: storedUser, + endpoint: '/me' + } - if (isBrowser) { - checkUserSession() - } - auth.listen('change', checkUserSession) + /* + `useSWR` here will always return the same stale user object. + See https://github.com/zooniverse/panoptes-javascript-client/issues/207 + */ + const { data, error, isLoading } = useSWR(key, fetchPanoptesUser, SWROptions) + if (data) { + storedUser = data + } + + useEffect(function subscribeToAuthChanges() { + auth.listen('change', auth.checkCurrent) return function () { - auth.stopListening('change', checkUserSession) + auth.stopListening('change', auth.checkCurrent) } }, []) - return { data: user, error, isLoading: loading } + useEffect(function persistUserInStorage() { + if (data) { + localStorage?.setItem('panoptes-user', JSON.stringify(data)) + } + + return () => { + localStorage?.removeItem('panoptes-user') + } + }, [data]) + + return { data: storedUser, error, isLoading } }