From 396c28c99d3df341845f09d35573daabd0148372 Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 1 Jul 2025 14:17:13 -0700 Subject: [PATCH 1/5] Minor updates (#1574) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # READ CAREFULLY THEN REMOVE Remove bullet points that are not relevant. PLEASE REFRAIN FROM USING AI TO WRITE YOUR CODE AND PR DESCRIPTION. IF YOU DO USE AI TO WRITE YOUR CODE PLEASE PROVIDE A DESCRIPTION AND REVIEW IT CAREFULLY. MAKE SURE YOU UNDERSTAND THE CODE YOU ARE SUBMITTING USING AI. - Pull requests that do not follow these guidelines will be closed without review or comment. - If you use AI to write your PR description your pr will be close without review or comment. - If you are unsure about anything, feel free to ask for clarification. ## Description Please provide a clear description of your changes. --- ## Type of Change Please delete options that are not relevant. - [ ] 🐛 Bug fix (non-breaking change which fixes an issue) - [ ] ✨ New feature (non-breaking change which adds functionality) - [ ] 💥 Breaking change (fix or feature with breaking changes) - [ ] 📝 Documentation update - [ ] 🎨 UI/UX improvement - [ ] 🔒 Security enhancement - [ ] ⚡ Performance improvement ## Areas Affected Please check all that apply: - [ ] Email Integration (Gmail, IMAP, etc.) - [ ] User Interface/Experience - [ ] Authentication/Authorization - [ ] Data Storage/Management - [ ] API Endpoints - [ ] Documentation - [ ] Testing Infrastructure - [ ] Development Workflow - [ ] Deployment/Infrastructure ## Testing Done Describe the tests you've done: - [ ] Unit tests added/updated - [ ] Integration tests added/updated - [ ] Manual testing performed - [ ] Cross-browser testing (if UI changes) - [ ] Mobile responsiveness verified (if UI changes) ## Security Considerations For changes involving data or authentication: - [ ] No sensitive data is exposed - [ ] Authentication checks are in place - [ ] Input validation is implemented - [ ] Rate limiting is considered (if applicable) ## Checklist - [ ] I have read the [CONTRIBUTING](https://github.com/Mail-0/Zero/blob/staging/.github/CONTRIBUTING.md) document - [ ] My code follows the project's style guidelines - [ ] I have performed a self-review of my code - [ ] I have commented my code, particularly in complex areas - [ ] I have updated the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix/feature works - [ ] All tests pass locally - [ ] Any dependent changes are merged and published ## Additional Notes Add any other context about the pull request here. ## Screenshots/Recordings Add screenshots or recordings here if applicable. --- _By submitting this pull request, I confirm that my contribution is made under the terms of the project's license._ --- apps/mail/components/mail/mail-list.tsx | 32 ++++++++++++------------- apps/server/wrangler.jsonc | 2 +- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/apps/mail/components/mail/mail-list.tsx b/apps/mail/components/mail/mail-list.tsx index f91b2c5d71..ff53f96e97 100644 --- a/apps/mail/components/mail/mail-list.tsx +++ b/apps/mail/components/mail/mail-list.tsx @@ -79,22 +79,22 @@ const Thread = memo( const [, setActiveReplyId] = useQueryState('activeReplyId'); const [focusedIndex, setFocusedIndex] = useAtom(focusedIndexAtom); - const latestReceivedMessage = useMemo(() => { - if (!getThreadData?.messages) return getThreadData?.latest; - - const nonDraftMessages = getThreadData.messages.filter((msg) => !msg.isDraft); - if (nonDraftMessages.length === 0) return getThreadData?.latest; - - return ( - nonDraftMessages.sort((a, b) => { - const dateA = new Date(a.receivedOn).getTime(); - const dateB = new Date(b.receivedOn).getTime(); - return dateB - dateA; - })[0] || getThreadData?.latest - ); - }, [getThreadData?.messages, getThreadData?.latest]); - - const latestMessage = latestReceivedMessage; + // const latestReceivedMessage = useMemo(() => { + // if (!getThreadData?.messages) return getThreadData?.latest; + + // const nonDraftMessages = getThreadData.messages.filter((msg) => !msg.isDraft); + // if (nonDraftMessages.length === 0) return getThreadData?.latest; + + // return ( + // nonDraftMessages.sort((a, b) => { + // const dateA = new Date(a.receivedOn).getTime(); + // const dateB = new Date(b.receivedOn).getTime(); + // return dateB - dateA; + // })[0] || getThreadData?.latest + // ); + // }, [getThreadData?.messages, getThreadData?.latest]); + + const latestMessage = getThreadData?.latest; const idToUse = useMemo(() => latestMessage?.threadId ?? latestMessage?.id, [latestMessage]); const { data: settingsData } = useSettings(); const queryClient = useQueryClient(); diff --git a/apps/server/wrangler.jsonc b/apps/server/wrangler.jsonc index f65421b09e..87fbeda9b2 100644 --- a/apps/server/wrangler.jsonc +++ b/apps/server/wrangler.jsonc @@ -337,7 +337,7 @@ "hyperdrive": [ { "binding": "HYPERDRIVE", - "id": "27171042364248898fc8672c0fc532a0", + "id": "b1be316b45fb439a9e54b74ecc20aa21", "localConnectionString": "postgresql://postgres:postgres@localhost:5432/zerodotemail", }, ], From 34fa41eca5e87c6dba0559098e7341e0948edec5 Mon Sep 17 00:00:00 2001 From: Ahmet Kilinc Date: Tue, 1 Jul 2025 22:21:24 +0100 Subject: [PATCH 2/5] Add posthog capture for optimistic actions (#1521) # Add PostHog analytics tracking for email actions ## Description Added PostHog event tracking to various email actions to improve analytics and user behavior insights. Events are now captured when users: - Mark emails as read/unread - Star/unstar emails - Move emails between folders - Delete emails - Mark emails as important/not important - Undo actions ## Summary by CodeRabbit * **New Features** * Added event tracking for key email actions, including marking as read/unread, starring/un-starring, moving, deleting, marking as important/unimportant, and undoing actions. This improves visibility into user interactions. --- apps/mail/hooks/use-optimistic-actions.ts | 32 ++++++++++++++++++--- apps/mail/lib/optimistic-actions-manager.ts | 15 ++++++++-- 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/apps/mail/hooks/use-optimistic-actions.ts b/apps/mail/hooks/use-optimistic-actions.ts index 693dff5d28..f4dc08f4fb 100644 --- a/apps/mail/hooks/use-optimistic-actions.ts +++ b/apps/mail/hooks/use-optimistic-actions.ts @@ -10,9 +10,27 @@ import { moveThreadsTo } from '@/lib/thread-actions'; import { useCallback, useRef } from 'react'; import { m } from '@/paraglide/messages'; import { useQueryState } from 'nuqs'; +import posthog from 'posthog-js'; import { useAtom } from 'jotai'; import { toast } from 'sonner'; +enum ActionType { + MOVE = 'MOVE', + STAR = 'STAR', + READ = 'READ', + LABEL = 'LABEL', + IMPORTANT = 'IMPORTANT', +} + +const actionEventNames: Record string> = { + [ActionType.MOVE]: () => 'email_moved', + [ActionType.STAR]: (params) => (params.starred ? 'email_starred' : 'email_unstarred'), + [ActionType.READ]: (params) => (params.read ? 'email_marked_read' : 'email_marked_unread'), + [ActionType.IMPORTANT]: (params) => + params.important ? 'email_marked_important' : 'email_unmarked_important', + [ActionType.LABEL]: (params) => (params.add ? 'email_label_added' : 'email_label_removed'), +}; + export function useOptimisticActions() { const trpc = useTRPC(); const queryClient = useQueryClient(); @@ -66,9 +84,9 @@ export function useOptimisticActions() { toastMessage, folders, }: { - type: 'MOVE' | 'STAR' | 'READ' | 'LABEL' | 'IMPORTANT'; + type: keyof typeof ActionType; threadIds: string[]; - params: any; + params: PendingAction['params']; optimisticId: string; execute: () => Promise; undo: () => void; @@ -92,7 +110,7 @@ export function useOptimisticActions() { optimisticActionsManager.pendingActionsByType.get(type)?.size, ); - const pendingAction: PendingAction = { + const pendingAction = { id: pendingActionId, type, threadIds, @@ -102,7 +120,7 @@ export function useOptimisticActions() { undo, }; - optimisticActionsManager.pendingActions.set(pendingActionId, pendingAction); + optimisticActionsManager.pendingActions.set(pendingActionId, pendingAction as PendingAction); const itemCount = threadIds.length; const bulkActionMessage = itemCount > 1 ? `${toastMessage} (${itemCount} items)` : toastMessage; @@ -116,6 +134,12 @@ export function useOptimisticActions() { pendingActionsRef: optimisticActionsManager.pendingActions.size, typeActions: typeActions?.size, }); + + const eventName = actionEventNames[type]?.(params); + if (eventName) { + posthog.capture(eventName); + } + optimisticActionsManager.pendingActions.delete(pendingActionId); optimisticActionsManager.pendingActionsByType.get(type)?.delete(pendingActionId); if (typeActions?.size === 1) { diff --git a/apps/mail/lib/optimistic-actions-manager.ts b/apps/mail/lib/optimistic-actions-manager.ts index 22262762d8..b1b180307f 100644 --- a/apps/mail/lib/optimistic-actions-manager.ts +++ b/apps/mail/lib/optimistic-actions-manager.ts @@ -1,14 +1,23 @@ -export type PendingAction = { +import type { ThreadDestination } from '@/lib/thread-actions'; + +type BasePendingAction = { id: string; - type: 'MOVE' | 'STAR' | 'READ' | 'LABEL' | 'IMPORTANT'; threadIds: string[]; - params: any; optimisticId: string; execute: () => Promise; undo: () => void; toastId?: string | number; }; +export type PendingAction = BasePendingAction & + ( + | { type: 'MOVE'; params: { currentFolder: string; destination: ThreadDestination } } + | { type: 'STAR'; params: { starred: boolean } } + | { type: 'READ'; params: { read: boolean } } + | { type: 'LABEL'; params: { labelId: string; add: boolean } } + | { type: 'IMPORTANT'; params: { important: boolean } } + ); + class OptimisticActionsManager { pendingActions: Map = new Map(); pendingActionsByType: Map> = new Map(); From 138c367d6b9a106d0145a56effab575176895121 Mon Sep 17 00:00:00 2001 From: amrit Date: Wed, 2 Jul 2025 22:03:13 +0530 Subject: [PATCH 3/5] perf: lower network requests after select all checkbox (#1576) Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> Co-authored-by: nizzy <140507264+nizzyabi@users.noreply.github.com> --- .../components/mail/select-all-checkbox.tsx | 36 +++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/apps/mail/components/mail/select-all-checkbox.tsx b/apps/mail/components/mail/select-all-checkbox.tsx index 7d69802121..d347844fda 100644 --- a/apps/mail/components/mail/select-all-checkbox.tsx +++ b/apps/mail/components/mail/select-all-checkbox.tsx @@ -6,7 +6,7 @@ import { trpcClient } from '@/providers/query-provider'; import { cn } from '@/lib/utils'; import { useParams } from 'react-router'; import { toast } from 'sonner'; -import React, { useCallback, useMemo, useRef, useState } from 'react'; +import React, { useCallback, useMemo, useRef, useState, useEffect } from 'react'; export default function SelectAllCheckbox({ className }: { className?: string }) { const [mail, setMail] = useMail(); @@ -15,6 +15,8 @@ export default function SelectAllCheckbox({ className }: { className?: string }) const { folder = 'inbox' } = useParams<{ folder: string }>() ?? {}; const [isFetchingIds, setIsFetchingIds] = useState(false); + const allIdsCache = useRef(null); + const checkboxRef = useRef(null); const loadedIds = useMemo(() => loadedThreads.map((t) => t.id), [loadedThreads]); @@ -31,7 +33,7 @@ export default function SelectAllCheckbox({ className }: { className?: string }) const fetchAllMatchingThreadIds = useCallback(async (): Promise => { const ids: string[] = []; let cursor = ''; - const MAX_PER_PAGE = 100; + const MAX_PER_PAGE = 500; try { while (true) { @@ -55,7 +57,7 @@ export default function SelectAllCheckbox({ className }: { className?: string }) return ids; }, [folder, query]); - const handleToggle = useCallback(async () => { + const handleToggle = useCallback(() => { if (isFetchingIds) return; if (mail.bulkSelected.length) { @@ -64,21 +66,35 @@ export default function SelectAllCheckbox({ className }: { className?: string }) } setMail((prev) => ({ ...prev, bulkSelected: loadedIds })); - - setIsFetchingIds(true); - const allIds = await fetchAllMatchingThreadIds(); - setIsFetchingIds(false); + toast( `${loadedIds.length} conversation${loadedIds.length !== 1 ? 's' : ''} on this page selected.`, { action: { - label: `Select all ${allIds.length} conversation${allIds.length !== 1 ? 's' : ''}`, - onClick: () => setMail((prev) => ({ ...prev, bulkSelected: allIds })), + label: 'Select all conversations', + onClick: async () => { + try { + if (!allIdsCache.current) { + setIsFetchingIds(true); + allIdsCache.current = await fetchAllMatchingThreadIds(); + setIsFetchingIds(false); + } + const allIds = allIdsCache.current ?? []; + setMail((prev) => ({ ...prev, bulkSelected: allIds })); + } catch (err) { + setIsFetchingIds(false); + toast.error('Failed to select all conversations'); + } + }, }, className: '!w-auto whitespace-nowrap', }, ); - }, [mail.bulkSelected.length, loadedIds, fetchAllMatchingThreadIds, isFetchingIds, setMail]); + }, [isFetchingIds, mail.bulkSelected.length, loadedIds, fetchAllMatchingThreadIds, setMail]); + + useEffect(() => { + allIdsCache.current = null; + }, [folder, query]); return ( Date: Wed, 2 Jul 2025 10:44:48 -0700 Subject: [PATCH 4/5] Temp Fix (#1599) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # READ CAREFULLY THEN REMOVE Remove bullet points that are not relevant. PLEASE REFRAIN FROM USING AI TO WRITE YOUR CODE AND PR DESCRIPTION. IF YOU DO USE AI TO WRITE YOUR CODE PLEASE PROVIDE A DESCRIPTION AND REVIEW IT CAREFULLY. MAKE SURE YOU UNDERSTAND THE CODE YOU ARE SUBMITTING USING AI. - Pull requests that do not follow these guidelines will be closed without review or comment. - If you use AI to write your PR description your pr will be close without review or comment. - If you are unsure about anything, feel free to ask for clarification. ## Description Please provide a clear description of your changes. --- ## Type of Change Please delete options that are not relevant. - [ ] 🐛 Bug fix (non-breaking change which fixes an issue) - [ ] ✨ New feature (non-breaking change which adds functionality) - [ ] 💥 Breaking change (fix or feature with breaking changes) - [ ] 📝 Documentation update - [ ] 🎨 UI/UX improvement - [ ] 🔒 Security enhancement - [ ] ⚡ Performance improvement ## Areas Affected Please check all that apply: - [ ] Email Integration (Gmail, IMAP, etc.) - [ ] User Interface/Experience - [ ] Authentication/Authorization - [ ] Data Storage/Management - [ ] API Endpoints - [ ] Documentation - [ ] Testing Infrastructure - [ ] Development Workflow - [ ] Deployment/Infrastructure ## Testing Done Describe the tests you've done: - [ ] Unit tests added/updated - [ ] Integration tests added/updated - [ ] Manual testing performed - [ ] Cross-browser testing (if UI changes) - [ ] Mobile responsiveness verified (if UI changes) ## Security Considerations For changes involving data or authentication: - [ ] No sensitive data is exposed - [ ] Authentication checks are in place - [ ] Input validation is implemented - [ ] Rate limiting is considered (if applicable) ## Checklist - [ ] I have read the [CONTRIBUTING](https://github.com/Mail-0/Zero/blob/staging/.github/CONTRIBUTING.md) document - [ ] My code follows the project's style guidelines - [ ] I have performed a self-review of my code - [ ] I have commented my code, particularly in complex areas - [ ] I have updated the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix/feature works - [ ] All tests pass locally - [ ] Any dependent changes are merged and published ## Additional Notes Add any other context about the pull request here. ## Screenshots/Recordings Add screenshots or recordings here if applicable. --- _By submitting this pull request, I confirm that my contribution is made under the terms of the project's license._ --- apps/mail/app/root.tsx | 32 ++++++++++++++++++++++-- apps/mail/components/party.tsx | 2 +- apps/mail/providers/query-provider.tsx | 22 ++++++++++------ apps/mail/providers/server-providers.tsx | 7 ++++-- apps/server/src/lib/driver/utils.ts | 2 +- apps/server/src/lib/server-utils.ts | 2 +- 6 files changed, 52 insertions(+), 15 deletions(-) diff --git a/apps/mail/app/root.tsx b/apps/mail/app/root.tsx index 056e1a2f60..22acfdacd1 100644 --- a/apps/mail/app/root.tsx +++ b/apps/mail/app/root.tsx @@ -7,13 +7,15 @@ import { ScrollRestoration, useLoaderData, useNavigate, + type LoaderFunctionArgs, type MetaFunction, } from 'react-router'; import { ServerProviders } from '@/providers/server-providers'; import { ClientProviders } from '@/providers/client-providers'; +import { createTRPCClient, httpBatchLink } from '@trpc/client'; import { useEffect, type PropsWithChildren } from 'react'; import { AlertCircle, Loader2 } from 'lucide-react'; -import { getServerTrpc } from '@/lib/trpc.server'; +import type { AppRouter } from '@zero/server/trpc'; import { Button } from '@/components/ui/button'; import { getLocale } from '@/paraglide/runtime'; import { siteConfig } from '@/lib/site-config'; @@ -21,8 +23,23 @@ import { signOut } from '@/lib/auth-client'; import type { Route } from './+types/root'; import { m } from '@/paraglide/messages'; import { ArrowLeft } from 'lucide-react'; +import superjson from 'superjson'; import './globals.css'; +const getUrl = () => import.meta.env.VITE_PUBLIC_BACKEND_URL + '/api/trpc'; + +export const getServerTrpc = (req: Request) => + createTRPCClient({ + links: [ + httpBatchLink({ + maxItems: 1, + url: getUrl(), + transformer: superjson, + headers: req.headers, + }), + ], + }); + export const meta: MetaFunction = () => { return [ { title: siteConfig.title }, @@ -36,7 +53,18 @@ export const meta: MetaFunction = () => { ]; }; +export async function loader({ request }: LoaderFunctionArgs) { + const trpc = getServerTrpc(request); + const defaultConnection = await trpc.connections.getDefault + .query() + .then((res) => (res?.id as string) ?? null) + .catch(() => null); + return { connectionId: defaultConnection }; +} + export function Layout({ children }: PropsWithChildren) { + const { connectionId } = useLoaderData(); + return ( @@ -52,7 +80,7 @@ export function Layout({ children }: PropsWithChildren) { - + {children} diff --git a/apps/mail/components/party.tsx b/apps/mail/components/party.tsx index b2b152827a..09bcd42e0a 100644 --- a/apps/mail/components/party.tsx +++ b/apps/mail/components/party.tsx @@ -48,7 +48,7 @@ export const NotificationProvider = () => { const { threadIds, type } = JSON.parse(message.data); if (type === IncomingMessageType.Mail_Get) { const { threadId, result } = JSON.parse(message.data); - queryClient.setQueryData(trpc.mail.get.queryKey({ id: threadId }), result); + // queryClient.setQueryData(trpc.mail.get.queryKey({ id: threadId }), result); } } catch (error) { console.error('error parsing party message', error); diff --git a/apps/mail/providers/query-provider.tsx b/apps/mail/providers/query-provider.tsx index c9a8e3c48d..f12803bcab 100644 --- a/apps/mail/providers/query-provider.tsx +++ b/apps/mail/providers/query-provider.tsx @@ -28,7 +28,7 @@ function createIDBPersister(idbValidKey: IDBValidKey = 'zero-query-cache') { } satisfies Persister; } -export const makeQueryClient = () => +export const makeQueryClient = (connectionId: string | null) => new QueryClient({ queryCache: new QueryCache({ onError: (err, { meta }) => { @@ -50,7 +50,7 @@ export const makeQueryClient = () => queries: { retry: false, refetchOnWindowFocus: false, - queryKeyHashFn: (queryKey) => hashKey([{ connectionId: 'default' }, ...queryKey]), + queryKeyHashFn: (queryKey) => hashKey([{ connectionId }, ...queryKey]), gcTime: 1000 * 60 * 60 * 24, }, mutations: { @@ -67,12 +67,12 @@ let browserQueryClient = { activeConnectionId: string | null; }; -const getQueryClient = () => { +const getQueryClient = (connectionId: string | null) => { if (typeof window === 'undefined') { - return makeQueryClient(); + return makeQueryClient(connectionId); } else { if (!browserQueryClient.queryClient) { - browserQueryClient.queryClient = makeQueryClient(); + browserQueryClient.queryClient = makeQueryClient(connectionId); } return browserQueryClient.queryClient; } @@ -103,9 +103,15 @@ export const trpcClient = createTRPCClient({ type TrpcHook = ReturnType; -export function QueryProvider({ children }: PropsWithChildren) { - const persister = useMemo(() => createIDBPersister(`zero-query-cache-default`), []); - const queryClient = useMemo(() => getQueryClient(), []); +export function QueryProvider({ + children, + connectionId, +}: PropsWithChildren<{ connectionId: string | null }>) { + const persister = useMemo( + () => createIDBPersister(`zero-query-cache-${connectionId ?? 'default'}`), + [connectionId], + ); + const queryClient = useMemo(() => getQueryClient(connectionId), [connectionId]); return ( ) { return ( - {children} + {children} ); } diff --git a/apps/server/src/lib/driver/utils.ts b/apps/server/src/lib/driver/utils.ts index 2e54384831..5355196619 100644 --- a/apps/server/src/lib/driver/utils.ts +++ b/apps/server/src/lib/driver/utils.ts @@ -31,7 +31,7 @@ export const getActiveDriver = async () => { if (!activeConnection) throw new Error('Invalid connection'); if (!activeConnection || !activeConnection.accessToken || !activeConnection.refreshToken) - throw new Error('Invalid connection'); + throw new Error(`Invalid connection ${JSON.stringify(activeConnection?.id)}`); return createDriver(activeConnection.providerId, { auth: { diff --git a/apps/server/src/lib/server-utils.ts b/apps/server/src/lib/server-utils.ts index bc4f078081..97662a697f 100644 --- a/apps/server/src/lib/server-utils.ts +++ b/apps/server/src/lib/server-utils.ts @@ -44,7 +44,7 @@ export const getActiveConnection = async () => { export const connectionToDriver = (activeConnection: typeof connection.$inferSelect) => { if (!activeConnection.accessToken || !activeConnection.refreshToken) { - throw new Error('Invalid connection'); + throw new Error(`Invalid connection ${JSON.stringify(activeConnection?.id)}`); } return createDriver(activeConnection.providerId, { From d1781b2029363820b2ee1160134ccb1fa74272d1 Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 2 Jul 2025 10:52:47 -0700 Subject: [PATCH 5/5] Cleanup (#1600) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # READ CAREFULLY THEN REMOVE Remove bullet points that are not relevant. PLEASE REFRAIN FROM USING AI TO WRITE YOUR CODE AND PR DESCRIPTION. IF YOU DO USE AI TO WRITE YOUR CODE PLEASE PROVIDE A DESCRIPTION AND REVIEW IT CAREFULLY. MAKE SURE YOU UNDERSTAND THE CODE YOU ARE SUBMITTING USING AI. - Pull requests that do not follow these guidelines will be closed without review or comment. - If you use AI to write your PR description your pr will be close without review or comment. - If you are unsure about anything, feel free to ask for clarification. ## Description Please provide a clear description of your changes. --- ## Type of Change Please delete options that are not relevant. - [ ] 🐛 Bug fix (non-breaking change which fixes an issue) - [ ] ✨ New feature (non-breaking change which adds functionality) - [ ] 💥 Breaking change (fix or feature with breaking changes) - [ ] 📝 Documentation update - [ ] 🎨 UI/UX improvement - [ ] 🔒 Security enhancement - [ ] ⚡ Performance improvement ## Areas Affected Please check all that apply: - [ ] Email Integration (Gmail, IMAP, etc.) - [ ] User Interface/Experience - [ ] Authentication/Authorization - [ ] Data Storage/Management - [ ] API Endpoints - [ ] Documentation - [ ] Testing Infrastructure - [ ] Development Workflow - [ ] Deployment/Infrastructure ## Testing Done Describe the tests you've done: - [ ] Unit tests added/updated - [ ] Integration tests added/updated - [ ] Manual testing performed - [ ] Cross-browser testing (if UI changes) - [ ] Mobile responsiveness verified (if UI changes) ## Security Considerations For changes involving data or authentication: - [ ] No sensitive data is exposed - [ ] Authentication checks are in place - [ ] Input validation is implemented - [ ] Rate limiting is considered (if applicable) ## Checklist - [ ] I have read the [CONTRIBUTING](https://github.com/Mail-0/Zero/blob/staging/.github/CONTRIBUTING.md) document - [ ] My code follows the project's style guidelines - [ ] I have performed a self-review of my code - [ ] I have commented my code, particularly in complex areas - [ ] I have updated the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix/feature works - [ ] All tests pass locally - [ ] Any dependent changes are merged and published ## Additional Notes Add any other context about the pull request here. ## Screenshots/Recordings Add screenshots or recordings here if applicable. --- _By submitting this pull request, I confirm that my contribution is made under the terms of the project's license._ --- apps/mail/providers/query-provider.tsx | 3 ++- apps/server/src/lib/driver/utils.ts | 20 -------------------- 2 files changed, 2 insertions(+), 21 deletions(-) diff --git a/apps/mail/providers/query-provider.tsx b/apps/mail/providers/query-provider.tsx index f12803bcab..2c0f429955 100644 --- a/apps/mail/providers/query-provider.tsx +++ b/apps/mail/providers/query-provider.tsx @@ -71,8 +71,9 @@ const getQueryClient = (connectionId: string | null) => { if (typeof window === 'undefined') { return makeQueryClient(connectionId); } else { - if (!browserQueryClient.queryClient) { + if (!browserQueryClient.queryClient || browserQueryClient.activeConnectionId !== connectionId) { browserQueryClient.queryClient = makeQueryClient(connectionId); + browserQueryClient.activeConnectionId = connectionId; } return browserQueryClient.queryClient; } diff --git a/apps/server/src/lib/driver/utils.ts b/apps/server/src/lib/driver/utils.ts index 5355196619..16f4b6e5bd 100644 --- a/apps/server/src/lib/driver/utils.ts +++ b/apps/server/src/lib/driver/utils.ts @@ -23,26 +23,6 @@ export const deleteActiveConnection = async () => { } }; -export const getActiveDriver = async () => { - const c = getContext(); - const session = await c.var.auth.api.getSession({ headers: c.req.raw.headers }); - if (!session) throw new Error('Invalid session'); - const activeConnection = await getActiveConnection(); - if (!activeConnection) throw new Error('Invalid connection'); - - if (!activeConnection || !activeConnection.accessToken || !activeConnection.refreshToken) - throw new Error(`Invalid connection ${JSON.stringify(activeConnection?.id)}`); - - return createDriver(activeConnection.providerId, { - auth: { - accessToken: activeConnection.accessToken, - refreshToken: activeConnection.refreshToken, - userId: activeConnection.userId, - email: activeConnection.email, - }, - }); -}; - export const fromBase64Url = (str: string) => str.replace(/-/g, '+').replace(/_/g, '/'); export const fromBinary = (str: string) =>