Skip to content

Comments

Implement logout functionality and improve error handling across the app#707

Merged
MrgSub merged 2 commits intomainfrom
staging
Apr 17, 2025
Merged

Implement logout functionality and improve error handling across the app#707
MrgSub merged 2 commits intomainfrom
staging

Conversation

@MrgSub
Copy link
Collaborator

@MrgSub MrgSub commented Apr 17, 2025

Summary by CodeRabbit

  • Bug Fixes

    • Improved error handling across multiple endpoints, ensuring users receive empty responses and are logged out on errors instead of being redirected or encountering unhandled issues.
    • Fixed bulk selection logic in mail list hotkeys to consistently use item IDs.
  • New Features

    • Added rate limiting to hotkey-related API endpoints for improved stability and security.
  • Refactor

    • Unified authentication methods and updated internal logic for thread and hotkey handling.
    • Refactored quick actions to use the latest thread data for more accurate state updates.
  • Style

    • Applied code formatting and import reordering for consistency across components.
  • Chores

    • Reduced Google OAuth2 scopes to request only necessary permissions.
    • Disabled shortcut registration logic for hotkeys.

…ous API routes. Refactor rate limiting checks and streamline response handling for unauthorized access.
@vercel
Copy link

vercel bot commented Apr 17, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
0 ✅ Ready (Inspect) Visit Preview 💬 Add feedback Apr 17, 2025 9:52pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Apr 17, 2025

Walkthrough

This set of changes introduces improved error handling and authentication consistency across several API routes by wrapping handlers in try-catch blocks, logging errors, and forcefully logging out users upon errors. The logout process now also deletes the active connection before signing out. Rate limiting is standardized and added to more endpoints. The Google driver reduces its OAuth2 scope and removes retry logic for Gmail API calls. The MailList and MailQuickActions components are refactored, with MailList removing its bulk selection logic and MailQuickActions now fetching thread data via a hook. Several files receive code style and formatting updates, and the useThreads hook is updated to dynamically construct query parameters for pagination.

Changes

File(s) Change Summary
apps/mail/app/api/driver/connections/route.ts,
apps/mail/app/api/driver/count/route.ts,
apps/mail/app/api/driver/notes/route.ts,
apps/mail/app/api/driver/route.ts
Wrapped GET handlers in try-catch blocks for error handling; on error, log the error, call logoutUser, and return an empty JSON response. No change to exported function signatures.
apps/mail/app/api/utils.ts Enhanced logoutUser to also delete the active connection before signing out. Added import for deleteActiveConnection.
apps/mail/app/api/driver/google.ts Reduced OAuth2 scope to only gmail.modify, removed production-only logic, and eliminated retry logic from Gmail API calls.
apps/mail/app/api/v1/hotkeys/route.ts Replaced direct session retrieval with getAuthenticatedUserId(), added rate limiting to all handlers, and updated database operations to use the new user ID source. Removed explicit 401 checks.
apps/mail/components/mail/mail-list.tsx Removed the selectAll callback and changed the dependency array of handleMailClick to an empty array.
apps/mail/components/mail/mail-quick-actions.tsx Refactored to use the useThread hook for fetching thread data, updated callbacks to use thread data, and adjusted dependencies accordingly.
apps/mail/components/mail/thread-display.tsx Applied code style and formatting changes, reordered imports, and made minor syntactic adjustments without altering logic or behavior.
apps/mail/hooks/use-threads.ts Refactored the fetcher in useThreads to dynamically construct query parameters inside the fetcher, supporting pagination tokens.
apps/mail/lib/hotkeys/mail-list-hotkeys.tsx Simplified selectAll callback to always use item.id instead of falling back to threadId.
apps/mail/lib/hotkeys/use-hotkey-utils.ts Commented out the logic in useShortcuts that registered multiple shortcuts, disabling this functionality.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant API_Handler
    participant Auth
    participant DB
    participant Utils

    Client->>API_Handler: Send GET/POST/PUT request
    API_Handler->>Auth: Authenticate user (getAuthenticatedUserId)
    Auth-->>API_Handler: User ID / Error
    API_Handler->>Utils: Apply rate limiting
    Utils-->>API_Handler: Rate limit status
    alt Rate limit exceeded
        API_Handler-->>Client: 429 Too Many Requests
    else Authenticated and allowed
        API_Handler->>DB: Perform requested DB operation
        DB-->>API_Handler: Data/result
        API_Handler-->>Client: Return response
    end
    alt Error occurs
        API_Handler->>Utils: logoutUser (delete connection, sign out)
        Utils-->>API_Handler: Done
        API_Handler-->>Client: Return empty/structured error response
    end
