mirror of https://github.com/buster-so/buster.git
create persister
This commit is contained in:
parent
9b9b0f0bc5
commit
7e5344ccc6
|
@ -2,6 +2,7 @@
|
||||||
"name": "@buster-app/web-tss",
|
"name": "@buster-app/web-tss",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
"version": "0.0.1",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite dev --port 3000",
|
"dev": "vite dev --port 3000",
|
||||||
"dev:fast": "pnpm run build && pnpm run start",
|
"dev:fast": "pnpm run build && pnpm run start",
|
||||||
|
@ -61,14 +62,18 @@
|
||||||
"@tailwindcss/vite": "^4.1.12",
|
"@tailwindcss/vite": "^4.1.12",
|
||||||
"@tanstack/db": "^0.1.3",
|
"@tanstack/db": "^0.1.3",
|
||||||
"@tanstack/match-sorter-utils": "^8.19.4",
|
"@tanstack/match-sorter-utils": "^8.19.4",
|
||||||
|
"@tanstack/query-async-storage-persister": "^5.85.3",
|
||||||
"@tanstack/query-db-collection": "0.1.1",
|
"@tanstack/query-db-collection": "0.1.1",
|
||||||
|
"@tanstack/query-sync-storage-persister": "^5.85.3",
|
||||||
"@tanstack/react-db": "0.1.3",
|
"@tanstack/react-db": "0.1.3",
|
||||||
"@tanstack/react-devtools": "^0.4.0",
|
"@tanstack/react-devtools": "^0.4.0",
|
||||||
"@tanstack/react-form": "^1.19.2",
|
"@tanstack/react-form": "^1.19.2",
|
||||||
"@tanstack/react-query": "^5.85.3",
|
"@tanstack/react-query": "^5.85.3",
|
||||||
"@tanstack/react-query-devtools": "^5.85.3",
|
"@tanstack/react-query-devtools": "^5.85.3",
|
||||||
|
"@tanstack/react-query-persist-client": "^5.85.3",
|
||||||
"@tanstack/react-router": "^1.131.14",
|
"@tanstack/react-router": "^1.131.14",
|
||||||
"@tanstack/react-router-devtools": "^1.131.14",
|
"@tanstack/react-router-devtools": "^1.131.14",
|
||||||
|
"@tanstack/react-router-ssr-query": "^1.131.14",
|
||||||
"@tanstack/react-router-with-query": "^1.130.17",
|
"@tanstack/react-router-with-query": "^1.130.17",
|
||||||
"@tanstack/react-start": "^1.131.15",
|
"@tanstack/react-start": "^1.131.15",
|
||||||
"@tanstack/react-store": "^0.7.3",
|
"@tanstack/react-store": "^0.7.3",
|
||||||
|
|
|
@ -78,42 +78,39 @@ const topItems: ISidebarList = {
|
||||||
const yourStuff: ISidebarGroup = {
|
const yourStuff: ISidebarGroup = {
|
||||||
label: 'Your stuff',
|
label: 'Your stuff',
|
||||||
id: 'your-stuff',
|
id: 'your-stuff',
|
||||||
items: (
|
items: [
|
||||||
[
|
{
|
||||||
{
|
label: 'Metrics',
|
||||||
label: 'Metrics',
|
icon: <ASSET_ICONS.metrics />,
|
||||||
icon: <ASSET_ICONS.metrics />,
|
route: { to: '/app/metrics' },
|
||||||
route: { to: '/app/metrics' },
|
id: '/app/metrics',
|
||||||
id: '/app/metrics',
|
preload: 'intent',
|
||||||
preload: 'intent',
|
preloadDelay: 1000,
|
||||||
preloadDelay: 1000,
|
},
|
||||||
},
|
{
|
||||||
{
|
label: 'Dashboards',
|
||||||
label: 'Dashboards',
|
icon: <ASSET_ICONS.dashboards />,
|
||||||
icon: <ASSET_ICONS.dashboards />,
|
route: { to: '/app/dashboards' },
|
||||||
route: { to: '/app/dashboards' },
|
id: '/app/dashboards/',
|
||||||
id: '/app/dashboards/',
|
preload: 'intent',
|
||||||
preload: 'intent',
|
preloadDelay: 1000,
|
||||||
preloadDelay: 1000,
|
},
|
||||||
},
|
{
|
||||||
{
|
label: 'Collections',
|
||||||
label: 'Collections',
|
icon: <ASSET_ICONS.collections />,
|
||||||
icon: <ASSET_ICONS.collections />,
|
route: { to: '/app/collections' },
|
||||||
route: { to: '/app/collections' },
|
id: '/app/collections/',
|
||||||
id: '/app/collections/',
|
preload: 'intent',
|
||||||
preload: 'intent',
|
preloadDelay: 1000,
|
||||||
preloadDelay: 1000,
|
},
|
||||||
},
|
{
|
||||||
{
|
label: 'Reports',
|
||||||
label: 'Reports',
|
icon: <ASSET_ICONS.reports />,
|
||||||
icon: <ASSET_ICONS.reports />,
|
route: { to: '/app/reports' },
|
||||||
route: { to: '/app/reports' },
|
id: '/app/reports/',
|
||||||
id: '/app/reports/',
|
preload: 'intent',
|
||||||
preload: 'intent',
|
},
|
||||||
show: process.env.NEXT_PUBLIC_ENABLE_REPORTS === 'true',
|
] satisfies (ISidebarItem & { show?: boolean })[],
|
||||||
},
|
|
||||||
] satisfies (ISidebarItem & { show?: boolean })[]
|
|
||||||
).filter((x) => x.show !== false),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const adminTools: ISidebarGroup = {
|
const adminTools: ISidebarGroup = {
|
||||||
|
@ -155,7 +152,10 @@ const tryGroup = (showInvitePeople: boolean): ISidebarGroup => ({
|
||||||
id: 'leave-feedback',
|
id: 'leave-feedback',
|
||||||
onClick: () => toggleContactSupportModal('feedback'),
|
onClick: () => toggleContactSupportModal('feedback'),
|
||||||
},
|
},
|
||||||
].filter((x) => x.show !== false),
|
].reduce((acc, { show, ...item }) => {
|
||||||
|
if (show !== false) acc.push(item);
|
||||||
|
return acc;
|
||||||
|
}, [] as ISidebarItem[]),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const SidebarPrimary = React.memo(() => {
|
export const SidebarPrimary = React.memo(() => {
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
|
import type { QueryClient } from '@tanstack/react-query';
|
||||||
import type React from 'react';
|
import type React from 'react';
|
||||||
import type { PropsWithChildren } from 'react';
|
import type { PropsWithChildren } from 'react';
|
||||||
import { BusterStyleProvider } from './BusterStyles';
|
import { BusterStyleProvider } from './BusterStyles';
|
||||||
|
import { QueryPersister } from './Query/QueryProvider';
|
||||||
import {
|
import {
|
||||||
SupabaseContextProvider,
|
SupabaseContextProvider,
|
||||||
type SupabaseContextType,
|
type SupabaseContextType,
|
||||||
} from './Supabase/SupabaseContextProvider';
|
} from './Supabase/SupabaseContextProvider';
|
||||||
|
|
||||||
// import type { UseSupabaseUserContextType } from '@/lib/supabase';
|
// import type { UseSupabaseUserContextType } from '@/lib/supabase';
|
||||||
// import { BusterAssetsProvider } from './Assets/BusterAssetsProvider';
|
// import { BusterAssetsProvider } from './Assets/BusterAssetsProvider';
|
||||||
// import { AppLayoutProvider } from './BusterAppLayout';
|
// import { AppLayoutProvider } from './BusterAppLayout';
|
||||||
|
@ -21,15 +24,19 @@ import {
|
||||||
// clearLog: false // clears the console per group of renders (default: false)
|
// clearLog: false // clears the console per group of renders (default: false)
|
||||||
// });
|
// });
|
||||||
|
|
||||||
export const RootProviders: React.FC<PropsWithChildren<SupabaseContextType>> = ({
|
type RootProvidersProps = PropsWithChildren<SupabaseContextType & { queryClient: QueryClient }>;
|
||||||
|
|
||||||
|
export const RootProviders: React.FC<RootProvidersProps> = ({
|
||||||
children,
|
children,
|
||||||
user,
|
user,
|
||||||
accessToken,
|
accessToken,
|
||||||
|
queryClient,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<SupabaseContextProvider user={user} accessToken={accessToken}>
|
<QueryPersister queryClient={queryClient}>
|
||||||
<BusterStyleProvider>{children}</BusterStyleProvider>
|
<SupabaseContextProvider user={user} accessToken={accessToken}>
|
||||||
{/* <BusterReactQueryProvider>
|
<BusterStyleProvider>{children}</BusterStyleProvider>
|
||||||
|
{/* <BusterReactQueryProvider>
|
||||||
<HydrationBoundary state={dehydrate(queryClient)}>
|
<HydrationBoundary state={dehydrate(queryClient)}>
|
||||||
<AppLayoutProvider>
|
<AppLayoutProvider>
|
||||||
<BusterUserConfigProvider>
|
<BusterUserConfigProvider>
|
||||||
|
@ -43,7 +50,8 @@ export const RootProviders: React.FC<PropsWithChildren<SupabaseContextType>> = (
|
||||||
</AppLayoutProvider>
|
</AppLayoutProvider>
|
||||||
</HydrationBoundary>
|
</HydrationBoundary>
|
||||||
</BusterReactQueryProvider> */}
|
</BusterReactQueryProvider> */}
|
||||||
</SupabaseContextProvider>
|
</SupabaseContextProvider>
|
||||||
|
</QueryPersister>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
import { isServer, type QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||||
|
import { PersistQueryClientProvider as TanstackPersistQueryClientProvider } from '@tanstack/react-query-persist-client';
|
||||||
|
import React from 'react';
|
||||||
|
import { persistOptions } from '@/integrations/tanstack-query/create-persister';
|
||||||
|
import { userQueryKeys } from '../../api/query_keys/users';
|
||||||
|
|
||||||
|
export const QueryPersister = ({
|
||||||
|
children,
|
||||||
|
queryClient,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
queryClient: QueryClient;
|
||||||
|
}) => {
|
||||||
|
const [mounted, setMounted] = React.useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TanstackPersistQueryClientProvider
|
||||||
|
client={queryClient}
|
||||||
|
persistOptions={persistOptions}
|
||||||
|
onSuccess={() => {
|
||||||
|
setMounted(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{mounted ? children : null}
|
||||||
|
</TanstackPersistQueryClientProvider>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,60 @@
|
||||||
|
import { createAsyncStoragePersister } from '@tanstack/query-async-storage-persister';
|
||||||
|
import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persister';
|
||||||
|
import { hashKey, isServer } from '@tanstack/react-query';
|
||||||
|
import type { PersistQueryClientProviderProps } from '@tanstack/react-query-persist-client';
|
||||||
|
import { dictionariesQueryKeys } from '@/api/query_keys/dictionaries';
|
||||||
|
import { slackQueryKeys } from '@/api/query_keys/slack';
|
||||||
|
import packageJson from '../../../package.json';
|
||||||
|
|
||||||
|
const buster = packageJson.version;
|
||||||
|
export const PERSIST_TIME = 1000 * 60 * 60 * 24 * 3; // 3 days
|
||||||
|
const PERSISTED_QUERIES = [slackQueryKeys.slackGetChannels.queryKey].map(hashKey);
|
||||||
|
|
||||||
|
export const PERMANENT_QUERIES = [
|
||||||
|
dictionariesQueryKeys.getCurrencies.queryKey,
|
||||||
|
dictionariesQueryKeys.colorPalettes.queryKey,
|
||||||
|
].map(hashKey);
|
||||||
|
|
||||||
|
const ALL_PERSISTED_QUERIES = [...PERSISTED_QUERIES, ...PERMANENT_QUERIES];
|
||||||
|
|
||||||
|
const persisterAsync = createAsyncStoragePersister({
|
||||||
|
key: 'buster-query-cache',
|
||||||
|
storage: isServer ? undefined : window.localStorage,
|
||||||
|
throttleTime: 1500, // 1.5 seconds,
|
||||||
|
serialize: (client) => {
|
||||||
|
/*
|
||||||
|
* Make persisted queries appear stale on first load by setting the dataUpdatedAt to 1 (NOT 0)
|
||||||
|
* This way the query will be refetched from the server when it is first mounted AND we
|
||||||
|
* don't have to deal with the flash of stale data that would otherwise happen.
|
||||||
|
*/
|
||||||
|
for (const query of client.clientState.queries) {
|
||||||
|
const isPermanentQuery = PERMANENT_QUERIES.includes(query.queryHash);
|
||||||
|
if (!isPermanentQuery) {
|
||||||
|
console.log('setting dataUpdatedAt to 1', query.queryHash);
|
||||||
|
query.state.dataUpdatedAt = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return JSON.stringify(client);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const persistOptions: PersistQueryClientProviderProps['persistOptions'] = {
|
||||||
|
maxAge: PERSIST_TIME,
|
||||||
|
dehydrateOptions: {
|
||||||
|
shouldDehydrateQuery: (query) => {
|
||||||
|
const isList =
|
||||||
|
query.queryKey[1] === 'list' || query.queryKey[query.queryKey.length - 1] === 'list';
|
||||||
|
|
||||||
|
return isList || ALL_PERSISTED_QUERIES.includes(query.queryHash);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
hydrateOptions: {
|
||||||
|
defaultOptions: {
|
||||||
|
queries: {
|
||||||
|
initialDataUpdatedAt: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
buster,
|
||||||
|
persister: persisterAsync,
|
||||||
|
};
|
|
@ -1,6 +1,8 @@
|
||||||
import type { User } from '@supabase/supabase-js';
|
import type { User } from '@supabase/supabase-js';
|
||||||
import type { QueryClient } from '@tanstack/react-query';
|
import type { QueryClient } from '@tanstack/react-query';
|
||||||
|
import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client';
|
||||||
import { createRouter as createTanstackRouter } from '@tanstack/react-router';
|
import { createRouter as createTanstackRouter } from '@tanstack/react-router';
|
||||||
|
import { setupRouterSsrQueryIntegration } from '@tanstack/react-router-ssr-query';
|
||||||
import { routerWithQueryClient } from '@tanstack/react-router-with-query';
|
import { routerWithQueryClient } from '@tanstack/react-router-with-query';
|
||||||
import * as TanstackQuery from './integrations/tanstack-query/query-client';
|
import * as TanstackQuery from './integrations/tanstack-query/query-client';
|
||||||
import { routeTree } from './routeTree.gen';
|
import { routeTree } from './routeTree.gen';
|
||||||
|
@ -14,7 +16,7 @@ export interface AppRouterContext {
|
||||||
export const createRouter = () => {
|
export const createRouter = () => {
|
||||||
const queryClient = TanstackQuery.getQueryClient();
|
const queryClient = TanstackQuery.getQueryClient();
|
||||||
|
|
||||||
return routerWithQueryClient(
|
const router = routerWithQueryClient(
|
||||||
createTanstackRouter({
|
createTanstackRouter({
|
||||||
routeTree,
|
routeTree,
|
||||||
context: { queryClient, user: null }, //context is defined in the root route
|
context: { queryClient, user: null }, //context is defined in the root route
|
||||||
|
@ -30,6 +32,15 @@ export const createRouter = () => {
|
||||||
}),
|
}),
|
||||||
queryClient
|
queryClient
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// setupRouterSsrQueryIntegration({
|
||||||
|
// router,
|
||||||
|
// queryClient,
|
||||||
|
// // Disable auto-wrapping since we'll handle it ourselves
|
||||||
|
// wrapQueryClient: false,
|
||||||
|
// });
|
||||||
|
|
||||||
|
return router;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Register the router instance for type safety
|
// Register the router instance for type safety
|
||||||
|
|
|
@ -10,16 +10,9 @@ import appCss from '../styles/styles.css?url';
|
||||||
export const Route = createRootRouteWithContext<AppRouterContext>()({
|
export const Route = createRootRouteWithContext<AppRouterContext>()({
|
||||||
head: () => ({
|
head: () => ({
|
||||||
meta: [
|
meta: [
|
||||||
{
|
{ charSet: 'utf-8' },
|
||||||
charSet: 'utf-8',
|
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
|
||||||
},
|
{ title: 'Buster' },
|
||||||
{
|
|
||||||
name: 'viewport',
|
|
||||||
content: 'width=device-width, initial-scale=1',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Buster',
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
links: [{ rel: 'stylesheet', href: appCss }],
|
links: [{ rel: 'stylesheet', href: appCss }],
|
||||||
}),
|
}),
|
||||||
|
@ -33,7 +26,7 @@ export const Route = createRootRouteWithContext<AppRouterContext>()({
|
||||||
});
|
});
|
||||||
|
|
||||||
function RootDocument({ children }: { children: React.ReactNode }) {
|
function RootDocument({ children }: { children: React.ReactNode }) {
|
||||||
const { user, accessToken } = Route.useRouteContext();
|
const { user, accessToken, queryClient } = Route.useRouteContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
@ -41,7 +34,7 @@ function RootDocument({ children }: { children: React.ReactNode }) {
|
||||||
<HeadContent />
|
<HeadContent />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<RootProviders user={user} accessToken={accessToken}>
|
<RootProviders user={user} accessToken={accessToken} queryClient={queryClient}>
|
||||||
{children}
|
{children}
|
||||||
</RootProviders>
|
</RootProviders>
|
||||||
<TanstackDevtools />
|
<TanstackDevtools />
|
||||||
|
|
398
pnpm-lock.yaml
398
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue