feat: server-action/SWR to trpc/tanstack-query#783
Conversation
|
@BlankParticle is attempting to deploy a commit to the Zero Team on Vercel. A member of the Team first needs to authorize it. |
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the WalkthroughThis change migrates core backend and frontend data-fetching, mutation, and state management logic in the mail application from legacy REST API endpoints and SWR-based hooks to a unified tRPC and React Query architecture. Numerous server-side action files and API route handlers are deleted, with their functionality refactored into tRPC routers and procedures. Corresponding frontend hooks and components are updated to use React Query and tRPC hooks for data retrieval and mutations. Supporting modules are added or modified to provide new query providers, email templates, and utility functions. The update also removes obsolete or redundant code, such as SWR cache providers and direct REST fetchers, consolidating all data operations under the new architecture. Changes
Sequence Diagram(s)sequenceDiagram
participant UI
participant QueryProvider
participant tRPCClient
participant tRPCServer
participant DB
UI->>QueryProvider: Render component
UI->>tRPCClient: useQuery/useMutation (e.g., fetch threads, send email)
tRPCClient->>tRPCServer: tRPC procedure call (e.g., mail.get, mail.send)
tRPCServer->>DB: Query/update database (if needed)
tRPCServer-->>tRPCClient: Return result/error
tRPCClient-->>UI: Update UI state via React Query cache
sequenceDiagram
participant UI
participant tRPCClient
participant AIService
UI->>tRPCClient: useMutation (e.g., generate AI email reply)
tRPCClient->>AIService: Call AI completion API with prompt and context
AIService-->>tRPCClient: Return AI-generated reply
tRPCClient-->>UI: Display generated reply in UI
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 24
🔭 Outside diff range comments (6)
apps/mail/lib/driver/microsoft.ts (2)
379-389: 🛠️ Refactor suggestionFix typing issues in
getUserInfomethodThe
getUserInfomethod has typing issues that are being suppressed with@ts-expect-error. This should be properly addressed before merging.Consider properly typing the
tokensparameter and return value:- // @ts-expect-error, fix types - getUserInfo: async (tokens: IConfig['auth']) => { + getUserInfo: async (tokens: Required<IConfig['auth']>) => { if (!tokens?.access_token) throw new Error('No access token provided'); const client = getClient(tokens.access_token); const user = await client.api('/me').get(); return { address: user.mail || user.userPrincipalName, name: user.displayName, photo: null, }; },
178-232: 💡 Verification agent🧩 Analysis chain
Address incomplete implementation in Microsoft driver's
getmethodThe
getmethod is marked as incomplete with a@ts-expect-errorcomment. This may cause issues when this driver is used in production.Prior to finalizing this PR, ensure the implementation is completed or add a more detailed TODO comment explaining what specifically needs to be fixed. Consider adding proper error handling for production use.
🏁 Script executed:
#!/bin/bash # Find where this method is being used in the codebase rg "microsoft.*\.get\(" --type=ts --type=tsxLength of output: 74
I’ll re-run a broader search without the invalid
--type=tsxflag to locate any references to the Microsoft driver’sgetmethod:
🏁 Script executed:
#!/bin/bash # Search for usage of Microsoft driver’s get() across TS and JS files rg "microsoft.*\.get\(" -g '*.ts' -g '*.js' # Look for imports of the Microsoft driver module rg "import .*microsoft" -g '*.ts' -g '*.js'Length of output: 470
Implement or Properly Annotate the Incomplete Microsoft Driver
getMethodI didn’t find any calls to
microsoftDriver.getin the repo, but the method is still marked with@ts-expect-errorand only returns stubbed data. Before merging:• Remove the
@ts-expect-errorand complete the API call logic (e.g., fetch full conversation, map all message fields)
• Add proper error handling aroundclient.api(...).get()so production failures bubble up meaningfully
• If this endpoint isn’t used yet, add a clearTODOexplaining what remains to be implemented and gate its use behind a feature flag or integration test
• (Optional) Write unit or integration tests to exercise this method once implementedapps/mail/components/create/email-composer.tsx (1)
531-538: 🛠️ Refactor suggestionAvoid creating a DOM node on each keystroke
Editor.onChangeis invoked for every edit, yet it constructs a<div>and performsinnerHTMLparsing each time.
This is (a) expensive for large messages and (b) blocks the main thread.-const tempDiv = document.createElement('div'); -tempDiv.innerHTML = content; -const plainText = tempDiv.textContent || tempDiv.innerText || ''; +const plainText = content.replace(/<[^>]+>/g, ''); // O-regex ≥ 10 × fasterOr, if
contentis already JSON, ask the editor for a text export to avoid any HTML altogether.[performance]
apps/mail/components/create/ai-chat.tsx (1)
240-245: 🛠️ Refactor suggestionTODO: switch file-upload to a tRPC mutation
The comment flags an unimplemented tRPC endpoint while the component still calls the legacy
/api/upload-filesREST route.
Mixing paradigms undermines the goal of the migration and prevents cache-aware progress / error handling.Recommend:
- Add a
uploadFilesmutation intrpc.file.- Replace the
fetchcall withawait uploadFiles.mutateAsync(formData).- Leverage
useMutation’s status flags instead of the ad-hocisLoadingstate.Would you like assistance drafting the new router and hook?
apps/mail/components/create/voice.tsx (1)
110-126: 🛠️ Refactor suggestion
⚠️ Potential issueAdd cleanup to close the ElevenLabs session when the component unmounts
conversation.startSessionkeeps an open WebSocket connection. If the user navigates away without pressing “End Conversation”, the session remains active and the microphone stays reserved, leaking resources.useEffect(() => { // Request microphone permission on component mount @@ requestMicPermission(); + // Cleanup: ensure the session is closed and volume restored + return () => { + if (conversation.status === 'connected') { + conversation.endSession().catch(() => { + /* swallow – we’re unmounting */ + }); + } + }; }, []);apps/mail/components/context/thread-context.tsx (1)
354-363:⚠️ Potential issue
favoriteaction is always disabled – probably unintended
disabled: trueblocks the user from starring messages even though the mutation & toast logic are fully implemented above.- disabled: true, + disabled: false,If starring is unfinished, delete the mutation code to avoid dead paths; otherwise enable the button.
🧹 Nitpick comments (41)
apps/mail/components/ui/input-otp.tsx (1)
38-38: Ensure runtime safety for slot access
The non-null assertion (!) oninputOTPContext.slots[index]!bypasses TypeScript checks but will throw at runtime if the slot isundefined. Confirm thatindexis always within bounds ofslots—otherwise, add a runtime guard to handle missing slots gracefully, for example:const slot = inputOTPContext.slots[index]; if (!slot) { // handle missing slot case (e.g., return null or render placeholder) } const { char, hasFakeCaret, isActive } = slot;apps/mail/components/ui/chart.tsx (1)
134-134: Validate payload item non-null assertion
Theitem!non-null assertions assumeitemis always defined. Although you guard against emptypayloadearlier, consider narrowing the type explicitly or adding a runtime check to avoid potential crashes, e.g.:const [item] = payload!; if (!item) return null; const key = `${labelKey ?? item.dataKey ?? item.name ?? 'value'}`;This makes the safety intent clear and removes the need for
!.apps/mail/components/create/selectors/text-buttons.tsx (3)
52-54: Consider fully removing the commented-out code.The change disables the icon rendering by returning
nullinstead of rendering an icon component whenitem.useImageis false. Since the code is commented out rather than removed, it suggests this might be a temporary change or something to revisit. If this is intentional as part of the transition from server actions to trpc, consider fully removing the commented code for cleaner maintenance.- ) : null - // <item.icon className="h-4 w-4" /> - } + ) : null}
1-1: Remove unused imports.The icons
MessageSquare,FileText, andEditimported from 'lucide-react' are not used in this component. These may have been used previously with the now-commented-out icon rendering logic.-import { MessageSquare, FileText, Edit } from 'lucide-react';
24-32: Consider redefining item type for better type safety.Since you're transitioning to a more strongly-typed approach with trpc, you might want to define a type for the items in the array. Currently, the
item.iconproperty referenced in the commented-out code doesn't appear in the item definition, which would cause TypeScript errors if the code were uncommented.+ type TextButtonItem = { + name: string; + label: string; + action: () => void; + useImage: boolean; + imageSrc?: string; + }; const items = [ { name: 'chat-with-zero', label: 'Chat with Zero', action: handleChatWithAI, useImage: true, imageSrc: '/ai.svg', }, - ]; + ] as TextButtonItem[];apps/mail/lib/email-templates.ts (4)
4-13: Consider Enhancing the<head>for ResponsivenessTo improve rendering across diverse email clients—especially on mobile—consider adding:
<meta name="viewport" content="width=device-width, initial-scale=1.0" />- A simple
<title>tagThis can help clients scale the email correctly and improve accessibility.
14-15: Unify HTML Attribute QuotationThe
<body>tag’sstyleattribute uses single quotes, while other inline attributes use double quotes. For consistency and readability, standardize on double quotes for all HTML attributes.
45-48: Improve Image Rendering StabilityConsider adding explicit
widthandheightattributes to the<img>tag to help email clients reserve layout space before loading:- <img - alt="Zero Early Access" - src="https://i.imgur.com/xBnWSpN.png" + <img + alt="Zero Early Access" + src="https://i.imgur.com/xBnWSpN.png" + width="600" + height="auto" style="width:100%;height:auto;object-fit:cover;border-radius:4px;display:block;outline:none;border:none;text-decoration:none" />
115-117: Use Dynamic Year for MaintainabilityHardcoding the year (
2025) can quickly become outdated. Consider embedding a dynamic expression:${new Date().getFullYear()}within your template to keep this current automatically.
apps/mail/components/ui/page-header.tsx (1)
50-53: Consider properly fixing component types instead of using@ts-expect-errorUsing
@ts-expect-errorcomments to suppress TypeScript errors is a temporary solution that can hide potential issues. These annotations indicate there are type incompatibilities when attaching subcomponents to the PageHeader component.Consider implementing proper TypeScript types for the compound component pattern. A common approach would be:
interface PageHeaderProps extends React.HTMLAttributes<HTMLDivElement> { + Title?: typeof PageHeaderTitle; + Description?: typeof PageHeaderDescription; } const PageHeader = React.forwardRef<HTMLDivElement, PageHeaderProps>( ({ className, children, ...props }, ref) => { // Component implementation } ); - // @ts-expect-error, fix types PageHeader.Title = PageHeaderTitle; - // @ts-expect-error, fix types PageHeader.Description = PageHeaderDescription;apps/mail/lib/brain.ts (1)
1-10: Add error logging and environment variable validationThe function correctly subscribes a connection to the brain service, but there are two improvement opportunities:
- The error case is silently handled, which might make debugging harder
- There's no validation that
process.env.BRAIN_URLexists before using itexport const enableBrainFunction = async (connection: { id: string; providerId: string }) => { + if (!process.env.BRAIN_URL) { + console.warn('BRAIN_URL environment variable is not set'); + return false; + } return await fetch(process.env.BRAIN_URL + `/subscribe/${connection.providerId}`, { body: JSON.stringify({ connectionId: connection.id, }), method: 'PUT', }) .then(() => true) - .catch(() => false); + .catch((error) => { + console.error('Failed to enable brain function:', error); + return false; + }); };apps/mail/lib/resend.ts (1)
1-5: Improve type safety in mock implementationThe mock implementation uses
any[]for arguments, which reduces type safety. Consider creating a more type-safe mock that matches the actual Resend client interface.import { Resend } from 'resend'; export const resend = process.env.RESEND_API_KEY ? new Resend(process.env.RESEND_API_KEY) - : { emails: { send: async (...args: any[]) => console.log(args) } }; + : { + emails: { + send: async (options: { + from: string; + to: string | string[]; + subject: string; + html?: string; + text?: string; + [key: string]: any; + }) => { + console.log('Mock email send:', options); + return { id: 'mock-email-id' }; + } + } + };apps/mail/app/(routes)/settings/labels/page.tsx (2)
57-67: Consider awaiting the toast.promise call for better flow controlThe logic for creating/updating labels is good, but you might want to await the toast.promise call directly rather than catching errors separately.
const onSubmit = async (data: LabelType) => { try { - toast.promise(editingLabel ? updateLabel(editingLabel.id!, data) : createLabel(data), { + await toast.promise(editingLabel ? updateLabel(editingLabel.id!, data) : createLabel(data), { loading: 'Saving label...', success: 'Label saved successfully', error: 'Failed to save label', }); } catch (error) { console.error('Error saving label:', error); } finally { await refetch(); handleClose(); } };
71-76: Same suggestion for delete operation toast promiseSimilarly, consider awaiting the toast.promise call directly for better flow control in the delete operation.
try { - toast.promise(deleteLabel({ id }), { + await toast.promise(deleteLabel({ id }), { loading: 'Deleting label...', success: 'Label deleted successfully', error: 'Failed to delete label', }); }apps/mail/components/create/email-composer.tsx (1)
100-102: Prefer the generatedtrpc.ai.generateBody.useMutation()helperUsing
useMutation(trpc.ai.generateBody.mutationOptions())works, but it duplicates boiler-plate that the codegen already solves.
Switching to the generated hook:-const { mutateAsync: generateEmailBody } = - useMutation(trpc.ai.generateBody.mutationOptions()); +const { mutateAsync: generateEmailBody } = + trpc.ai.generateBody.useMutation();yields identical behaviour while giving you:
- Correct generic types without manually importing
useMutation- Built-in error / loading state wiring
- A terser diff when the procedure signature changes
apps/mail/hooks/use-stats.ts (1)
9-14: Leverage the generated query hook for concisenessReact-Query already wraps the options for you:
-const statsQuery = useQuery( - trpc.mail.count.queryOptions(void 0, { - enabled: !!session?.user.id, - staleTime: 1000 * 60 * 60, // 1 hour - }), -); +const statsQuery = trpc.mail.count.useQuery(undefined, { + enabled: !!session?.user.id, + staleTime: 60 * 60 * 1_000, // 1 h +});Same behaviour, less boiler-plate and one less direct dependency on
@tanstack/react-query.apps/mail/lib/hotkeys/mail-list-hotkeys.tsx (1)
100-102:itemsis unused – trim dependency noise
archiveEmail’s dependency array listsitems, but the callback no longer
references it after the refactor. Retaining it will trigger needless
re-creations every time the list changes.-}, [items, queryClient, t]); +}, [queryClient, t]);apps/mail/app/api/[...all]/route.ts (1)
18-29: Consider adding more detailed error handling for TRPC.The error logging is good, but consider adding more structured error handling with proper error codes and responses to improve client-side error handling capabilities.
.all('/trpc/*', (c) => fetchRequestHandler({ endpoint: '/api/trpc', req: c.req.raw, router: appRouter, createContext: () => ({ c, session: c.var.session, db: c.var.db }), allowMethodOverride: true, onError: (opts) => { console.error('Error in TRPC handler:', opts.error); + // Log additional context about the request + const path = opts.path; + const type = opts.type; + console.error(`Path: ${path}, Type: ${type}`); + + // Return structured error response for better client handling + const statusCode = opts.error.code === 'UNAUTHORIZED' ? 401 : + opts.error.code === 'FORBIDDEN' ? 403 : + opts.error.code === 'NOT_FOUND' ? 404 : 500; + return new Response(JSON.stringify({ + message: opts.error.message, + code: opts.error.code + }), { status: statusCode, headers: { 'content-type': 'application/json' } }); }, }), )apps/mail/components/create/ai-chat.tsx (1)
104-113: Avoid shadowing the outer-scopedatavariableInside
handleSendMessagea new constant calleddatais declared (const data = await generateAISearch…).
Because there is already adatavariable in the upper scope (returned fromuseConnections), the shadowing hinders readability and increases the chance of confusion or accidental misuse.Rename the inner constant to something descriptive, e.g.
aiResponse:- const data = await generateAISearch({ + const aiResponse = await generateAISearch({ messages: [...messages, userMessage], }); … - value: data.searchQuery, + value: aiResponse.searchQuery,apps/mail/app/(routes)/settings/general/page.tsx (1)
120-127: Duplicate “saving” state can be derived from the mutationYou maintain a separate
isSavingflag althoughsaveUserSettingsalready exposesisPending.
Removing the extra state reduces complexity:- const [isSaving, setIsSaving] = useState(false); … - setIsSaving(true); + // no-op … - } finally { - setIsSaving(false); - }and use
isPendingfor button disabling and text.apps/mail/app/(routes)/settings/danger-zone/page.tsx (1)
55-72:await mutateAsync+ lifecycle callbacks is redundantWhen you pass
onSuccess,onError, andonSettledtomutateAsync, the promise returned byawaitresolves after those callbacks; thusawaitadds no benefit and may mislead future readers.Two clearer alternatives:
- Use
mutateAsyncwithout callbacks and handle the resolved value in-line.- Use
mutate(non-async) with callbacks and dropawait.Either approach avoids mixing paradigms.
apps/mail/components/mail/mail-iframe.tsx (1)
30-30: Remove console.log statement before production.This debug statement should be removed as it's not needed in production code.
- console.log(data?.settings);apps/mail/components/create/voice.tsx (2)
14-15: Remove unuseduseTRPCimport to avoid dead code & bundle bloat
useTRPCis imported but never referenced in this component. Tree-shakers may eliminate it, yet keeping the import is a lint error in most configs (eslint-plugin-unused-imports / TypeScriptnoUnusedLocals).-import { trpcClient, useTRPC } from '@/providers/query-provider'; +import { trpcClient } from '@/providers/query-provider';
63-67: Gracefully handle missing sender / recipient names
latestMessage.sender.nameandt.namemay beundefined, yieldingSubject: …\nFrom: undefined.
Guard with a fallback to the email address or"Unknown"to avoid confusing prompts.apps/mail/lib/thread-actions.ts (1)
56-73: Nested ternaries reduce readability – consider a small helperThe triple-nest makes the logic error-prone. A helper like
getRemoveLabel()or a mapping table will be easier to test and maintain.apps/mail/components/ui/nav-user.tsx (3)
17-18: Remove unused/internaldataTagErrorSymbolimport
dataTagErrorSymbolis an internal constant of TanStack Query and is not used in this component. Importing internal symbols couples the code to library internals that can change without notice and produces an unused-import warning.-import { dataTagErrorSymbol, useMutation } from '@tanstack/react-query'; +import { useMutation } from '@tanstack/react-query';
58-60:handleClearCacheno longer clears anythingSince the Indexed-DB cache was removed, this handler now only shows a success toast.
If the intention is to clear React-Query cache (or any other cache layer), callqueryClient.clear()or remove the option entirely to avoid a misleading UX.
63-66: Rename mutation instance to follow JS/TS naming conventions
EnableBrainis a function reference, not a component/class, socamelCase(enableBrain) is preferred. This keeps the codebase consistent and prevents ESLint naming-convention warnings.-const { mutateAsync: EnableBrain } = useMutation(trpc.brain.enableBrain.mutationOptions()); +const { mutateAsync: enableBrain } = useMutation( + trpc.brain.enableBrain.mutationOptions(), +); ... -const enabled = await EnableBrain({}); +const enabled = await enableBrain({});apps/mail/trpc/routes/ai/chat.ts (3)
23-28: Redundant runtime check after schema validationIf the Zod schema ensures every
contentis a non-empty string, theBAD_REQUESTguard becomes unreachable. Either remove the guard or simplify the schema.
53-62: Prefermutation()for costly, non-idempotent AI callsAI completions are compute-heavy, rate-limited, and should not be automatically cached/retried like queries. Consider switching to
.mutation()to signal side-effects and integrate better with React-Query’s invalidation patterns.
64-73: Handle JSON parsing failures explicitlyWhen
JSON.parsefails, the response shape changes from{emailContent, subject, content}to{content}.
Return a discriminated union or include anokflag so callers can reliably branch withoutinchecks.- return { content: completion }; + return { ok: false as const, content: completion }; ... -return { content: completion }; +return { ok: true as const, content: completion };apps/mail/trpc/routes/ai/reply.ts (3)
6-13: Consider using mutation instead of query for content generation.This procedure generates new content but is implemented as a query rather than a mutation. According to React Query and tRPC best practices, queries should be used for data fetching operations, while mutations should be used for operations that create, update, or delete data.
- .query(async ({ input, ctx }) => { + .mutation(async ({ input, ctx }) => {
17-19: Unused content processing comment.The comment indicates content reduction, but the code simply assigns
threadContenttoprocessedContentwithout any processing. Either implement the processing logic or remove the misleading comment.- // Use a more aggressive content reduction approach - const processedContent = threadContent; + const processedContent = threadContent;
72-72: Remove or gate debug logging.Console logs should not be present in production code. Either remove it or gate it behind a debug flag.
- console.log('Generating AI response with prompt:', prompt); + if (process.env.NODE_ENV !== 'production') { + console.log('Generating AI response with prompt:', prompt); + }apps/mail/components/mail/mail.tsx (1)
85-87: Fix optional chaining in the filter callbackConsider using optional chaining when accessing potentially undefined properties.
- const filtered = items.filter( - (item) => item.tags && item.tags.find((i) => i.name === filterTag), - ); + const filtered = items.filter( + (item) => item.tags?.find((i) => i.name === filterTag), + );🧰 Tools
🪛 Biome (1.9.4)
[error] 86-86: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
apps/mail/trpc/routes/ai/email.ts (2)
31-42: Potential duplication in error handling logicThere's some duplication in the error handling logic between the "no response" case and the "missing body field" case. Consider extracting this to a helper function.
+ function createErrorResponse(errorMsg: string) { + return { + content: errorMsg, + jsonContent: createJsonContentFromBody(errorMsg), + type: 'system', + }; + } + // ... if (!response) { console.error( 'AI Action Error (Body): Received no response array item from generateEmailBody', ); const errorMsg = 'AI failed to generate a response.'; - return { - content: errorMsg, - jsonContent: createJsonContentFromBody(errorMsg), - type: 'system', - }; + return createErrorResponse(errorMsg); } // ... if (!responseBody) { console.error('AI Action Error (Body): Missing body field on response'); const errorMsg = 'AI returned an unexpected format.'; - return { - content: errorMsg, - jsonContent: createJsonContentFromBody(errorMsg), - type: 'system', - }; + return createErrorResponse(errorMsg); }Also applies to: 49-59
22-29: Consider adding rate limiting for AI operationsThe procedure calls AI functions directly without any rate limiting. Since AI operations can be expensive and could be abused, consider implementing rate limiting.
You could add a rate limiter in the procedure to prevent excessive use:
import { rateLimit } from '@/lib/rate-limit'; // ...inside the mutation handler const rateLimitResult = await rateLimit.check(ctx.session.user.id); if (!rateLimitResult.success) { return { content: 'You've reached the AI generation limit. Please try again later.', jsonContent: createJsonContentFromBody('You've reached the AI generation limit. Please try again later.'), type: 'system', }; } // Then proceed with AI generationapps/mail/hooks/use-threads.ts (1)
34-44: Thread filtering logic has an unnecessary dependency.The
useMemodependency array includessessionbut this variable isn't used within the memo function. This could cause unnecessary recalculations.- [threadsQuery.data, session, backgroundQueue, isInQueue], + [threadsQuery.data, backgroundQueue, isInQueue],apps/mail/components/mail/mail-list.tsx (2)
411-412: Minor UI style cleanup in Avatar component.The Avatar component's className has been reorganized to place
rounded-fullandborderclasses in a more logical order. This is a minor improvement for code readability.
419-420: Consistent style organization in AvatarImage.Similar to the Avatar component, the AvatarImage className has been reorganized to place the background class after the rounded-full class for consistency.
apps/mail/hooks/use-labels.ts (1)
1-25: Consider reducing stale time for more frequent label updates.The current stale time of 1 hour may be too long for an application where labels could be frequently updated. This could lead to users seeing outdated label data for up to an hour after changes.
Consider reducing the stale time to something shorter, like 5 or 10 minutes, to balance performance with data freshness:
- staleTime: 1000 * 60 * 60, // 1 hour + staleTime: 1000 * 60 * 10, // 10 minutes
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (107)
apps/mail/actions/ai-reply.ts(0 hunks)apps/mail/actions/ai.ts(0 hunks)apps/mail/actions/brain.ts(0 hunks)apps/mail/actions/connections.ts(0 hunks)apps/mail/actions/cookies.ts(0 hunks)apps/mail/actions/drafts.ts(0 hunks)apps/mail/actions/extractText.ts(0 hunks)apps/mail/actions/getSummary.ts(0 hunks)apps/mail/actions/mail.ts(0 hunks)apps/mail/actions/notes.ts(0 hunks)apps/mail/actions/send.ts(0 hunks)apps/mail/actions/settings.ts(0 hunks)apps/mail/actions/user.ts(0 hunks)apps/mail/app/(full-width)/contributors/page.tsx(5 hunks)apps/mail/app/(routes)/layout.tsx(1 hunks)apps/mail/app/(routes)/mail/compose/page.tsx(2 hunks)apps/mail/app/(routes)/settings/connections/page.tsx(3 hunks)apps/mail/app/(routes)/settings/danger-zone/page.tsx(4 hunks)apps/mail/app/(routes)/settings/general/page.tsx(4 hunks)apps/mail/app/(routes)/settings/labels/page.tsx(5 hunks)apps/mail/app/api/[...all]/route.ts(1 hunks)apps/mail/app/api/ai-search/route.ts(0 hunks)apps/mail/app/api/auth/[...all]/route.ts(0 hunks)apps/mail/app/api/auth/early-access/count/route.ts(0 hunks)apps/mail/app/api/auth/early-access/route.ts(0 hunks)apps/mail/app/api/auth/settings/route.ts(0 hunks)apps/mail/app/api/chat/route.ts(0 hunks)apps/mail/app/api/driver/[id]/route.ts(0 hunks)apps/mail/app/api/driver/connections/route.ts(0 hunks)apps/mail/app/api/driver/count/route.ts(0 hunks)apps/mail/app/api/driver/drafts/[id]/route.ts(0 hunks)apps/mail/app/api/driver/notes/route.ts(0 hunks)apps/mail/app/api/driver/route.ts(0 hunks)apps/mail/app/api/golden-ticket/route.ts(0 hunks)apps/mail/app/api/mailto-handler.ts(5 hunks)apps/mail/app/api/notes/index.ts(0 hunks)apps/mail/app/api/notes/types.ts(0 hunks)apps/mail/app/api/utils.ts(0 hunks)apps/mail/app/api/v1/email-aliases/route.ts(0 hunks)apps/mail/app/api/v1/hotkeys/route.ts(1 hunks)apps/mail/app/api/v1/labels/route.ts(0 hunks)apps/mail/app/error.tsx(1 hunks)apps/mail/app/layout.tsx(3 hunks)apps/mail/app/manifest.ts(1 hunks)apps/mail/components/context/thread-context.tsx(8 hunks)apps/mail/components/cookies/toggle.tsx(2 hunks)apps/mail/components/create/ai-assistant.tsx(7 hunks)apps/mail/components/create/ai-chat.tsx(7 hunks)apps/mail/components/create/create-email.tsx(2 hunks)apps/mail/components/create/email-composer.tsx(14 hunks)apps/mail/components/create/selectors/text-buttons.tsx(1 hunks)apps/mail/components/create/voice.tsx(11 hunks)apps/mail/components/golden.tsx(5 hunks)apps/mail/components/home/hero.tsx(5 hunks)apps/mail/components/mail/empty-state.tsx(1 hunks)apps/mail/components/mail/mail-iframe.tsx(2 hunks)apps/mail/components/mail/mail-list.tsx(12 hunks)apps/mail/components/mail/mail-quick-actions.tsx(5 hunks)apps/mail/components/mail/mail.tsx(11 hunks)apps/mail/components/mail/nav-main.tsx(1 hunks)apps/mail/components/mail/note-panel.tsx(12 hunks)apps/mail/components/mail/render-labels.tsx(1 hunks)apps/mail/components/mail/reply-composer.tsx(2 hunks)apps/mail/components/mail/thread-display.tsx(4 hunks)apps/mail/components/ui/animated-number.tsx(1 hunks)apps/mail/components/ui/chart.tsx(1 hunks)apps/mail/components/ui/input-otp.tsx(1 hunks)apps/mail/components/ui/nav-main.tsx(4 hunks)apps/mail/components/ui/nav-user.tsx(5 hunks)apps/mail/components/ui/page-header.tsx(1 hunks)apps/mail/hooks/use-ai-search.ts(0 hunks)apps/mail/hooks/use-connections.ts(1 hunks)apps/mail/hooks/use-contacts.ts(0 hunks)apps/mail/hooks/use-drafts.ts(1 hunks)apps/mail/hooks/use-email-aliases.ts(1 hunks)apps/mail/hooks/use-labels.ts(1 hunks)apps/mail/hooks/use-mail-navigation.ts(3 hunks)apps/mail/hooks/use-notes.tsx(1 hunks)apps/mail/hooks/use-settings.ts(1 hunks)apps/mail/hooks/use-stats.ts(1 hunks)apps/mail/hooks/use-summary.ts(1 hunks)apps/mail/hooks/use-threads.ts(1 hunks)apps/mail/lib/auth-client.ts(1 hunks)apps/mail/lib/auth.ts(3 hunks)apps/mail/lib/brain.ts(1 hunks)apps/mail/lib/cookies.ts(2 hunks)apps/mail/lib/driver-utils.ts(2 hunks)apps/mail/lib/driver/google.ts(1 hunks)apps/mail/lib/driver/microsoft.ts(3 hunks)apps/mail/lib/driver/types.ts(1 hunks)apps/mail/lib/email-templates.ts(1 hunks)apps/mail/lib/email-utils.client.tsx(2 hunks)apps/mail/lib/hotkeys/mail-list-hotkeys.tsx(4 hunks)apps/mail/lib/idb.ts(1 hunks)apps/mail/lib/notes-manager.ts(7 hunks)apps/mail/lib/notes-utils.ts(4 hunks)apps/mail/lib/resend.ts(1 hunks)apps/mail/lib/thread-actions.ts(4 hunks)apps/mail/lib/utils.ts(0 hunks)apps/mail/package.json(6 hunks)apps/mail/providers/query-provider.tsx(1 hunks)apps/mail/trpc/hono.ts(1 hunks)apps/mail/trpc/index.ts(1 hunks)apps/mail/trpc/routes/ai/chat.ts(1 hunks)apps/mail/trpc/routes/ai/email.ts(1 hunks)apps/mail/trpc/routes/ai/index.ts(1 hunks)apps/mail/trpc/routes/ai/reply.ts(1 hunks)
⛔ Files not processed due to max files limit (17)
- apps/mail/trpc/routes/ai/search-v2.ts
- apps/mail/trpc/routes/ai/search.ts
- apps/mail/trpc/routes/brain/enable.ts
- apps/mail/trpc/routes/brain/index.ts
- apps/mail/trpc/routes/brain/summary.ts
- apps/mail/trpc/routes/connections.ts
- apps/mail/trpc/routes/cookies.ts
- apps/mail/trpc/routes/drafts.ts
- apps/mail/trpc/routes/early-access.ts
- apps/mail/trpc/routes/label.ts
- apps/mail/trpc/routes/mail.ts
- apps/mail/trpc/routes/misc.ts
- apps/mail/trpc/routes/notes.ts
- apps/mail/trpc/routes/settings.ts
- apps/mail/trpc/routes/user.ts
- apps/mail/trpc/trpc.ts
- apps/mail/types/index.ts
💤 Files with no reviewable changes (34)
- apps/mail/lib/utils.ts
- apps/mail/app/api/auth/[...all]/route.ts
- apps/mail/actions/user.ts
- apps/mail/actions/extractText.ts
- apps/mail/app/api/auth/early-access/count/route.ts
- apps/mail/hooks/use-contacts.ts
- apps/mail/app/api/v1/email-aliases/route.ts
- apps/mail/app/api/auth/settings/route.ts
- apps/mail/app/api/driver/route.ts
- apps/mail/actions/brain.ts
- apps/mail/app/api/driver/notes/route.ts
- apps/mail/app/api/notes/types.ts
- apps/mail/app/api/driver/[id]/route.ts
- apps/mail/actions/getSummary.ts
- apps/mail/hooks/use-ai-search.ts
- apps/mail/app/api/driver/connections/route.ts
- apps/mail/app/api/driver/drafts/[id]/route.ts
- apps/mail/app/api/chat/route.ts
- apps/mail/actions/connections.ts
- apps/mail/actions/drafts.ts
- apps/mail/actions/send.ts
- apps/mail/actions/settings.ts
- apps/mail/actions/ai-reply.ts
- apps/mail/app/api/notes/index.ts
- apps/mail/app/api/auth/early-access/route.ts
- apps/mail/actions/cookies.ts
- apps/mail/app/api/golden-ticket/route.ts
- apps/mail/app/api/driver/count/route.ts
- apps/mail/actions/mail.ts
- apps/mail/app/api/ai-search/route.ts
- apps/mail/actions/ai.ts
- apps/mail/actions/notes.ts
- apps/mail/app/api/utils.ts
- apps/mail/app/api/v1/labels/route.ts
🧰 Additional context used
🧬 Code Graph Analysis (25)
apps/mail/app/layout.tsx (1)
apps/mail/providers/query-provider.tsx (1)
QueryProvider(61-72)
apps/mail/lib/email-utils.client.tsx (1)
apps/mail/providers/query-provider.tsx (1)
trpcClient(50-59)
apps/mail/components/ui/nav-main.tsx (1)
apps/mail/hooks/use-labels.ts (1)
useLabels(5-13)
apps/mail/lib/driver-utils.ts (2)
packages/db/src/schema.ts (1)
session(19-30)apps/mail/lib/auth.ts (1)
auth(330-333)
apps/mail/trpc/hono.ts (1)
apps/mail/lib/auth.ts (1)
auth(330-333)
apps/mail/lib/brain.ts (1)
packages/db/src/schema.ts (1)
connection(77-96)
apps/mail/components/mail/empty-state.tsx (1)
apps/mail/hooks/use-connections.ts (1)
useConnections(4-12)
apps/mail/lib/thread-actions.ts (2)
apps/mail/lib/utils.ts (1)
LABELS(21-28)apps/mail/providers/query-provider.tsx (1)
trpcClient(50-59)
apps/mail/components/cookies/toggle.tsx (1)
apps/mail/lib/cookies.ts (1)
CookieCategory(4-4)
apps/mail/trpc/routes/ai/index.ts (5)
apps/mail/trpc/trpc.ts (1)
router(17-17)apps/mail/trpc/routes/ai/reply.ts (1)
generateReply(6-83)apps/mail/trpc/routes/ai/search.ts (1)
generateSearchQuery(4-15)apps/mail/trpc/routes/ai/search-v2.ts (1)
generateSearchQueryEnhanced(25-161)apps/mail/trpc/routes/ai/email.ts (1)
generateEmailSubject(79-100)
apps/mail/app/(routes)/settings/labels/page.tsx (1)
apps/mail/hooks/use-labels.ts (1)
useLabels(5-13)
apps/mail/components/mail/mail-quick-actions.tsx (2)
apps/mail/hooks/use-threads.ts (2)
useThread(60-79)useThreads(11-58)apps/mail/hooks/use-stats.ts (1)
useStats(5-17)
apps/mail/hooks/use-notes.tsx (1)
apps/mail/lib/notes-manager.ts (1)
Note(5-15)
apps/mail/app/(routes)/settings/general/page.tsx (2)
apps/mail/hooks/use-settings.ts (1)
useSettings(5-17)packages/db/src/user_settings_default.ts (1)
userSettingsSchema(13-21)
apps/mail/components/create/voice.tsx (3)
apps/mail/hooks/use-threads.ts (1)
useThreads(11-58)apps/mail/providers/query-provider.tsx (1)
trpcClient(50-59)apps/mail/types/index.ts (1)
Sender(47-50)
apps/mail/components/mail/mail.tsx (2)
apps/mail/hooks/use-threads.ts (1)
useThreads(11-58)apps/mail/lib/utils.ts (1)
cn(53-53)
apps/mail/components/create/ai-chat.tsx (2)
apps/mail/hooks/use-connections.ts (1)
useConnections(4-12)packages/db/src/schema.ts (2)
connection(77-96)session(19-30)
apps/mail/app/(routes)/settings/connections/page.tsx (2)
apps/mail/hooks/use-connections.ts (1)
useConnections(4-12)packages/db/src/schema.ts (1)
connection(77-96)
apps/mail/app/api/mailto-handler.ts (2)
apps/mail/trpc/hono.ts (1)
HonoVariables(4-7)apps/mail/trpc/index.ts (1)
serverTrpc(38-39)
apps/mail/lib/notes-utils.ts (2)
apps/mail/lib/notes-manager.ts (1)
Note(5-15)packages/db/src/schema.ts (1)
note(110-122)
apps/mail/providers/query-provider.tsx (1)
apps/mail/trpc/index.ts (1)
AppRouter(33-33)
apps/mail/components/mail/mail-iframe.tsx (3)
apps/mail/hooks/use-settings.ts (1)
useSettings(5-17)packages/db/src/user_settings_default.ts (1)
defaultUserSettings(3-11)apps/mail/lib/timezones.ts (1)
getBrowserTimezone(2-2)
apps/mail/components/home/hero.tsx (1)
apps/mail/components/ui/animated-number.tsx (1)
AnimatedNumber(13-29)
apps/mail/components/create/email-composer.tsx (1)
apps/mail/components/icons/icons.tsx (1)
Sparkles(355-380)
apps/mail/components/mail/note-panel.tsx (2)
apps/mail/hooks/use-notes.tsx (1)
useThreadNotes(7-27)apps/mail/lib/notes-manager.ts (4)
createNote(34-66)updateNote(68-96)deleteNote(98-112)reorderNotes(114-169)
🪛 Biome (1.9.4)
apps/mail/components/mail/mail.tsx
[error] 86-86: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
🔇 Additional comments (175)
apps/mail/components/ui/animated-number.tsx (1)
19-19: Improved type safety by removing unnecessary type assertion.This change removes the explicit type assertion (
as keyof JSX.IntrinsicElements) and now properly leverages theasprop's type, which is already defined asReact.ElementType. This is a positive improvement because:
- It eliminates redundant type casting while maintaining the same functionality
React.ElementTypeis more flexible, allowing both intrinsic HTML elements and custom React components- It aligns with modern TypeScript best practices by letting the type system work without manual assertions
apps/mail/app/manifest.ts (3)
8-9: Verify the PWAscopeandstart_urlpairing
You’ve setscope: '/'with astart_urlof/mail/inbox, which grants the PWA control over the entire site. If the PWA is meant only for the mail section, consider narrowingscopeto'/mail/'so that navigations outside the mail app aren’t intercepted by the PWA shell.
15-17: Approve consistent string-literal formatting
The migration from double quotes to single quotes forsrc,sizes, andtypevalues is consistent with the project’s style guide and improves readability.Also applies to: 22-24, 29-31
33-33: Approve trailing comma in arrays
Adding a trailing comma after the closing bracket oficonsaligns with the project’s formatting conventions and produces cleaner diffs on future edits.apps/mail/components/create/selectors/text-buttons.tsx (1)
13-22: Placeholder implementation for AI chat functionality.The
handleChatWithAIfunction only logs to the console and doesn't implement the actual functionality. As part of your refactoring from server-actions/SWR to trpc/tanstack-query, consider implementing this using your new data fetching approach.Is the AI chat functionality intended to be implemented in this PR, or is this placeholder intentionally left for future work?
apps/mail/lib/email-templates.ts (2)
1-3: Import and Export Structure Looks GoodThe
dedentimport and theexport const goldenTicketEmaildeclaration follow existing conventions and correctly set up the HTML template.
16-22: Verify Hidden Preview Text CharactersThe hidden preview snippet includes a sequence of zero-width Unicode characters (
). Please confirm these are intentional and render as expected across major email clients, as malformed invisible characters can break the preview text or trigger client quirks.apps/mail/lib/driver/google.ts (1)
3-3: Import path updated to align with new structureThe import path for
deleteActiveConnectionandFatalErrorshas been updated to use@/lib/driver-utilsinstead of the previous path from actions. This change supports the move away from server actions to tRPC.apps/mail/components/mail/render-labels.tsx (1)
8-8: Type import source updated correctlyThe
Labeltype is now imported from@/typesinstead of the previous hooks location, which aligns with the migration from SWR to tRPC and React Query for data management.apps/mail/lib/auth-client.ts (1)
1-3: Import style standardizedThe import statements have been updated to use single quotes consistently, which improves code style standardization across the codebase.
apps/mail/lib/driver/types.ts (1)
2-2: Type import updated for consistencyThe
Labeltype import has been updated to reference@/typesand explicitly uses thetypekeyword, maintaining consistency with other components in the codebase during the migration to tRPC.apps/mail/app/error.tsx (1)
23-32:Details
✅ Verification successful
IndexedDB cache clearing removed as part of migration
The call to
dexieStorageProvider().clear()has been removed from the error handler, which aligns with the broader removal of IndexedDB cache management in favor of React Query's caching system.Since the PR mentions that "the indexed DB cache mechanism used in SWR has not yet been ported to the new version", please verify that removing this cache clearing behavior doesn't lead to stale data issues when users encounter errors and retry.
🏁 Script executed:
#!/bin/bash # Check if any other components still use dexieStorageProvider rg "dexieStorageProvider" --type ts --type tsxLength of output: 74
I’ll re-run the search to cover both .ts and .tsx files explicitly.
🏁 Script executed:
#!/bin/bash # Re-check for any remaining references to dexieStorageProvider in TS and TSX files rg "dexieStorageProvider" --glob '*.ts' --glob '*.tsx' || trueLength of output: 128
No remaining Dexie cache references—removal is safe
I ran a workspace-wide grep for
dexieStorageProviderand only saw its definition inapps/mail/lib/idb.ts; there are no imports or calls elsewhere. Removing the call todexieStorageProvider().clear()in the error handler will not leave any unhandled IndexedDB cache logic in the current code. Feel free to merge.apps/mail/app/layout.tsx (1)
13-13: Well-structured integration of React Query and tRPCThe addition of QueryProvider as a wrapper around the application is a clean implementation that properly establishes React Query and tRPC context throughout the app. This aligns perfectly with the PR objective of transitioning from server actions and SWR to tRPC and tanstack-query.
Also applies to: 57-67
apps/mail/lib/driver/microsoft.ts (1)
3-3: Import path update aligns with refactoring strategyThe change in import path from
@/actions/utilsto@/lib/driver-utilscorrectly supports the broader refactoring from direct action imports to the new architecture.apps/mail/app/(routes)/mail/compose/page.tsx (2)
3-4: Import order change looks good.The import order between
headersandauthhas been swapped, which doesn't affect functionality.
31-31: Redirect URL updated to align with new architecture.The redirect path has been changed from what was likely
/mail/compose/handle-mailtoto/api/mailto-handler, which aligns with the PR's goal of decoupling from NextJS-specific features and moving to a more unified tRPC architecture.apps/mail/lib/cookies.ts (2)
1-1: 'use client' directive is appropriate.Adding the 'use client' directive aligns with the transition from server-side actions to client-side TRPC calls for cookie preferences management.
2-49: Quote style standardization looks good.All string literals have been standardized to use single quotes throughout the file, which improves consistency. The formatting changes to the
CookiePreferencesinterface are also consistent with the codebase style.apps/mail/lib/email-utils.client.tsx (2)
4-4: Import change aligns with tRPC migration.The import has been changed from direct server action import to tRPC client import, which aligns with the PR's goal of transitioning from server actions to tRPC.
43-53: Server action replaced with tRPC mutation.The direct call to
sendEmailserver action has been replaced withtrpcClient.mail.send.mutate, maintaining the same functionality while aligning with the new architecture. The parameters passed remain the same, ensuring consistent behavior.apps/mail/components/ui/nav-main.tsx (3)
17-18: Import structure update looks good.The separation of the
Labeltype import from theuseLabelshook follows good practice for type organization, moving the type to a centralized location.Also applies to: 28-28
66-66: Correctly updated to match React Query pattern.This change properly aligns with the React Query pattern where hooks return the full query object with properties like
data,error, andisLoading.
224-224: Consistent data access update.The change from
labels?.maptodata?.mapaligns with the updated return structure from theuseLabelshook which now returns the full React Query result object.apps/mail/components/mail/nav-main.tsx (3)
1-1: Successful internationalization library migration.The change from
useTranslation(react-i18next) touseTranslations(next-intl) correctly implements the new internationalization approach.Also applies to: 6-6
10-10: Improved code readability.Adding parentheses around the function parameter and conditional expression enhances code clarity without changing functionality.
13-14: Simplified component structure.The JSX return and export statement have been streamlined, improving code readability while maintaining functionality.
Also applies to: 16-16
apps/mail/trpc/hono.ts (1)
1-7: Well-structured type definition for Hono context.The
HonoVariablestype provides strong typing for the Hono context, ensuring type safety when accessing session data and database operations. This supports the decoupling of backend code from NextJS-specific features.apps/mail/lib/driver-utils.ts (2)
2-2: Appropriate reorganization of import path.Moving from
@/app/api/driverto@/lib/driverbetter organizes the code by placing driver creation logic in a more general library location rather than an API-specific one.
33-34: Successfully decoupled from NextJS-specific API.Changing
getActiveDriverto accept aRequestobject parameter instead of usingheaders()fromnext/headersimproves flexibility and decouples the function from NextJS-specific APIs. This aligns with the PR objective of making the backend more separable from the frontend.apps/mail/lib/idb.ts (1)
1-3: Ensure alternative caching strategy is documentedThe removal of IndexedDB caching aligns with the PR description that mentions the SWR cache hasn't been ported yet. This explains why the application might initially feel slower as stated in the PR objectives.
Consider updating the comment to include:
- Expected timeline for reintroduction
- Current fallback caching mechanism
- Impact on performance and offline capabilities
/* -Removed for now, In future more granular cache management will be added along with offline support +Removed as part of migration from SWR to tanstack-query. A new cache implementation with +more granular control and offline support will be added in a future PR. + +Note: Until this is reintroduced, the application may have reduced performance for repeat +queries and limited offline functionality. The current tanstack-query implementation uses +in-memory caching only. */apps/mail/package.json (1)
21-21:Details
❓ Verification inconclusive
Dependencies align well with architectural shift
The added dependencies appropriately support the migration from server actions/SWR to tRPC/tanstack-query. The inclusion of Hono for API routing and supporting libraries like superjson and dedent all fit the new architecture.
Let's verify the latest versions of these critical packages:
Also applies to: 47-47, 58-60, 77-77, 86-86, 113-113, 120-120
🌐 Web query:
What are the latest versions of @tanstack/react-query, @trpc/server, and hono?💡 Result:
Here are the latest versions of the requested libraries as of April 27, 2025:
@tanstack/react-query
- Latest version: 5.74.6
- Release date: April 22, 2025[2]
@trpc/server
- Latest version: 11.1.0
- Release date: Announced March 21, 2025, with continued maintenance updates[6][10]
hono
- Latest version: 4.7.5
- Release date: (Most recent version as of current date)[7]
These are the most up-to-date stable releases for each package based on official sources and package repositories.
Citations:
- 1: https://www.npmjs.com/package/@tanstack/react-query
- 2: https://github.com/TanStack/query/releases
- 3: https://tanstack.com/query/latest/docs/framework/react/installation
- 4: https://tanstack.com/query
- 5: https://tanstack.com/query/latest/docs/framework/react/guides/migrating-to-v5
- 6: https://trpc.io/blog/announcing-trpc-v11
- 7: https://security.snyk.io/package/npm/hono
- 8: https://discord-questions.trpc.io/m/1355819192187621506
- 9: https://www.npmjs.com/package/react-query
- 10: https://github.com/trpc/trpc/releases
🏁 Script executed:
#!/bin/bash # Check current versions of critical dependencies in apps/mail/package.json grep -n '"@tanstack/react-query"' apps/mail/package.json || echo "No @tanstack/react-query dependency found" grep -n '"@trpc/server"' apps/mail/package.json || echo "No @trpc/server dependency found" grep -n '"hono":' apps/mail/package.json || echo "No hono dependency found"Length of output: 253
Please verify dependency versions against the latest stable patches
The added dependencies align well with the tRPC/tanstack-query architectural shift, but a few are not on the most recent patch releases:
• apps/mail/package.json:47 –
@tanstack/react-queryis^5.74.4(latest 5.74.6)
• apps/mail/package.json:59 –@trpc/serveris^11.1.1(latest reported 11.1.0) – please confirm that 11.1.1 is published or adjust to 11.1.0
• apps/mail/package.json:86 –honois^4.7.7(latest 4.7.5)Everything else fits the new architecture. Once you’ve verified these versions, feel free to bump them to the current stable patches.
apps/mail/components/mail/empty-state.tsx (3)
108-108: Destructuring optimization aligns with React Query patternsGood update to destructure only the needed properties from the tRPC query result. This follows best practices with React Query by directly accessing what the component needs.
110-112: Enhanced dependency array with proper optional chainingThe update to use
data?.connectionswith optional chaining is good practice. The dependency array is also correctly updated to watchdata?.connectionsinstead of the previous structure.
115-115: Clean loading state handlingThe simplified reference to
isLoadingmakes the code more readable and maintainable, properly leveraging React Query's standardized return values.apps/mail/hooks/use-summary.ts (1)
1-9: Well-structured migration to tRPC and React QueryThis refactoring cleanly implements the transition from server actions to tRPC. The hook now:
- Properly uses the tRPC client
- Leverages React Query's
useQueryhook with the appropriate options- Returns the full query result object giving components access to loading, error states, etc.
The implementation follows best practices by using the queryOptions pattern from tRPC for type safety.
apps/mail/components/cookies/toggle.tsx (3)
4-6: Good adoption of tRPC and React Query for importsClean import structure that brings in the necessary hooks for the refactoring.
17-20: Well-structured mutation setupGood implementation of the tRPC mutation using the proper patterns. The destructuring of
mutateAsyncmakes the code more readable.
24-24: Updated mutation call signatureThe mutation call is properly updated to use an object parameter structure (
{ category: key, enabled: checked }) which is more maintainable and clear than separate arguments.apps/mail/components/create/create-email.tsx (4)
5-7: Imports updated for TRPC and React Query integrationGood addition of the necessary imports for the new data-fetching architecture using TRPC and React Query.
29-30: Updated hook destructuring for new data patternsCorrectly updated destructuring from data fetching hooks to match the new return structure from TRPC/React Query hooks.
33-34: Implemented TRPC mutation for email sendingGood implementation of the TRPC-based mutation for sending emails. This properly replaces the previous direct server action call with the new mutation pattern.
36-39: Updated reference to connections with proper optional chainingThe updated reference to data.connections with optional chaining is correct and helps prevent potential null reference errors.
apps/mail/trpc/routes/ai/index.ts (2)
1-6: Good modular imports for AI functionalityWell-structured imports that keep the AI functionality modular and organized by importing from separate files.
7-13: Well-organized TRPC router for AI functionalityGreat job creating a centralized TRPC router for AI functionality. This approach:
- Improves type safety
- Centralizes all AI capabilities under a single namespace
- Makes these functions easily accessible throughout the application
- Facilitates the decoupling from NextJS-specific features mentioned in the PR objectives
apps/mail/app/(routes)/settings/labels/page.tsx (5)
22-25: Updated imports for TRPC and React QueryGood update of imports to support the new data-fetching architecture.
39-39: Properly destructured label query with refetch capabilityGood update to the hook usage, now properly destructuring the returned query object to access data, loading state, error, and the refetch function.
48-51: Implemented TRPC mutations for label operationsWell-implemented TRPC mutations for label creation, updating, and deletion operations. This is a clean way to handle these operations with proper type safety.
194-195: Good error handling and null checkingThe error handling has been improved by displaying the actual error message, and the null checking with optional chaining is correctly implemented.
200-200: Proper optional chaining in the map functionGood use of optional chaining in the map function to handle the possibility of undefined labels.
apps/mail/components/home/hero.tsx (5)
4-8: Updated imports for TRPC and React QueryGood update of imports to support the new data-fetching architecture.
29-33: Well-implemented TRPC query and mutation for early access functionalityThe implementation of TRPC query for count data and mutation for registration is clean and effective. Good job setting the initialData to prevent null reference issues before data loads.
47-50: Updated to use TRPC mutation for registrationGood replacement of what was likely a direct API call with the new TRPC mutation.
62-62: Using refetch to update count after registrationGood approach using refetch to update the count after a successful registration rather than manually incrementing a local state variable.
166-175: Updated count display with proper data referenceGood update to reference the count directly from the query data and pass it to the AnimatedNumber component.
apps/mail/components/golden.tsx (3)
12-14: Updated imports to support TRPC with React QueryThe imports have been properly updated to support the migration from server actions to TRPC with React Query for data fetching and mutations.
43-46: Proper TRPC setup for the golden ticket claim mutationThe code correctly initializes TRPC and sets up the mutation for claiming a golden ticket using TRPC's mutation options pattern.
55-75: Refactored form submission to use TRPC mutation with proper error handlingThe form submission has been well-refactored to use a TRPC mutation instead of direct fetch calls. The implementation includes:
- Toast ID tracking for updating the loading toast
- Proper success and error callbacks
- Session refetching and router refresh on success
- Modal closure on successful submission
This is a good implementation of the React Query mutation pattern.
apps/mail/app/(full-width)/contributors/page.tsx (8)
30-30: Updated import for React QueryCorrectly imported useQuery from @tanstack/react-query to replace SWR.
58-64: Improved formatting for excludedUsernames arrayThe array has been reformatted for better readability while maintaining the same content.
68-71: Enhanced type declaration for specialRolesThe type declaration for specialRoles has been improved for better readability and maintainability.
144-150: Replaced SWR with useQuery for initial contributorsCorrectly implemented React Query's useQuery hook to fetch the initial contributors from GitHub API.
152-159: Smart conditional query for additional contributorsWell-implemented conditional query that only runs when the initial contributors count equals 100, suggesting there might be more pages to fetch.
171-174: Replaced SWR with useQuery for repository dataAll GitHub API requests have been standardized to use React Query with appropriate query keys and functions.
Also applies to: 176-182, 184-190
192-206: Refactored useMemo implementation for filteredCoreTeamThe useMemo implementation has been refactored to a more concise arrow function style while preserving the same filtering and sorting logic.
855-855: Fixed spacing in Avatar classNameMinor formatting fix to ensure consistent className formatting.
apps/mail/hooks/use-connections.ts (2)
1-2: Updated imports for TRPC and React QueryCorrectly replaced the old SWR imports with TRPC and React Query imports.
4-12: Refactored useConnections hook to use TRPC with React QueryThe hook has been completely refactored to:
- Initialize TRPC client
- Use React Query's useQuery with TRPC's connection list query
- Set an infinite stale time to prevent unnecessary refetches
- Return the full query object instead of just the data
This implementation aligns with the architectural shift from SWR to React Query and provides consumers with access to all React Query features like refetching, loading states, and error handling.
apps/mail/components/mail/mail-quick-actions.tsx (5)
6-8: Updated imports for TRPC and React QueryCorrectly added imports for TRPC client and React Query's useMutation hook.
36-39: Setup TRPC mutations for mail operationsProperly initialized TRPC client and created mutations for markAsRead and markAsUnread operations using TRPC's mutation options pattern.
40-45: Updated hook usage for refetch capabilitiesProperly destructured the query results to access refetch methods from thread, threads, and stats queries, which is essential for refreshing data after mutations.
82-82: Optimized cache refresh with Promise.allEfficiently refreshes both thread list and stats data in parallel after an archive operation using Promise.all.
119-149: Replaced direct function calls with TRPC mutationsEffectively replaced direct markAsRead/markAsUnread function calls with TRPC mutations that include proper success, error, and settled callbacks.
The implementation provides:
- Better type safety through TRPC
- Improved error handling with specific error messages
- Proper UI feedback via toast notifications
- Automatic thread data refreshing on success
apps/mail/app/api/[...all]/route.ts (2)
1-32: LGTM: Well-structured route handler consolidation.This new route handler effectively consolidates multiple API routes into a single unified handler using Hono. The implementation properly extracts user sessions, provides authentication routes, TRPC integration, and mail handler functionality.
The clean organization with middleware for session and database handling will simplify maintenance and ensure consistent behavior across endpoints.
32-42: LGTM: Uniform HTTP method handling approach.Exporting the same handler for all HTTP methods is elegant and ensures consistent behavior. This approach also aligns well with the goal of decoupling from NextJS-specific features.
apps/mail/hooks/use-email-aliases.ts (2)
1-13: LGTM: Successfully migrated from SWR to React Query.The hook has been successfully migrated from
useSWRImmutableto React Query'suseQuery. The implementation properly sets up a 1-hour stale time and provides appropriate initial data.The return type change (returning the full query object) gives consumers more flexibility to access additional React Query features like
isRefetching,refetch, etc.
6-10:Details
✅ Verification successful
Verify clients consuming this hook can handle the changed return type.
The hook now returns the full React Query object instead of the previous custom object with
aliases,isLoading, etc. This change in return type might break components that expect the previous structure.
🏁 Script executed:
#!/bin/bash # Search for components using useEmailAliases echo "Searching for components using useEmailAliases..." rg -A 3 -B 3 "useEmailAliases\(\)" --type tsx --type jsxLength of output: 195
Let's retry the search without relying on built-in
rgfile types—use globs instead to catch all.tsx/.jsxusages:
🏁 Script executed:
#!/bin/bash echo "Searching components for useEmailAliases usage (TSX/JSX globs)…" rg -n -A3 -B3 "useEmailAliases(" -g "*.tsx" -g "*.jsx"Length of output: 276
Let’s search both the imports and the literal hook calls across all TS/JS files:
🏁 Script executed:
#!/bin/bash echo "Searching for imports of useEmailAliases in .ts/.tsx/.js/.jsx files..." rg -n "import.*useEmailAliases" -g "*.ts" -g "*.tsx" -g "*.js" -g "*.jsx" echo echo "Searching for literal calls of useEmailAliases(...) in .ts/.tsx/.js/.jsx files..." rg -F -n -A3 -B3 "useEmailAliases(" -g "*.ts" -g "*.tsx" -g "*.js" -g "*.jsx"Length of output: 2597
Verified safe usage of useEmailAliases return shape
I checked all imports and calls of useEmailAliases – it’s only used in:
- apps/mail/components/mail/reply-composer.tsx (line 28)
- apps/mail/components/create/create-email.tsx (line 30)
In both places the hook’s return value is destructured as
const { data: aliases, isLoading: isLoadingAliases } = useEmailAliases();which matches the new React Query object shape (
dataandisLoading). No components rely on the old custom{ aliases, … }return type, so this change won’t break any existing consumers.apps/mail/components/create/ai-assistant.tsx (6)
1-14: LGTM: Updated imports for TRPC and React Query integration.The import statements have been updated to use the new TRPC and React Query hooks, which aligns with the architecture changes described in the PR.
209-214: LGTM: Successfully migrated to TRPC mutations for AI operations.The component now properly uses TRPC mutation hooks for email body and subject generation, making it compatible with the new architecture.
287-294: Improved typed request structure for generating email body.The function call now uses a properly structured object for the request parameters, providing better type safety.
321-322: Simplified API call for subject generation.The subject generation mutation call has been simplified, improving code readability.
335-338: Improved error handling and reporting for empty content.The error handling has been enhanced with more explicit error messages and proper error types, which will make debugging easier.
343-357: Well-structured error handling with appropriate user feedback.The improved error handling now properly differentiates between specific error types and provides appropriate user feedback through toast notifications. This enhancement will improve the user experience when errors occur.
apps/mail/lib/auth.ts (5)
16-18: LGTM: Updated imports for better modularity.The import changes support better modularity by separating brain functionality into its own module and updating the driver utilities import path.
21-21: LGTM: Simplified resend email client import.The simplified import of the resend email client from a dedicated module improves modularity and separation of concerns.
87-88: Added proper request validation in deleteUser hook.The updated hook now correctly validates the request object and passes it to getActiveDriver, aligning with the function's updated signature.
274-274: Simplified brain enablement function call.The brain enablement function call has been simplified with a flatter object structure, making the code more readable and maintainable.
87-101: Verify that user account deletion works correctly with the updated driver.The user deletion flow has been modified to use the new getActiveDriver signature. Ensure that this critical functionality works correctly after these changes.
Given the changes to how active drivers are obtained, this is a critical flow to test as it involves revoking tokens and deleting user data.
apps/mail/components/mail/mail-iframe.tsx (2)
23-24: LGTM: Good use of tRPC mutations.The implementation correctly uses React Query's mutation hook with tRPC, which provides better type safety and consistency compared to direct action imports.
37-42: LGTM: Proper handling of trustedSenders array.The code correctly handles the case where trustedSenders might be undefined by using optional chaining and providing a fallback.
apps/mail/hooks/use-drafts.ts (1)
8-14: LGTM: Good query configuration.The implementation correctly:
- Uses proper typing with tRPC
- Enables the query only when both session and draft ID exist
- Sets a reasonable stale time of 1 hour for draft data
This is an improvement over the previous SWR implementation.
apps/mail/app/(routes)/settings/connections/page.tsx (3)
29-29: LGTM: Good use of explicit renaming.Explicitly renaming
refetchtorefetchConnectionsimproves code readability by making the purpose of the function clear.
36-49: LGTM: Improved error handling and user feedback.The implementation correctly:
- Uses the tRPC mutation hook
- Provides proper error handling with console logging and user notification
- Shows success messages to the user
- Refreshes both connections and session data to ensure UI consistency
This is more robust than the previous implementation.
76-78: LGTM: Consistent data structure access.The code correctly updates references to connection data to use the new data structure from React Query.
apps/mail/hooks/use-settings.ts (2)
9-14: Consider the implications of Infinity staleTime.Setting
staleTime: Infinitymeans the data will never become stale automatically, which is appropriate for relatively static user settings. However, be aware that you'll need to manually trigger a refetch whenever settings change in other parts of the application.Do you have a strategy for ensuring settings are refreshed when modified in other components or contexts? For example, when a different component changes user settings, how will this be reflected where
useSettingsis used?
16-16: LGTM: Returning the full query object provides flexibility.Returning the complete React Query result object gives consumers access to additional useful properties like
isLoading,isError,error, andrefetch, making this hook more versatile.apps/mail/components/create/voice.tsx (1)
52-83: Avoid N×M re-fetch storm by memoising already-downloaded threadsEvery change to
threadsre-runsfetchThreadContents, re-querying the same 20 IDs (even if none changed), hammering the API and slowing the UI. Cache thread IDs you have already fetched or comparethreadslength/IDs before re-running.Pseudo-fix:
- useEffect(() => { - if (!threads || threads.length === 0) return; + const fetchedRef = useRef<Set<string>>(new Set()); + + useEffect(() => { + if (!threads?.length) return; ... - threads.slice(0, 20).map(async (thread) => { + threads + .slice(0, 20) + .filter((t) => !fetchedRef.current.has(t.id)) + .map(async (thread) => { ... + fetchedRef.current.add(thread.id); }),[ suggest_optional_refactor ]
[ request_verification ]apps/mail/lib/thread-actions.ts (1)
91-95:Details
❌ Incorrect review comment
Parameter name mismatch may break the TRPC call
modifyLabelsexpects (per previous API)threadIds: string[], but the call supplies a singular keythreadId. If the router still expects the plural form the mutation will fail at runtime.- return trpcClient.mail.modifyLabels.mutate({ - threadId: threadIds, + return trpcClient.mail.modifyLabels.mutate({ + threadIds, addLabels: addLabel ? [addLabel] : [], removeLabels: removeLabel ? [removeLabel] : [], });Please verify the server-side procedure signature.
🏁 Script executed:
#!/bin/bash set -e rg --color=never -n "modifyLabels" .Length of output: 608
🏁 Script executed:
#!/bin/bash set -e rg -n -A5 -B5 "modifyLabels" apps/mail/trpc/routes/mail.tsLength of output: 1181
No change needed: client is already using the correct
threadIdparameterThe server-side procedure in
apps/mail/trpc/routes/mail.tsdeclares:.input( z.object({ threadId: z.string().array(), addLabels: z.string().array().optional().default([]), removeLabels: z.string().array().optional().default([]), }) )…so calling
trpcClient.mail.modifyLabels.mutate({ threadId: threadIds, addLabels: addLabel ? [addLabel] : [], removeLabels: removeLabel ? [removeLabel] : [], });matches the expected schema. The suggested rename to
threadIdswould break the call.Likely an incorrect or invalid review comment.
apps/mail/components/context/thread-context.tsx (1)
132-141: Background-queue IDs include thethread:prefix – ensure backend expects it
targetsare built asthread:${id}, but other TRPC calls (e.g.,deleteThread) pass raw IDs. Mixing formats may break analytics or cache keys.Confirm the expected identifier format and normalise across the codebase.
apps/mail/trpc/index.ts (1)
38-40: LGTM – cleanserverTrpchelperNeat helper; context typing is correct and keeps the caller construction DRY. 👍
apps/mail/hooks/use-notes.tsx (3)
1-5: Clean import structuring.The imports have been properly reorganized to accommodate the new React Query and tRPC architecture, replacing the previous SWR-based approach.
10-24: Well-implemented React Query transition with appropriate configurations.The migration from SWR to React Query with tRPC is well structured. The query configuration includes:
- Conditional enabling based on session and threadId presence
- A reasonable 5-minute stale time for caching notes data
- Proper error handling via the meta customError property
- Appropriate initial data to prevent UI flickers
26-26: Return value is now more feature-rich.The hook now returns the full React Query result object instead of a custom return structure, providing access to additional query state flags and functions like
isLoading,isFetching,refetch, etc.apps/mail/components/mail/reply-composer.tsx (4)
6-8: Clean import updates for new architecture.The imports have been properly updated to support the migration from server actions to tRPC and React Query.
24-31: Correctly migrated hook usage patterns.The component now properly uses the updated hook patterns:
- Destructuring
refetchfromuseThreadinstead ofmutate- Using the full data response pattern with
data: aliasesfromuseEmailAliases- Setting up tRPC and the email sending mutation correctly
138-157: Email sending mutation properly configured.The email sending functionality has been successfully migrated to use the tRPC mutation while maintaining all the necessary parameters and headers for proper email threading.
161-161: Thread refetching correctly updated.The code now uses
await refetch()instead of the previous SWRmutate()call, properly aligning with the React Query paradigm.apps/mail/lib/notes-manager.ts (7)
1-15: Well-structured Note interface definition.The Note interface is now explicitly defined locally with appropriate types, including allowing
isPinnedto benull. This provides clear type definitions for the module.
17-17: Improved export style.Changed from exporting an interface to a directly implemented object, which is cleaner and more straightforward.
38-40: Consistent parameter defaults.Default values for
colorandisPinnedare now consistently formatted.
71-71: Type-safe parameter using Partial and Omit.The update data parameter is now properly typed using
Partial<Omit<Note, ...>>, ensuring better type safety when updating note properties.
116-116: Extended isPinned type to include null.Updated the type definition to allow
nullvalues forisPinnedin the reorderNotes function, ensuring compatibility with the Note interface.
127-135: Improved SQL query readability.The SQL query construction has been refactored for better readability with proper indentation and structure.
145-168: Well-structured transaction logic.The transaction logic in
reorderNoteshas been refactored for better readability and maintainability:
- Clear separation of transaction steps
- Proper error handling with informative messages
- Conditional property updating for isPinned
apps/mail/components/mail/mail.tsx (5)
226-226: Updated hook usage for React Query compatibilityThe code has been modified to destructure
isLoadingandisFetchingfrom the hook result instead of the previousisLoadingandisValidating. This change aligns with the transition from SWR to React Query (TanStack Query) as mentioned in the PR objectives.
280-281: URL path updated for mailto protocol handlerThe mailto protocol handler path has been changed from NextJS-specific route to a more generic API endpoint (
/api/mailto-handler). This aligns with the PR's goal of decoupling the backend from NextJS-specific features, potentially allowing the backend to be separated from the frontend in the future.
363-363: Improved UI layout for categoriesThe category selection has been moved to its own line with a margin-top, which improves UI spacing and makes the interface more readable.
369-370: Loading indicator now uses isFetching instead of isValidatingThe loading indicator now checks
isFetchinginstead ofisValidatingto determine when to show the loading animation. This is consistent with the React Query (TanStack Query) API naming conventions.
596-597: CSS order updates in category iconsThe conditional class application for category icons has been reworked for consistency across all categories. This improves maintainability by following a consistent pattern.
Also applies to: 609-610, 624-625, 637-638
apps/mail/components/mail/note-panel.tsx (8)
71-77: Updated imports for tRPC and React Query integrationThe component now correctly imports tRPC and React Query hooks to align with the new data fetching architecture. The type import for
Notehas also been updated to reference the appropriate location.
246-250: Updated hook usage pattern for thread notesThe
useThreadNoteshook usage has been refactored to properly destructurenotesfromdataand userefetchfor invalidating the query cache, which follows React Query patterns. This is more explicit than the previous approach and aligns with the overall architectural changes.
264-268: Implemented tRPC mutations for note operationsThe component now uses tRPC mutations via React Query's
useMutationhook for all note operations (create, update, delete, reorder). This provides better type safety and aligns with the new backend architecture.
305-312: Improved note creation state managementThe note creation flow has been improved to properly manage loading state before and after the operation. The state is set to loading before the operation starts and cleared after refetching data, which provides a better user experience by preventing multiple submissions.
342-348: Updated note update mutation with named parametersThe note update mutation now uses a more robust named parameter object pattern instead of positional parameters. This improves code readability and maintainability while reducing the risk of parameter order mistakes.
376-377: Consistent parameter pattern for delete operationThe delete note operation now uses a named parameter object pattern consistent with other mutations. This uniformity improves maintainability and reduces cognitive load when working with different mutations.
394-407: Improved pin toggle implementationThe toggle pin operation has been updated to use named parameters and now returns the refetch promise, allowing for proper sequence control. The toast message is also dynamically updated based on the current pin state.
454-464: Consistent note reordering with proper refetchingBoth pinned and unpinned note reordering operations now use a consistent parameter pattern and properly await the refetch operation. This ensures the UI stays in sync with the backend data.
Also applies to: 475-485
apps/mail/app/api/mailto-handler.ts (5)
1-3: Updated imports for Hono framework integrationThe imports have been updated to use Hono framework types and tRPC, aligning with the new backend architecture that decouples from NextJS-specific features.
82-84: Updated function signature for Hono contextThe
createDraftFromMailtofunction signature has been updated to accept a Hono context, enabling seamless integration with the new backend architecture.
92-103: Improved HTML content generation from plain textThe email body formatter has been enhanced to properly convert plain text to HTML with appropriate paragraph and line break handling. It now:
- Normalizes line breaks to \n
- Splits text into paragraphs
- Replaces line breaks with
tags- Replaces multiple spaces with to preserve whitespace
- Wraps paragraphs in
tags
This provides a much better rendering of plain text emails in HTML editors.
118-119: Migrated to tRPC for draft creationDraft creation now uses the tRPC caller with the Hono context, providing better type safety and aligning with the new backend architecture.
133-155: Updated handler function for Hono integrationThe handler function has been completely refactored to:
- Accept a Hono context
- Check session from context variables
- Access URL parameters using Hono's API
- Use Hono's redirect method for navigation
This maintains the same functionality while aligning with the new backend architecture.
apps/mail/trpc/routes/ai/email.ts (6)
1-5: Appropriate imports for AI email generationThe file correctly imports the necessary functions and types, including the AI generation functions from the lib, tRPC procedures, JSON content type from Novel, and zod for input validation.
6-21: Well-structured tRPC procedure with input validationThe
generateEmailBodyprocedure is properly defined with comprehensive input validation using zod. It accepts all necessary parameters like prompt, current content, subject, recipients, and conversation ID, which provides good flexibility for different use cases.
22-60: Robust error handling and loggingThe email body generation process includes thorough error handling:
- Checks for null/undefined responses
- Provides fallback error messages
- Includes comprehensive logging for debugging
- Handles missing body field cases
This robust approach will help with troubleshooting and ensure users receive appropriate feedback.
68-77: Comprehensive error capturingThe try-catch block properly catches any unhandled exceptions during body generation and returns a user-friendly error message. The error is also logged for debugging purposes.
79-100: Streamlined subject generationThe subject generation procedure is well implemented with:
- Input validation using zod
- Empty body check to prevent unnecessary API calls
- Proper error handling
- Appropriate logging
This provides an efficient way to generate subjects from email bodies.
102-116: Effective JSON content creation helperThe
createJsonContentFromBodyhelper function efficiently converts plain text into a structured JSON format compatible with rich text editors. It includes a fallback message if the input is empty, ensuring the UI always has something to display.apps/mail/providers/query-provider.tsx (6)
1-10: Well-structured imports and initial client configuration.The imports are properly organized and the
makeQueryClientfunction correctly sets up global error handling with toast notifications, while respecting thenoGlobalErrorflag. The query configuration also includes proper performance settings like disabling refetchOnWindowFocus and query retries by default, which aligns with the PR objectives of improving the architecture.
11-30: Query client customization improves user-specific caching.Including the
userIdin the query key hash function is an excellent approach to ensure per-user cache isolation. This addresses one of the issues mentioned in the PR objectives about improper cache keying when handling multiple connections.
32-41: Good singleton pattern implementation for client-side performance.The singleton pattern for browser query client ensures that only one instance is maintained on the client side, which is memory-efficient, while creating new instances per request on the server side to prevent cross-request data leakage.
48-48: Type-safe TRPC context creation.The TRPC context is correctly created with type safety using the AppRouter type, ensuring that all TRPC queries and mutations will have proper type checking.
50-59: TRPC client configuration includes appropriate links and transformers.The TRPC client is configured with logging (always enabled) and HTTP batch link with superjson for data transformation. This setup optimizes network requests through batching and ensures proper serialization/deserialization of complex data types.
61-72: QueryProvider properly composes React Query and TRPC providers.The
QueryProvidercomponent correctly obtains the current user session to create a user-scoped query client, and then wraps children with both QueryClientProvider and TRPCProvider. This composition ensures that all data-fetching throughout the application benefits from both libraries' capabilities.apps/mail/hooks/use-threads.ts (5)
1-5: Updated imports align with the new architecture.The imports have been updated to use React Query and TRPC hooks, replacing the previous SWR-based approach. This change is consistent with the architectural shift described in the PR objectives.
11-32: Infinite query implementation with appropriate configuration.The implementation of
useInfiniteQuerywith TRPC correctly sets up cursor-based pagination. The query configuration includes:
- Setting initial cursor
- Proper extraction of the next page token
- A reasonable stale time (60 seconds)
- Enabling refetchOnMount
This configuration balances data freshness with performance.
47-51: Clear end-of-data detection logic.The
isReachingEndcalculation correctly handles both empty results and missing next page token, ensuring proper pagination behavior in the UI.
52-55: Load more function with fetching guard.The
loadMorefunction correctly prevents concurrent fetching operations by checking the loading and fetching states before triggering the next page fetch.
60-79: Single thread query implementation with appropriate caching.The
useThreadimplementation properly:
- Uses the TRPC query hook
- Conditionally enables the query based on ID and session availability
- Sets a long stale time (12 hours) which is appropriate for thread data that changes infrequently
The return of the full query object gives consumers access to loading states, error information, and refetch capabilities.
apps/mail/components/mail/mail-list.tsx (10)
15-16: Updated imports for component modularity.The imports have been adjusted to use the more specific named imports, which improves code maintainability and bundle size through better tree-shaking.
20-30: Improved hook import organization.The additional import for
useThreadLabelsfrom the labels hook and the import foruseQueryClientfrom React Query show proper organization of dependencies according to functionality.
151-152: Updated hook usage with destructuring pattern.The
useThreadshook usage has been updated to destructure only therefetchfunction from the first element of the returned tuple, which aligns with the new hook implementation.
157-165: Optimized thread prefetching implementation.The prefetching logic has been improved to use React Query's prefetchQuery with TRPC. This approach is more efficient and leverages the React Query cache more effectively than the previous implementation.
516-517: Updated callback to match new refetch pattern.The refreshCallback has been updated to use
refetch()instead of the previous pattern, which aligns with the React Query paradigm.
564-565: Updated destructuring pattern for useThreads hook.The destructuring of the useThreads hook return value has been updated to match the new implementation, correctly extracting the query object with its properties and the additional returned values.
604-605: Updated dependency array in event listener effect.The dependency array for the refresh event listener effect has been updated to include only the refetch function, ensuring the event listener is properly updated when the refetch function changes.
634-637: Improved scroll handling with query state checks.The scroll handler now correctly checks for loading, fetching, and hasNextPage states before triggering the loadMore function, which prevents unnecessary API calls and provides a better user experience.
749-749: Updated pagination condition with hasNextPage.The condition for displaying the "Load More" button has been updated to use the hasNextPage property from React Query, which provides a more accurate representation of whether more data is available.
773-773: Comprehensive loading state handling.The loading indicator now correctly accounts for both initial loading and background fetching states, showing the spinner in either case to provide appropriate feedback to the user.
apps/mail/hooks/use-labels.ts (3)
1-2: Updated imports for the new data fetching approach.The imports have been updated to use React Query and TRPC hooks instead of the previous approach, which is consistent with the architectural changes in the PR.
5-13: Simplified useLabels hook with appropriate caching.The
useLabelshook has been refactored to use TRPC with React Query, setting a one-hour stale time which is reasonable for label data that doesn't change frequently. The hook now returns the full query result object, giving consumers access to loading states, error information, and refetch capabilities.
15-17: Updated useThreadLabels to work with the new hook return type.The
useThreadLabelsfunction has been adjusted to properly destructure the data from the newuseLabelshook return type, maintaining backward compatibility for consumers.apps/mail/lib/notes-utils.ts (7)
1-1: LGTM: Import path updated for the Note type.The import statement has been updated to use a relative path, which is appropriate since the
Notetype is defined in a neighboring file.
5-55: Code formatting improvements applied.The
NOTE_COLORSconstant has been reformatted to use consistent single quotes and better whitespace organization, which improves readability while maintaining the same functionality.
58-66: Consistent string formatting applied.The record definitions for note color translation keys now consistently use single quotes, which aligns with the project's coding style.
73-74: Arrow function parameter parentheses added consistently.The callback functions in
find()methods now consistently use parentheses around parameters, which improves code style consistency.Also applies to: 78-79
90-97: Improved spacing for better readability.Added appropriate line breaks to improve code readability in the formatting functions, which makes the code easier to scan and understand.
Also applies to: 116-123
134-139: Consistent arrow function formatting in sortNotes.The arrow functions and spacing in the
sortNotesfunction have been standardized, improving code readability.
166-190: Improved formatting of updateNotesWithNewOrders function.The parameter format and internal function structure have been improved for better readability while maintaining the same functionality.
apps/mail/components/mail/thread-display.tsx (7)
33-34: Added tRPC and TanStack Query imports.These new imports support the shift from server actions to tRPC and TanStack Query for data fetching and mutations, aligning with the PR's stated objective.
148-149: Updated hook usage to align with new data fetching pattern.The hooks now use
refetchfrom React Query instead ofmutatefrom SWR, properly implementing the migration from SWR to React Query.Also applies to: 155-155
161-162: Implemented tRPC mutation for markAsRead.Correctly sets up the tRPC mutation hook for marking emails as read, replacing the previous server action approach.
214-230: LGTM: Successful migration to tRPC mutation.The effect that marks emails as read has been successfully migrated to use the tRPC mutation with proper error handling and cache invalidation.
276-290: Simplified star toggle implementation.The
handleToggleStarfunction has been cleaned up and reformatted while maintaining the same functionality, correctly using the new data refetching pattern.
401-407: Improved className formatting for better readability.The Star component's conditional class application has been reformatted for better readability while maintaining the same styling logic.
412-414: Improved ternary expression formatting.The conditional expression for the tooltip content has been reformatted to use a more readable multi-line approach.
7a88a10 to
d015b70
Compare
d015b70 to
4132f03
Compare
b9857a6 to
9cdf57f
Compare
9cdf57f to
5082eda
Compare
Current Status: Unstable, needs testing
Known Issues
File uploads in send emails dont work yetTested basic stuff locally, but need a lot more testing, not suitable for merging to main until everything has been to tested for feature parity.
App might feel slow because the indexed db cache used in SWR is not ported to this version yet. We might need to think of a better caching mechanism as caching every request is too aggressive and cause way too many issues while using multiple connections due to the cache not being keyd properly.
These changes might be hard for everyone to adopt to as a contributor, but the experience using server actions and SWR without proper typesafety became frustrating to me and others, motivations and wins due to these changes has been already discussed on discord.
Due to these changes the backend is now decoupled from NextJS specific features like
headers()and in future the backend can be seperated out if needed.If you have any questions, please feel free to ask on discord.
Type of Change
Please delete options that are not relevant.
By submitting this pull request, I confirm that my contribution is made under the terms of the project's license.
Summary by CodeRabbit