Loading

Possibly related PRs

  • Mail-0/Zero#387: Refactors useThreads for improved type safety and function signatures; both PRs modify the same hook but focus on different internal aspects.
  • Mail-0/Zero#522: Modifies MailQuickActions by disabling certain actions and simplifying handlers; both PRs change the same component with different logic focuses.
  • Mail-0/Zero#521: Adds keyboard navigation and quick actions to MailList; both PRs involve changes to MailList component’s interaction and selection logic.

Suggested reviewers

  • ahmetskilinc

Poem

In the warren of code, where the hotkeys hop,
We wrapped our routes in a safety crop.
With errors caught and users logged out,
The mail-list’s bulk select took a different route.
Threads now fetch with pagination in tow,
And quick actions nibble on fresh data they know.
🐇 Here’s to code that’s tidy and bright—
Bugs beware, we’re hopping right!

Tip

⚡💬 Agentic Chat (Pro Plan, General Availability)
  • We're introducing multi-step agentic chat in review comments and issue comments, within and outside of PR's. This feature enhances review and issue discussions with the CodeRabbit agentic chat by enabling advanced interactions, including the ability to create pull requests directly from comments and add commits to existing pull requests.
✨ Finishing Touches
  • 📝 Generate Docstrings

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@MrgSub MrgSub merged commit c41813e into main Apr 17, 2025
3 checks passed
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🔭 Outside diff range comments (2)
apps/mail/lib/hotkeys/use-hotkey-utils.ts (1)

133-145: ⚠️ Potential issue

Disabled functionality without explanation or alternative

The entire internal logic of the useShortcuts function has been commented out, effectively disabling keyboard shortcut registration. This means any component calling this function will silently fail to register shortcuts.

Consider one of the following approaches instead of commenting out code:

  1. Use a feature flag to conditionally enable/disable shortcuts
  2. Return early with a clear reason
  3. Add a proper explanation in code comments about why this is disabled and when it will be re-enabled
export function useShortcuts(
  shortcuts: Shortcut[],
  handlers: { [key: string]: () => void },
  options: Partial<HotkeyOptions> = {},
) {
-  // DISABLED
-  //   shortcuts.forEach((shortcut) => {
-  //     const handler = handlers[shortcut.action];
-  //     if (handler) {
-  //       useShortcut(shortcut, handler, options);
-  //     }
-  //   });
+  // If there's a reason to disable shortcuts temporarily, explain why
+  const enableShortcuts = process.env.ENABLE_SHORTCUTS !== 'false';
+  if (!enableShortcuts) {
+    console.debug('Keyboard shortcuts are currently disabled');
+    return;
+  }
+
+  shortcuts.forEach((shortcut) => {
+    const handler = handlers[shortcut.action];
+    if (handler) {
+      useShortcut(shortcut, handler, options);
+    }
+  });
}
apps/mail/components/mail/mail-list.tsx (1)

497-514: ⚠️ Potential issue

Missing dependencies in the handleMailClick useCallback

The dependency array for handleMailClick is empty, but the function uses handleMouseEnter, setMail, and setThreadId. This could cause stale closures where the callback captures outdated values.

  const handleMailClick = useCallback(
    (message: ParsedMessage) => () => {
      handleMouseEnter(message.id);

      const messageThreadId = message.threadId ?? message.id;

      // Update local state immediately for optimistic UI
      setMail((prev) => ({
        ...prev,
        replyComposerOpen: false,
        forwardComposerOpen: false,
      }));

      // Update URL param without navigation
      void setThreadId(messageThreadId);
    },
-    [],
+    [handleMouseEnter, setMail, setThreadId],
  );
🧹 Nitpick comments (4)
apps/mail/app/api/driver/connections/route.ts (1)

16-50: Good addition of robust error handling with graceful degradation.

The try-catch block improves error handling by logging errors, logging out the user when authentication issues occur, and returning an empty array response instead of crashing or returning a 500 error.

