Improved cache invalidation, and minor design fixes
This commit is contained in:
24
apps/web/components/react-query-provider.tsx
Normal file
24
apps/web/components/react-query-provider.tsx
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
// set gcTime to 0 on the server
|
||||||
|
// as we cannot invalidate the cache on the server
|
||||||
|
const isServer = typeof document === 'undefined';
|
||||||
|
const gcTime = isServer ? 0 : undefined;
|
||||||
|
const staleTime = isServer ? 0 : 60 * 1000;
|
||||||
|
|
||||||
|
const queryClient = new QueryClient({
|
||||||
|
defaultOptions: {
|
||||||
|
queries: {
|
||||||
|
gcTime,
|
||||||
|
staleTime,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export function ReactQueryProvider(props: React.PropsWithChildren) {
|
||||||
|
return (
|
||||||
|
<QueryClientProvider client={queryClient}>
|
||||||
|
{props.children}
|
||||||
|
</QueryClientProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
|
|
||||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
||||||
import { ReactQueryStreamedHydration } from '@tanstack/react-query-next-experimental';
|
import { ReactQueryStreamedHydration } from '@tanstack/react-query-next-experimental';
|
||||||
import { ThemeProvider } from 'next-themes';
|
import { ThemeProvider } from 'next-themes';
|
||||||
|
|
||||||
@@ -17,8 +16,9 @@ import pathsConfig from '~/config/paths.config';
|
|||||||
import { i18nResolver } from '~/lib/i18n/i18n.resolver';
|
import { i18nResolver } from '~/lib/i18n/i18n.resolver';
|
||||||
import { getI18nSettings } from '~/lib/i18n/i18n.settings';
|
import { getI18nSettings } from '~/lib/i18n/i18n.settings';
|
||||||
|
|
||||||
|
import { ReactQueryProvider } from './react-query-provider';
|
||||||
|
|
||||||
const captchaSiteKey = authConfig.captchaTokenSiteKey;
|
const captchaSiteKey = authConfig.captchaTokenSiteKey;
|
||||||
const queryClient = new QueryClient();
|
|
||||||
|
|
||||||
const CaptchaTokenSetter = dynamic(async () => {
|
const CaptchaTokenSetter = dynamic(async () => {
|
||||||
if (!captchaSiteKey) {
|
if (!captchaSiteKey) {
|
||||||
@@ -44,7 +44,7 @@ export function RootProviders({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<MonitoringProvider>
|
<MonitoringProvider>
|
||||||
<QueryClientProvider client={queryClient}>
|
<ReactQueryProvider>
|
||||||
<ReactQueryStreamedHydration>
|
<ReactQueryStreamedHydration>
|
||||||
<I18nProvider settings={i18nSettings} resolver={i18nResolver}>
|
<I18nProvider settings={i18nSettings} resolver={i18nResolver}>
|
||||||
<CaptchaProvider>
|
<CaptchaProvider>
|
||||||
@@ -64,7 +64,7 @@ export function RootProviders({
|
|||||||
</CaptchaProvider>
|
</CaptchaProvider>
|
||||||
</I18nProvider>
|
</I18nProvider>
|
||||||
</ReactQueryStreamedHydration>
|
</ReactQueryStreamedHydration>
|
||||||
</QueryClientProvider>
|
</ReactQueryProvider>
|
||||||
</MonitoringProvider>
|
</MonitoringProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ export function PersonalAccountDropdown({
|
|||||||
const phone = user?.phone ?? undefined;
|
const phone = user?.phone ?? undefined;
|
||||||
|
|
||||||
return email ?? phone;
|
return email ?? phone;
|
||||||
}, [user?.email, user?.phone]);
|
}, [user]);
|
||||||
|
|
||||||
const displayName =
|
const displayName =
|
||||||
personalAccountData?.name ?? account?.name ?? user?.email ?? '';
|
personalAccountData?.name ?? account?.name ?? user?.email ?? '';
|
||||||
|
|||||||
@@ -140,6 +140,7 @@ function useSetSession(tokens: { accessToken: string; refreshToken: string }) {
|
|||||||
|
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ['impersonate-user', tokens.accessToken, tokens.refreshToken],
|
queryKey: ['impersonate-user', tokens.accessToken, tokens.refreshToken],
|
||||||
|
gcTime: 0,
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
await supabase.auth.setSession({
|
await supabase.auth.setSession({
|
||||||
refresh_token: tokens.refreshToken,
|
refresh_token: tokens.refreshToken,
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
"@types/react": "^18.3.3",
|
"@types/react": "^18.3.3",
|
||||||
"next": "14.2.3",
|
"next": "14.2.3",
|
||||||
"react": "18.3.1",
|
"react": "18.3.1",
|
||||||
|
"server-only": "^0.0.1",
|
||||||
"zod": "^3.23.8"
|
"zod": "^3.23.8"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import { useEffect } from 'react';
|
|||||||
|
|
||||||
import { usePathname, useRouter } from 'next/navigation';
|
import { usePathname, useRouter } from 'next/navigation';
|
||||||
|
|
||||||
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { useSupabase } from './use-supabase';
|
import { useSupabase } from './use-supabase';
|
||||||
import { useRevalidateUserSession, useUserSession } from './use-user-session';
|
import { useRevalidateUserSession, useUserSession } from './use-user-session';
|
||||||
|
|
||||||
@@ -30,11 +32,12 @@ export function useAuthChangeListener({
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const revalidateUserSession = useRevalidateUserSession();
|
const revalidateUserSession = useRevalidateUserSession();
|
||||||
const session = useUserSession();
|
const session = useUserSession();
|
||||||
|
const queryClient = useQueryClient();
|
||||||
const accessToken = session.data?.access_token;
|
const accessToken = session.data?.access_token;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// keep this running for the whole session unless the component was unmounted
|
// keep this running for the whole session unless the component was unmounted
|
||||||
const listener = client.auth.onAuthStateChange((event, user) => {
|
const listener = client.auth.onAuthStateChange(async (event, user) => {
|
||||||
// log user out if user is falsy
|
// log user out if user is falsy
|
||||||
// and if the current path is a private route
|
// and if the current path is a private route
|
||||||
const shouldRedirectUser =
|
const shouldRedirectUser =
|
||||||
@@ -47,10 +50,14 @@ export function useAuthChangeListener({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// revalidate user session when user signs in or out
|
||||||
if (event === 'SIGNED_OUT') {
|
if (event === 'SIGNED_OUT') {
|
||||||
|
await queryClient.invalidateQueries();
|
||||||
|
|
||||||
return router.refresh();
|
return router.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// revalidate user session when access token is out of sync
|
||||||
if (accessToken) {
|
if (accessToken) {
|
||||||
const isOutOfSync = user?.access_token !== accessToken;
|
const isOutOfSync = user?.access_token !== accessToken;
|
||||||
|
|
||||||
@@ -70,6 +77,7 @@ export function useAuthChangeListener({
|
|||||||
pathName,
|
pathName,
|
||||||
appHomePath,
|
appHomePath,
|
||||||
privatePathPrefixes,
|
privatePathPrefixes,
|
||||||
|
queryClient,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
import { useMutation } from '@tanstack/react-query';
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { useSupabase } from './use-supabase';
|
import { useSupabase } from './use-supabase';
|
||||||
|
|
||||||
export function useSignOut() {
|
export function useSignOut() {
|
||||||
const client = useSupabase();
|
const client = useSupabase();
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: async () => {
|
mutationFn: async () => {
|
||||||
await client.auth.signOut();
|
await client.auth.signOut();
|
||||||
|
await queryClient.invalidateQueries();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,14 +24,10 @@ export function useUser(initialData?: User | null) {
|
|||||||
return Promise.reject('Unexpected result format');
|
return Promise.reject('Unexpected result format');
|
||||||
};
|
};
|
||||||
|
|
||||||
// update staleTime to 5 minutes
|
|
||||||
const staleTime = 1000 * 60 * 5;
|
|
||||||
|
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryFn,
|
queryFn,
|
||||||
queryKey,
|
queryKey,
|
||||||
initialData,
|
initialData,
|
||||||
staleTime,
|
|
||||||
refetchInterval: false,
|
refetchInterval: false,
|
||||||
refetchOnMount: false,
|
refetchOnMount: false,
|
||||||
refetchOnWindowFocus: false,
|
refetchOnWindowFocus: false,
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ function getClassNameBuilder() {
|
|||||||
},
|
},
|
||||||
selected: {
|
selected: {
|
||||||
true: '',
|
true: '',
|
||||||
false: '',
|
false: 'hidden sm:flex',
|
||||||
},
|
},
|
||||||
complete: {
|
complete: {
|
||||||
true: '',
|
true: '',
|
||||||
|
|||||||
17313
pnpm-lock.yaml
generated
17313
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user