mirror of
https://github.com/Mail-0/Zero.git
synced 2026-07-01 08:16:28 +00:00
Merge pull request #884 from BlankParticle/feat/idb-caching
feat: add local query cache for faster load times
This commit is contained in:
@@ -895,6 +895,7 @@ export const MailList = memo(({ isCompact }: MailListProps) => {
|
||||
<div className="flex h-[calc(100vh-4rem)] w-full items-center justify-center">
|
||||
<div className="flex flex-col items-center justify-center gap-2 text-center">
|
||||
<Image
|
||||
suppressHydrationWarning
|
||||
src={resolvedTheme === 'dark' ? '/empty-state.svg' : '/empty-state-light.svg'}
|
||||
alt="Empty Inbox"
|
||||
width={200}
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
'use client';
|
||||
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import {
|
||||
HelpCircle,
|
||||
LogIn,
|
||||
@@ -12,28 +19,20 @@ import {
|
||||
} from 'lucide-react';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||||
import { CircleCheck, ThreeDots } from '../icons/icons';
|
||||
import { SunIcon } from '../icons/animated/sun';
|
||||
import Link from 'next/link';
|
||||
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { Popover, PopoverContent, PopoverTrigger } from './popover';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { usePathname, useSearchParams } from 'next/navigation';
|
||||
import { useConnections } from '@/hooks/use-connections';
|
||||
import { signOut, useSession } from '@/lib/auth-client';
|
||||
import { AddConnectionDialog } from '../connection/add';
|
||||
import { CircleCheck, ThreeDots } from '../icons/icons';
|
||||
import { useTRPC } from '@/providers/query-provider';
|
||||
import { useSidebar } from '@/components/ui/sidebar';
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
import { useBrainState } from '@/hooks/use-summary';
|
||||
import { useBilling } from '@/hooks/use-billing';
|
||||
import { SunIcon } from '../icons/animated/sun';
|
||||
import { clear as idbClear } from 'idb-keyval';
|
||||
import { Gauge } from '@/components/ui/gauge';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useTranslations } from 'next-intl';
|
||||
@@ -43,6 +42,7 @@ import { Progress } from './progress';
|
||||
import { Button } from './button';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { toast } from 'sonner';
|
||||
import Link from 'next/link';
|
||||
|
||||
export function NavUser() {
|
||||
const { data: session, refetch } = useSession();
|
||||
@@ -61,6 +61,7 @@ export function NavUser() {
|
||||
const { chatMessages, brainActivity } = useBilling();
|
||||
const pathname = usePathname();
|
||||
const searchParams = useSearchParams();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const getSettingsHref = useCallback(() => {
|
||||
const category = searchParams.get('category');
|
||||
@@ -71,7 +72,11 @@ export function NavUser() {
|
||||
}, [pathname, searchParams]);
|
||||
|
||||
const handleClearCache = useCallback(async () => {
|
||||
queryClient.clear();
|
||||
await idbClear();
|
||||
toast.success('Cache cleared successfully');
|
||||
// Reload the page after clearing the cache
|
||||
setTimeout(() => window.location.reload(), 500);
|
||||
}, []);
|
||||
|
||||
const handleCopyConnectionId = useCallback(async () => {
|
||||
|
||||
@@ -72,7 +72,7 @@ export const useThread = (threadId: string | null) => {
|
||||
},
|
||||
{
|
||||
enabled: !!id && !!session?.user.id,
|
||||
staleTime: 1000 * 60 * 60 * 12, // 12 hour
|
||||
staleTime: 1000 * 60 * 60, // 60 minutes
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
@@ -8,6 +8,7 @@ export const SIDEBAR_WIDTH_ICON = '3rem';
|
||||
export const SIDEBAR_KEYBOARD_SHORTCUT = 'b';
|
||||
export const BASE_URL = process.env.NEXT_PUBLIC_APP_URL;
|
||||
export const MAX_URL_LENGTH = 2000;
|
||||
export const CACHE_BURST_KEY = 'cache-burst:v0.0.1';
|
||||
|
||||
export const emailProviders = [
|
||||
{
|
||||
|
||||
@@ -55,7 +55,9 @@
|
||||
"@react-email/components": "^0.0.36",
|
||||
"@react-email/html": "^0.0.11",
|
||||
"@react-email/render": "^1.0.6",
|
||||
"@tanstack/query-sync-storage-persister": "^5.75.0",
|
||||
"@tanstack/react-query": "^5.74.4",
|
||||
"@tanstack/react-query-persist-client": "^5.75.2",
|
||||
"@tiptap/core": "2.11.5",
|
||||
"@tiptap/extension-bold": "2.11.5",
|
||||
"@tiptap/extension-document": "2.11.5",
|
||||
@@ -98,6 +100,7 @@
|
||||
"he": "1.2.0",
|
||||
"hono": "^4.7.7",
|
||||
"husky": "9.1.7",
|
||||
"idb-keyval": "^6.2.1",
|
||||
"input-otp": "1.4.2",
|
||||
"jotai": "2.12.1",
|
||||
"jsonrepair": "^3.12.0",
|
||||
|
||||
@@ -1,13 +1,34 @@
|
||||
'use client';
|
||||
import { QueryCache, QueryClient, QueryClientProvider, hashKey } from '@tanstack/react-query';
|
||||
import {
|
||||
PersistQueryClientProvider,
|
||||
type PersistedClient,
|
||||
type Persister,
|
||||
} from '@tanstack/react-query-persist-client';
|
||||
import { createTRPCClient, httpBatchLink, loggerLink } from '@trpc/client';
|
||||
import { QueryCache, QueryClient, hashKey } from '@tanstack/react-query';
|
||||
import { createTRPCContext } from '@trpc/tanstack-react-query';
|
||||
import { useSession, type Session } from '@/lib/auth-client';
|
||||
import { CACHE_BURST_KEY } from '@/lib/constants';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { get, set, del } from 'idb-keyval';
|
||||
import type { AppRouter } from '@/trpc';
|
||||
import superjson from 'superjson';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
function createIDBPersister(idbValidKey: IDBValidKey = 'zero-query-cache') {
|
||||
return {
|
||||
persistClient: async (client: PersistedClient) => {
|
||||
await set(idbValidKey, client);
|
||||
},
|
||||
restoreClient: async () => {
|
||||
return await get<PersistedClient>(idbValidKey);
|
||||
},
|
||||
removeClient: async () => {
|
||||
await del(idbValidKey);
|
||||
},
|
||||
} satisfies Persister;
|
||||
}
|
||||
|
||||
export const makeQueryClient = (session: Session | null) =>
|
||||
new QueryClient({
|
||||
queryCache: new QueryCache({
|
||||
@@ -26,6 +47,7 @@ export const makeQueryClient = (session: Session | null) =>
|
||||
session ? { userId: session.user.id, connectionId: session.connectionId } : undefined,
|
||||
...queryKey,
|
||||
]),
|
||||
gcTime: 1000 * 60 * 60 * 24,
|
||||
},
|
||||
mutations: {
|
||||
onError: (err) => toast.error(err.message),
|
||||
@@ -67,10 +89,17 @@ export function QueryProvider({ children }: PropsWithChildren) {
|
||||
const queryClient = getQueryClient(data ?? null);
|
||||
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<PersistQueryClientProvider
|
||||
client={queryClient}
|
||||
persistOptions={{
|
||||
persister: createIDBPersister(),
|
||||
buster: CACHE_BURST_KEY,
|
||||
maxAge: 1000 * 60 * 60 * 24, // 24 hours
|
||||
}}
|
||||
>
|
||||
<TRPCProvider trpcClient={trpcClient} queryClient={queryClient}>
|
||||
{children}
|
||||
</TRPCProvider>
|
||||
</QueryClientProvider>
|
||||
</PersistQueryClientProvider>
|
||||
);
|
||||
}
|
||||
|
||||
15
bun.lock
15
bun.lock
@@ -63,7 +63,9 @@
|
||||
"@react-email/components": "^0.0.36",
|
||||
"@react-email/html": "^0.0.11",
|
||||
"@react-email/render": "^1.0.6",
|
||||
"@tanstack/query-sync-storage-persister": "^5.75.0",
|
||||
"@tanstack/react-query": "^5.74.4",
|
||||
"@tanstack/react-query-persist-client": "^5.75.2",
|
||||
"@tiptap/core": "2.11.5",
|
||||
"@tiptap/extension-bold": "2.11.5",
|
||||
"@tiptap/extension-document": "2.11.5",
|
||||
@@ -106,6 +108,7 @@
|
||||
"he": "1.2.0",
|
||||
"hono": "^4.7.7",
|
||||
"husky": "9.1.7",
|
||||
"idb-keyval": "^6.2.1",
|
||||
"input-otp": "1.4.2",
|
||||
"jotai": "2.12.1",
|
||||
"jsonrepair": "^3.12.0",
|
||||
@@ -728,10 +731,16 @@
|
||||
|
||||
"@tailwindcss/typography": ["@tailwindcss/typography@0.5.16", "", { "dependencies": { "lodash.castarray": "^4.4.0", "lodash.isplainobject": "^4.0.6", "lodash.merge": "^4.6.2", "postcss-selector-parser": "6.0.10" }, "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" } }, "sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA=="],
|
||||
|
||||
"@tanstack/query-core": ["@tanstack/query-core@5.74.9", "", {}, "sha512-qmjXpWyigDw4SfqdSBy24FzRvpBPXlaSbl92N77lcrL+yvVQLQkf0T6bQNbTxl9IEB/SvVFhhVZoIlQvFnNuuw=="],
|
||||
"@tanstack/query-core": ["@tanstack/query-core@5.75.0", "", {}, "sha512-rk8KQuCdhoRkzjRVF3QxLgAfFUyS0k7+GCQjlGEpEGco+qazJ0eMH6aO1DjDjibH7/ik383nnztua3BG+lOnwg=="],
|
||||
|
||||
"@tanstack/query-persist-client-core": ["@tanstack/query-persist-client-core@5.75.0", "", { "dependencies": { "@tanstack/query-core": "5.75.0" } }, "sha512-/EeURZCen5hw1u4B7s/UbbwrtvNBX/VNWhR8rNtXvXmY9020YtvRd95SCblUVd8kcia4knPM+T+UThskowKmVA=="],
|
||||
|
||||
"@tanstack/query-sync-storage-persister": ["@tanstack/query-sync-storage-persister@5.75.0", "", { "dependencies": { "@tanstack/query-core": "5.75.0", "@tanstack/query-persist-client-core": "5.75.0" } }, "sha512-RudT5n121Rvj6KS8zAtF4dsnuuAfbY/H7XARW/kwqKN5R1yCEajf2oOViFCOSgvjm66aRGS2xpiOM8f98mO5Zg=="],
|
||||
|
||||
"@tanstack/react-query": ["@tanstack/react-query@5.74.9", "", { "dependencies": { "@tanstack/query-core": "5.74.9" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-F8xCXDQRDgsPzLzX9+d6ycNoITAIU2bycc1idZd06bt/GjN1quEJDjHvEDWZGoVn0A/ZmntVrYv6TE0kR7c7LA=="],
|
||||
|
||||
"@tanstack/react-query-persist-client": ["@tanstack/react-query-persist-client@5.75.2", "", { "dependencies": { "@tanstack/query-persist-client-core": "5.75.0" }, "peerDependencies": { "@tanstack/react-query": "^5.75.2", "react": "^18 || ^19" } }, "sha512-XcUKk95LLsHpPFcnE7jP1tXOsjIvM61bvkICmRC+yzg4DAb4aGCO11aqHiBgHfnli5w218DwNXG0RGd731UAUg=="],
|
||||
|
||||
"@tiptap/core": ["@tiptap/core@2.11.5", "", { "peerDependencies": { "@tiptap/pm": "^2.7.0" } }, "sha512-jb0KTdUJaJY53JaN7ooY3XAxHQNoMYti/H6ANo707PsLXVeEqJ9o8+eBup1JU5CuwzrgnDc2dECt2WIGX9f8Jw=="],
|
||||
|
||||
"@tiptap/extension-blockquote": ["@tiptap/extension-blockquote@2.11.7", "", { "peerDependencies": { "@tiptap/core": "^2.7.0" } }, "sha512-liD8kWowl3CcYCG9JQlVx1eSNc/aHlt6JpVsuWvzq6J8APWX693i3+zFqyK2eCDn0k+vW62muhSBe3u09hA3Zw=="],
|
||||
@@ -1498,6 +1507,8 @@
|
||||
|
||||
"iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
|
||||
|
||||
"idb-keyval": ["idb-keyval@6.2.1", "", {}, "sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg=="],
|
||||
|
||||
"ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
||||
|
||||
"import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="],
|
||||
@@ -2740,6 +2751,8 @@
|
||||
|
||||
"@radix-ui/react-visually-hidden/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.0.2", "", { "dependencies": { "@radix-ui/react-slot": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w=="],
|
||||
|
||||
"@tanstack/react-query/@tanstack/query-core": ["@tanstack/query-core@5.74.9", "", {}, "sha512-qmjXpWyigDw4SfqdSBy24FzRvpBPXlaSbl92N77lcrL+yvVQLQkf0T6bQNbTxl9IEB/SvVFhhVZoIlQvFnNuuw=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
|
||||
|
||||
Reference in New Issue
Block a user