This pattern ensures better user experience and security by preventing users from remaining in potentially invalid sessions when errors occur.

Consider implementing more granular error handling for certain types of errors that might not require logging the user out (e.g., transient database errors that could be retried).

apps/mail/app/api/driver/notes/route.ts (1)

14-44: Good addition of robust error handling with graceful degradation.

The try-catch block improves error handling by logging errors, logging out the user when authentication issues occur, and returning an empty array response instead of crashing or returning a 500 error.

This pattern ensures better user experience and security by preventing users from remaining in potentially invalid sessions when errors occur.

Consider implementing more granular error handling for certain types of errors that might not require logging the user out (e.g., temporary database connection issues).

apps/mail/app/api/driver/count/route.ts (1)

13-37: Good addition of robust error handling with graceful degradation.

The try-catch block improves error handling by logging errors, logging out the user when authentication issues occur, and returning an empty object response instead of crashing or returning a 500 error.

This pattern ensures better user experience and security by preventing users from remaining in potentially invalid sessions when errors occur.

Consider implementing more granular error handling for certain types of errors that might not require logging the user out (e.g., temporary service unavailability).

apps/mail/app/api/v1/hotkeys/route.ts (1)

1-9: Consider adding try-catch blocks for error handling

While the authentication and rate limiting improvements are good, consider adding try-catch blocks around database operations to align with the PR's objective of improving error handling across the app.

You could wrap the database operations in try-catch blocks and implement proper error handling:

  export async function GET() {
    const userId = await getAuthenticatedUserId();

    const ratelimit = getRatelimitModule({
      prefix: 'ratelimit:hotkeys',
      limiter: Ratelimit.slidingWindow(60, '1m'),
    });

    const { success, headers } = await checkRateLimit(ratelimit, userId);
    if (!success) {
      return NextResponse.json(
        { error: 'Too many requests. Please try again later.' },
        { status: 429, headers },
      );
    }

+   try {
      const result = await db.select().from(userHotkeys).where(eq(userHotkeys.userId, userId));
      return NextResponse.json(result[0]?.shortcuts || []);
+   } catch (error) {
+     console.error('Error fetching user hotkeys:', error);
+     return NextResponse.json(
+       { error: 'Failed to fetch hotkeys' },
+       { status: 500 }
+     );
+   }
  }

Apply similar error handling to POST and PUT methods as well.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 13d7923 and 4ca33e8.

