diff --git a/src/components/AbstractSideNav/AbstractSideNav.tsx b/src/components/AbstractSideNav/AbstractSideNav.tsx index f5b1a9334..aebc4cdf3 100644 --- a/src/components/AbstractSideNav/AbstractSideNav.tsx +++ b/src/components/AbstractSideNav/AbstractSideNav.tsx @@ -1,6 +1,6 @@ -import { DEFAULT_USER_DATA, IDocsEntity, useGetUserSettings, useHasGraphics, useHasMetrics } from '@api'; +import { IDocsEntity, useGetUserSettings, useHasGraphics, useHasMetrics } from '@api'; import { Badge } from '@chakra-ui/react'; -import { IMenuItem, SideNavigationMenu, TopNavigationMenu, exportFormats } from '@components'; +import { exportFormats, IMenuItem, SideNavigationMenu, TopNavigationMenu } from '@components'; import { ArrowDownIcon as DownloadIcon, ChartPieIcon, @@ -40,7 +40,7 @@ const useGetItems = ({ // for export citation menu link, it needs to go to user's default setting if logged in // otherwise go to bibtex - const defaultExportFormat = settings?.defaultExportFormat ?? DEFAULT_USER_DATA.defaultExportFormat; + const defaultExportFormat = settings.defaultExportFormat; const defaultExportFormatPath = typeof defaultExportFormat === 'string' ? values(exportFormats).find((f) => f.label === defaultExportFormat).value diff --git a/src/components/AbstractSources/AbstractSources.tsx b/src/components/AbstractSources/AbstractSources.tsx index 4d0a9bb34..d6aea50b7 100644 --- a/src/components/AbstractSources/AbstractSources.tsx +++ b/src/components/AbstractSources/AbstractSources.tsx @@ -29,7 +29,8 @@ export interface IAbstractSourcesProps extends HTMLAttributes { export const AbstractSources = ({ doc }: IAbstractSourcesProps): ReactElement => { const isClient = useIsClient(); const { data: settings } = useGetUserSettings(); - const sources = processLinkData(doc, settings?.link_server); + + const sources = processLinkData(doc, settings.link_server); const { data: relatedWorksResp } = useResolverQuery( { bibcode: doc.bibcode, link_type: 'associated' }, diff --git a/src/components/AbstractSources/linkGenerator.ts b/src/components/AbstractSources/linkGenerator.ts index 3cb3845d9..8b1a47c27 100644 --- a/src/components/AbstractSources/linkGenerator.ts +++ b/src/components/AbstractSources/linkGenerator.ts @@ -2,7 +2,7 @@ import { Esources, IDocsEntity } from '@api'; import { compose, descend, is, map, pipe, prop, sort } from 'ramda'; import { DEFAULT_ORDERING, GATEWAY_BASE_URL, LINK_TYPES, MAYBE_OPEN_SOURCES } from './model'; import { getOpenUrl } from './openUrlGenerator'; -import { isNilOrEmpty } from 'ramda-adjunct'; +import { isNilOrEmpty, isNonEmptyString } from 'ramda-adjunct'; import { IDataProductSource, IFullTextSource, ProcessLinkDataReturns } from '@components/AbstractSources/types'; /** @@ -45,7 +45,7 @@ export const processLinkData = (doc: IDocsEntity, linkServer?: string): ProcessL */ const maybeCreateInstitutionURL = () => { const identifier = doc.doi || doc.issn || doc.isbn; - if (identifier && linkServer) { + if (identifier && isNonEmptyString(linkServer)) { return { url: getOpenUrl({ metadata: doc, linkServer }), openUrl: true, diff --git a/src/components/CitationExporter/components/CustomFormatSelect.tsx b/src/components/CitationExporter/components/CustomFormatSelect.tsx index 190a8117c..572cace0d 100644 --- a/src/components/CitationExporter/components/CustomFormatSelect.tsx +++ b/src/components/CitationExporter/components/CustomFormatSelect.tsx @@ -15,7 +15,7 @@ export interface ICustomFormatSelectProps { export const CustomFormatSelect = ({ dispatch }: ICustomFormatSelectProps) => { const { isAuthenticated } = useSession(); - const { settings: settingsData } = useSettings({ enabled: isAuthenticated, suspense: false }); + const { settings: settingsData } = useSettings({ suspense: false }); const customFormats = settingsData?.customFormats ?? DEFAULT_USER_DATA.customFormats; diff --git a/src/components/ResultList/ListActions.tsx b/src/components/ResultList/ListActions.tsx index 280159ed6..18518c758 100644 --- a/src/components/ResultList/ListActions.tsx +++ b/src/components/ResultList/ListActions.tsx @@ -1,4 +1,4 @@ -import { Bibcode, DEFAULT_USER_DATA, ExportApiFormatKey, useVaultBigQuerySearch } from '@api'; +import { Bibcode, ExportApiFormatKey, useVaultBigQuerySearch } from '@api'; import { ChevronDownIcon, SettingsIcon } from '@chakra-ui/icons'; import { Button, @@ -56,9 +56,7 @@ export const ListActions = (props: IListActionsProps): ReactElement => { const router = useRouter(); const toast = useToast(); - const { settings } = useSettings({ - enabled: isAuthenticated, - }); + const { settings } = useSettings({ suspense: false }); useEffect(() => { setExploreAll(noneSelected); @@ -187,10 +185,7 @@ export const ListActions = (props: IListActionsProps): ReactElement => { )} - + diff --git a/src/lib/useSession.ts b/src/lib/useSession.ts index 7fa0bf2cf..de1b8027f 100644 --- a/src/lib/useSession.ts +++ b/src/lib/useSession.ts @@ -1,24 +1,17 @@ import api, { isAuthenticated } from '@api'; import axios from 'axios'; import { useEffect } from 'react'; -import { NotificationId } from '@store/slices'; import { useUser } from '@lib/useUser'; import { useMutation } from '@tanstack/react-query'; import { ILogoutResponse } from '@pages/api/auth/logout'; -import { useReloadWithNotification } from '@components/Notification'; - -interface IUseSessionProps { - redirectWithMessage?: NotificationId; -} +import { useRouter } from 'next/router'; /** * Provides access to the user session and methods to logout - * @param props */ -export const useSession = (props: IUseSessionProps = { redirectWithMessage: null }) => { - const { redirectWithMessage } = props; +export const useSession = () => { const { user, reset } = useUser(); - const reload = useReloadWithNotification(); + const { reload } = useRouter(); const { mutate: logout, ...result } = useMutation(['logout'], async () => { const { data } = await axios.post('/api/auth/logout'); @@ -28,13 +21,15 @@ export const useSession = (props: IUseSessionProps = { redirectWithMessage: null useEffect(() => { if (result.data?.success) { api.reset(); - reset().finally(() => void reload(redirectWithMessage ?? 'account-logout-success')); + reset().finally(() => { + reload(); + }); } - }, [result.data?.success, redirectWithMessage]); + }, [result.data?.success]); useEffect(() => { if (result.isError) { - void reload('account-logout-failed'); + reload(); } }, [result.isError]); diff --git a/src/lib/useSettings.ts b/src/lib/useSettings.ts index a98349057..b2d46472c 100644 --- a/src/lib/useSettings.ts +++ b/src/lib/useSettings.ts @@ -12,8 +12,10 @@ import { useQueryClient, UseQueryOptions } from '@tanstack/react-query'; import { useToast } from '@chakra-ui/react'; import { useEffect } from 'react'; import { isNotEmpty } from 'ramda-adjunct'; +import { useSession } from '@lib/useSession'; export const useSettings = (options?: UseQueryOptions) => { + const { isAuthenticated } = useSession(); const toast = useToast({ position: 'bottom', isClosable: true, @@ -23,6 +25,8 @@ export const useSettings = (options?: UseQueryOptions) const queryClient = useQueryClient(); const { data: settings, ...getSettingsState } = useGetUserSettings({ suspense: true, + retry: false, + enabled: isAuthenticated, ...options, }); const { mutate, ...updateSettingsState } = useUpdateUserSettings({ diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 59fe41a47..4609b63ec 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -11,11 +11,11 @@ import dynamic from 'next/dynamic'; import { useRouter } from 'next/router'; import 'nprogress/nprogress.css'; import { FC, memo, ReactElement, useEffect } from 'react'; -import { DehydratedState, Hydrate, QueryClientProvider, useQuery } from '@tanstack/react-query'; +import { DehydratedState, Hydrate, QueryClientProvider, useQuery, useQueryClient } from '@tanstack/react-query'; import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import { IronSession } from 'iron-session'; import axios from 'axios'; -import api, { checkUserData } from '@api'; +import api, { checkUserData, userKeys } from '@api'; import { isNilOrEmpty, notEqual } from 'ramda-adjunct'; import { useUser } from '@lib/useUser'; import { GoogleReCaptchaProvider } from 'react-google-recaptcha-v3'; @@ -106,6 +106,7 @@ const UserSync = (): ReactElement => { const router = useRouter(); const store = useStoreApi(); const { user } = useUser(); + const qc = useQueryClient(); const { data } = useQuery<{ user: IronSession['token']; @@ -132,6 +133,9 @@ const UserSync = (): ReactElement => { // apply the user data to the api instance api.setUserData(data.user); + + // attempt to invalidate any currently cached user settings + void qc.invalidateQueries(userKeys.getUserSettings()); } }, [data, store, user]); diff --git a/src/pages/abs/[id]/exportcitation/[format].tsx b/src/pages/abs/[id]/exportcitation/[format].tsx index a9cd21c18..1e37cc832 100644 --- a/src/pages/abs/[id]/exportcitation/[format].tsx +++ b/src/pages/abs/[id]/exportcitation/[format].tsx @@ -9,7 +9,6 @@ import { path } from 'ramda'; import { composeNextGSSP } from '@ssr-utils'; import { useRouter } from 'next/router'; import { getDetailsPageTitle } from '@pages/abs/[id]/abstract'; -import { useSession } from '@lib/useSession'; import { useSettings } from '@lib/useSettings'; const ExportCitationPage: NextPage = () => { @@ -18,11 +17,8 @@ const ExportCitationPage: NextPage = () => { const { data } = useGetAbstract({ id: router.query.id as string }); const doc = path(['docs', 0], data); - const { isAuthenticated } = useSession(); - // get export related user settings const { settings } = useSettings({ - enabled: isAuthenticated, suspense: false, }); diff --git a/src/pages/search/exportcitation/[format].tsx b/src/pages/search/exportcitation/[format].tsx index 76a288c15..0dd77ed2b 100644 --- a/src/pages/search/exportcitation/[format].tsx +++ b/src/pages/search/exportcitation/[format].tsx @@ -23,7 +23,6 @@ import { useRouter } from 'next/router'; import { last, omit } from 'ramda'; import { dehydrate, QueryClient } from '@tanstack/react-query'; import { composeNextGSSP } from '@ssr-utils'; -import { useSession } from '@lib/useSession'; import { useSettings } from '@lib/useSettings'; interface IExportCitationPageProps { @@ -39,11 +38,9 @@ const ExportCitationPage: NextPage = (props) => { const { format, query, error } = props; const isClient = useIsClient(); const router = useRouter(); - const { isAuthenticated } = useSession(); // get export related user settings const { settings } = useSettings({ - enabled: isAuthenticated, suspense: false, }); diff --git a/src/pages/user/account/login.tsx b/src/pages/user/account/login.tsx index 634189b6a..0bd9ae92f 100644 --- a/src/pages/user/account/login.tsx +++ b/src/pages/user/account/login.tsx @@ -7,17 +7,17 @@ import { FormEventHandler, useCallback, useEffect, useState } from 'react'; import { useMutation } from '@tanstack/react-query'; import axios, { AxiosError } from 'axios'; import { ILoginResponse } from '@pages/api/auth/login'; -import { useReloadWithNotification } from '@components/Notification'; import { useFocus } from '@lib/useFocus'; import { useUser } from '@lib/useUser'; import { parseAPIError } from '@utils'; +import { useRouter } from 'next/router'; const initialParams: IUserCredentials = { email: '', password: '' }; const Login: NextPage = () => { + const { reload } = useRouter(); const [params, setParams] = useState(initialParams); const [mainInputRef, focus] = useFocus(); - const reload = useReloadWithNotification(); const { reset: resetUser } = useUser(); const { @@ -44,7 +44,7 @@ const Login: NextPage = () => { // redirect on successful login useEffect(() => { if (data?.success) { - resetUser().finally(() => void reload('account-login-success')); + reload(); } }, [data?.success, reload, resetUser]); @@ -108,10 +108,11 @@ const Login: NextPage = () => { Forgot password? - - {isLoading ? null : ( + {isLoading || data?.success ? null : ( Register diff --git a/src/pages/user/settings/delete.tsx b/src/pages/user/settings/delete.tsx index edb7b98d5..f717d9c00 100644 --- a/src/pages/user/settings/delete.tsx +++ b/src/pages/user/settings/delete.tsx @@ -15,7 +15,7 @@ const DeleteAccountPage = () => { const [email, setEmail] = useState(''); const [formError, setFormError] = useState(null); const storeEmail = useStore((state: AppState) => state.user.username); - const { logout } = useSession({ redirectWithMessage: 'account-deleted-success' }); + const { logout } = useSession(); const { mutate: deleteAccount, error,