📒 Files selected for processing (13)
  • apps/mail/app/api/driver/connections/route.ts (2 hunks)
  • apps/mail/app/api/driver/count/route.ts (1 hunks)
  • apps/mail/app/api/driver/google.ts (2 hunks)
  • apps/mail/app/api/driver/notes/route.ts (1 hunks)
  • apps/mail/app/api/driver/route.ts (1 hunks)
  • apps/mail/app/api/utils.ts (2 hunks)
  • apps/mail/app/api/v1/hotkeys/route.ts (4 hunks)
  • apps/mail/components/mail/mail-list.tsx (1 hunks)
  • apps/mail/components/mail/mail-quick-actions.tsx (8 hunks)
  • apps/mail/components/mail/thread-display.tsx (9 hunks)
  • apps/mail/hooks/use-threads.ts (1 hunks)
  • apps/mail/lib/hotkeys/mail-list-hotkeys.tsx (1 hunks)
  • apps/mail/lib/hotkeys/use-hotkey-utils.ts (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (6)
apps/mail/app/api/utils.ts (1)
apps/mail/actions/utils.ts (1)
  • deleteActiveConnection (12-29)
apps/mail/app/api/driver/connections/route.ts (4)
apps/mail/app/api/utils.ts (5)
  • getAuthenticatedUserId (35-44)
  • processIP (64-73)
  • getRatelimitModule (9-21)
  • checkRateLimit (53-62)
  • logoutUser (47-51)
packages/db/src/index.ts (1)
  • db (17-17)
packages/db/src/schema.ts (1)
  • connection (77-94)
apps/mail/types/index.ts (1)
  • IConnection (64-69)
apps/mail/app/api/driver/count/route.ts (7)
apps/mail/app/api/driver/connections/route.ts (1)
  • GET (15-51)
apps/mail/app/api/driver/route.ts (1)
  • GET (13-50)
apps/mail/app/api/v1/hotkeys/route.ts (1)
  • GET (11-30)
apps/mail/app/api/driver/notes/route.ts (1)
  • GET (13-45)
apps/mail/app/api/driver/[id]/route.ts (1)
  • GET (6-27)
apps/mail/app/api/utils.ts (5)
  • getAuthenticatedUserId (35-44)
  • processIP (64-73)
  • getRatelimitModule (9-21)
  • checkRateLimit (53-62)
  • logoutUser (47-51)
apps/mail/actions/utils.ts (1)
  • getActiveDriver (31-61)
apps/mail/app/api/driver/notes/route.ts (2)
apps/mail/app/api/utils.ts (5)
  • getAuthenticatedUserId (35-44)
  • processIP (64-73)
  • getRatelimitModule (9-21)
  • checkRateLimit (53-62)
  • logoutUser (47-51)
apps/mail/app/api/notes/db.ts (1)
  • notesManager (6-157)
apps/mail/hooks/use-threads.ts (1)
apps/mail/lib/utils.ts (1)
  • defaultPageSize (143-143)
apps/mail/app/api/v1/hotkeys/route.ts (4)
apps/mail/app/api/utils.ts (3)
  • getAuthenticatedUserId (35-44)
  • getRatelimitModule (9-21)
  • checkRateLimit (53-62)
packages/db/src/index.ts (1)
  • db (17-17)
packages/db/src/schema.ts (1)
  • userHotkeys (50-57)
apps/mail/config/shortcuts.ts (1)
  • Shortcut (3-10)
🔇 Additional comments (25)
apps/mail/app/api/utils.ts (1)

46-51: Good improvement to logout process

The logout function now properly cleans up by deleting the active connection before signing out the user. The added comment is helpful for clarifying the function's purpose.

apps/mail/lib/hotkeys/mail-list-hotkeys.tsx (1)

35-50: Simplified ID selection logic

The ID selection logic has been simplified to consistently use item.id instead of possibly falling back to another property like threadId. This improves code consistency.

However, note that this function is still being defined and used even though the useShortcuts function in use-hotkey-utils.ts has been disabled, meaning this shortcut won't actually work. Consider addressing this inconsistency.

apps/mail/app/api/driver/route.ts (1)

13-50: Improved error handling with proper cleanup

The error handling has been improved by wrapping the code in a try-catch block, logging the error, properly logging out the user (which now includes deleting the active connection), and returning a valid JSON response instead of redirecting.

Consider adding a status code to the error response:

catch (error) {
  console.warn('Error getting threads:', error);
  await logoutUser();
-  return NextResponse.json({ messages: [], nextPageToken: null });
+  return NextResponse.json({ messages: [], nextPageToken: null }, { status: 401 });
}
apps/mail/app/api/driver/connections/route.ts (1)

1-7: Import additions support enhanced error handling.

The import of logoutUser enables the new error handling strategy for this endpoint.

apps/mail/app/api/driver/google.ts (1)

132-132: Security improvement: Reduced OAuth2 scope.

Reducing the OAuth scope to only gmail.modify follows the principle of least privilege by requesting only the permissions needed for the application to function, which is a security best practice.

apps/mail/app/api/driver/notes/route.ts (1)

1-7: Import additions support enhanced error handling.

The import of logoutUser enables the new error handling strategy for this endpoint.

apps/mail/app/api/driver/count/route.ts (1)

1-7: Import additions support enhanced error handling.

The import of logoutUser enables the new error handling strategy for this endpoint.

apps/mail/hooks/use-threads.ts (1)

94-104: LGTM - Improved request parameter handling

The refactoring of the fetcher function to dynamically construct query parameters inside the function is cleaner than using a static searchParams object outside. This ensures that each request uses the latest parameter values.

apps/mail/components/mail/mail-quick-actions.tsx (3)

35-37: LGTM - Improved data fetching with useThread hook

Using the useThread hook to fetch the latest thread data directly inside the component is better than relying solely on the passed message prop, ensuring you always work with up-to-date information.


50-51: Good defensive programming with null checks

Adding null checks for latestMessage before attempting to use it prevents potential runtime errors when thread data hasn't loaded yet.

Also applies to: 65-66, 107-108


169-170: LGTM - Better read status checking

Using threadData?.hasUnread for the label is more reliable than checking the message's unread status directly, as it accounts for the entire thread's state.

apps/mail/app/api/v1/hotkeys/route.ts (3)

11-30: LGTM - Added proper authentication and rate limiting to GET endpoint

The implementation of centralized authentication and rate limiting provides better security and abuse prevention for the API.


32-67: LGTM - Added proper authentication and rate limiting to POST endpoint

Consistent application of the same authentication and rate limiting patterns to all endpoints is good practice.


69-115: LGTM - Added proper authentication and rate limiting to PUT endpoint

The rate limiting with custom prefixes for each method type allows for more granular control over API usage.

apps/mail/components/mail/thread-display.tsx (11)

14-19: Appropriate addition of navigation and mail action imports
The new imports (useParams, moveThreadsTo, ThreadDestination, markAsRead, markAsUnread) are correctly added and used throughout the component for folder routing and thread/mail actions.


28-34: Import of NotesPanel and toast is utilized correctly
The NotesPanel component and toast from sonner are now imported to support note display and UI notifications. They are properly integrated in the action toolbar and error–notification flows.


87-87: Stylistic update in ThreadDemo reply composer
The ReplyCompose call in ThreadDemo now consistently uses arrow‐function prop formatting. Behavior remains unchanged.


149-157: hasImages useMemo logic is clear and correct
The updated image‐detection logic inside useMemo correctly checks attachments and inline <img> tags (excluding SVG avatars). Consider extracting this predicate into a named helper for readability, but the implementation is sound.


159-162: hasMultipleParticipants calculation is concise
The logic for detecting multiple participants correctly sums recipients and sender, driving the “Reply All” UI.


168-176: Verify .then after .catch in markAsRead
The chain markAsRead(...).catch(...).then(...) will invoke mutateThread() and mutateStats() even if marking as read fails. Confirm whether the intent is to always refresh, or if you’d prefer separating success and error flows (e.g., using .finally(...)).


193-194: handleClose resets threadId correctly
Clearing the threadId query state on close ensures proper navigation and unmounting of the thread view.


228-235: handleMarkAsUnread flow is robust
The markAsUnread handler leverages toast.promise for loading/success/error feedback and correctly resets bulk selection, stats, and thread data on completion.


376-383: Composer toggle UI enhancements are consistent
The class toggles and onClick handlers for Reply, Reply All, and Forward buttons now uniformly manage the mail state to open the correct composer. The UX is consistent across all modes.

Also applies to: 391-398, 406-413


421-429: Image‐hidden warning panel is user-friendly
Displaying a warning banner when images are hidden, with a clear CTA to show them, provides a good user experience and ensures inline images are opt‑in.


454-464: Bottom ReplyCompose placement and mode logic
The bottom-mounted ReplyCompose now respects the current mode (forward, replyAll, or reply) and adjusts for fullscreen vs. embedded views, mirroring the top toolbar logic.

Comment on lines +528 to +536
const res = await gmail.users.threads.list({
userId: 'me',
q: normalizedQ ? normalizedQ : undefined,
labelIds: folder === 'inbox' ? labelIds : [],
maxResults,
pageToken: pageToken ? pageToken : undefined,
quotaUser: config.auth?.email,
});
return { ...res.data, threads: res.data.threads } as any;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Removed retry logic for Gmail API calls.

The removal of the withExponentialBackoff wrapper means the Gmail API call no longer has built-in retry logic for rate limiting issues.

This change could reduce the resilience to transient rate limit errors. Ensure that rate limit handling is implemented elsewhere, or consider restoring the retry logic:


🏁 Script executed:

#!/bin/bash
# Check for rate limiting error handling elsewhere
echo "Searching for rate limit error handling:"
rg -A 5 -B 5 "rate.*limit|429" --type ts apps/mail/ | grep -v "gmail-v1"

Length of output: 25720


🏁 Script executed:

#!/bin/bash
# Display lines around retry logic and the list call in google.ts
sed -n '480,600p' apps/mail/app/api/driver/google.ts

Length of output: 4553


Add retry with exponential backoff to the list call

The list method in apps/mail/app/api/driver/google.ts now invokes gmail.users.threads.list directly, whereas the get method still uses your withExponentialBackoff wrapper. Without backoff retries, transient 429/rate‑limit errors will surface immediately, reducing resilience.

• File: apps/mail/app/api/driver/google.ts
• Around lines ~528–536

Suggested change:

     list: async (
       folder: string,
       q: string,
       maxResults = 20,
       _labelIds: string[] = [],
       pageToken?: string,
     ) => {
       const { folder: normalizedFolder, q: normalizedQ } = normalizeSearch(folder, q ?? '');
       const labelIds = [..._labelIds];
       if (normalizedFolder) labelIds.push(normalizedFolder.toUpperCase());
-      const res = await gmail.users.threads.list({
-        userId: 'me',
-        q: normalizedQ || undefined,
-        labelIds: folder === 'inbox' ? labelIds : [],
-        maxResults,
-        pageToken: pageToken || undefined,
-        quotaUser: config.auth?.email,
-      });
-      return { ...res.data, threads: res.data.threads } as any;
+      return withExponentialBackoff(async () => {
+        const res = await gmail.users.threads.list({
+          userId: 'me',
+          q: normalizedQ || undefined,
+          labelIds: folder === 'inbox' ? labelIds : [],
+          maxResults,
+          pageToken: pageToken || undefined,
+          quotaUser: config.auth?.email,
+        });
+        return { ...res.data, threads: res.data.threads } as any;
+      });
     },

This restores automatic retries on rate‑limit and other transient errors, matching your existing get implementation.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const res = await gmail.users.threads.list({
userId: 'me',
q: normalizedQ ? normalizedQ : undefined,
labelIds: folder === 'inbox' ? labelIds : [],
maxResults,
pageToken: pageToken ? pageToken : undefined,
quotaUser: config.auth?.email,
});
return { ...res.data, threads: res.data.threads } as any;
list: async (
folder: string,
q: string,
maxResults = 20,
_labelIds: string[] = [],
pageToken?: string,
) => {
const { folder: normalizedFolder, q: normalizedQ } = normalizeSearch(folder, q ?? '');
const labelIds = [..._labelIds];
if (normalizedFolder) labelIds.push(normalizedFolder.toUpperCase());
return withExponentialBackoff(async () => {
const res = await gmail.users.threads.list({
userId: 'me',
q: normalizedQ || undefined,
labelIds: folder === 'inbox' ? labelIds : [],
maxResults,
pageToken: pageToken || undefined,
quotaUser: config.auth?.email,
});
return { ...res.data, threads: res.data.threads } as any;
});
},

Comment on lines +116 to 117
mutateThread();
toast.success(t('common.mail.markedAsRead'));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify mutateThread usage in dependency array

You're now using mutateThread from useThread instead of mutate from useThreads, but mutate is still listed in the dependency array at line 140.

      },
-      [latestMessage, mutate, t, isProcessing, isLoading, closeThreadIfOpen],
+      [latestMessage, mutateThread, t, isProcessing, isLoading, closeThreadIfOpen],

Let's check all dependencies in the handleToggleRead function:

Also applies to: 126-127


🏁 Script executed:

#!/bin/bash
# Check where mutate from useThreads is used in handleToggleRead
grep -n "mutate" -A 10 -B 10 apps/mail/components/mail/mail-quick-actions.tsx | grep -A 20 -B 20 "handleToggleRead"

Length of output: 1467


🏁 Script executed:

#!/bin/bash
# Display the handleToggleRead useCallback and its dependency array
grep -n "const handleToggleRead" -A 20 -B 5 apps/mail/components/mail/mail-quick-actions.tsx

Length of output: 1044


🏁 Script executed:

#!/bin/bash
# Extract handleToggleRead callback and its dependencies
sed -n '104,160p' apps/mail/components/mail/mail-quick-actions.tsx

Length of output: 1817


Update handleToggleRead’s dependencies to use mutateThread

In apps/mail/components/mail/mail-quick-actions.tsx, the handleToggleRead callback calls mutateThread() but its dependency array still lists the old mutate. This can lead to stale closures and missed updates.

• Location: inside the useCallback for handleToggleRead (around line 104–112)
• Change the dependency array as follows:

       [
-        latestMessage, mutate, t, isProcessing, isLoading, closeThreadIfOpen
+        latestMessage, mutateThread, t, isProcessing, isLoading, closeThreadIfOpen
       ],

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant