Skip to content

Comments

context#595

Merged
MrgSub merged 5 commits intostagingfrom
context-menu
Apr 5, 2025
Merged

context#595
MrgSub merged 5 commits intostagingfrom
context-menu

Conversation

@hiheyhello123
Copy link
Contributor

@hiheyhello123 hiheyhello123 commented Apr 5, 2025

Summary by CodeRabbit

  • New Features
    • Introduced a toggle to mark or unmark email threads as favorites.
    • Added a dynamic context menu for managing threads, including actions for moving, marking read/unread, and toggling favorites.
    • Enhanced email thread display with a new wrapper component that adapts controls based on folder type.
    • Expanded user feedback with additional localized messages for various email operations.
    • Implemented a context menu component for improved user interaction across the application.
    • Added a new context menu for email thread actions, providing a more interactive user experience.

@vercel
Copy link

vercel bot commented Apr 5, 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 5, 2025 8:56pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Apr 5, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

This pull request introduces new functionality to manage email thread states. An asynchronous function is added to toggle the star status of threads. Additionally, several UI components are implemented to provide context menus and wrappers for thread display, along with enhancements to folder-based logic. Localization strings have been extended to cover new actions, and identifiers related to favorites have been updated.

Changes

File(s) Change Summary
apps/mail/actions/mail.ts Added toggleStar async function to toggle a thread's star status with validation, error handling, and label updates.
apps/mail/components/.../thread-context.tsx
apps/mail/components/mail/mail-list.tsx
Introduced new UI components: ThreadContextMenu for handling thread actions and ThreadWrapper for encapsulating thread rendering with folder state management.
apps/mail/components/.../context-menu.tsx Added a suite of customizable, Radix UI-based context menu components (e.g., trigger, content, item, separator) with ref forwarding and conditional styling.
apps/mail/locales/en.json Updated localization entries: added new strings for actions (moving, archiving, marking, favorites) and renamed "addStar" to "addFavorite" with a new "removeFavorite" key.
apps/mail/app/api/driver/google.ts Updated modifyLabels method to iterate over thread IDs for label modifications and improved logging in the get method.

Sequence Diagram(s)

sequenceDiagram
    participant U as User
    participant TS as toggleStar Function
    participant AD as Active Driver
    participant DB as Thread Database
    U->>TS: Call toggleStar({ids})
    TS->>AD: Get active driver
    TS->>TS: Normalize IDs and validate input
    TS->>DB: Fetch thread(s) by IDs
    alt Thread found
       TS->>TS: Check if thread is already starred
       alt Already starred
           TS->>DB: Remove 'STARRED' label
       else
           TS->>DB: Add 'STARRED' label
       end
       TS->>U: Return success response
    else Thread not found
       TS->>U: Return error "Thread not found"
    end
Loading
sequenceDiagram
    participant U as User
    participant TCM as ThreadContextMenu
    participant TW as ThreadWrapper
    U->>TCM: Right-click to open context menu
    TCM->>TCM: Render dynamic actions based on thread state and folder type
    U->>TCM: Select "Toggle Favorite" action
    TCM->>TW: Trigger refresh callback/update state (invoking toggle behavior)
Loading

Possibly related PRs

  • Feature/add favourite #484: The changes in the main PR, specifically the addition of the toggleStar function for managing starred emails, are directly related to the modifications in the retrieved PR, which also focuses on implementing a feature for starring favorite emails, including the management of the 'STARRED' label.
  • Mail list updates #375: The changes in the main PR, specifically the addition of the toggleStar function in apps/mail/actions/mail.ts, are related to the modifications made to the modifyLabels function in the retrieved PR, as both involve label management for email threads within the same file.
  • fix: threads with unread messages where wrongly flagged on mail list #480: The changes in the main PR, specifically the addition of the toggleStar function that modifies labels for email threads, are related to the modifications in the retrieved PR, which also involves changes to how labels are handled for messages within threads.

Suggested reviewers

  • MrgSub
  • ahmetskilinc

Poem

I'm a little rabbit, hopping with glee,
New features in mail, as fresh as can be!
From toggling stars to menus that shine,
With every update, our code feels divine.
Follow my hops through the lines of our code,
Enjoy the improvements on this joyful road!
🐇✨


📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 1c55a80 and dc78c0a.

📒 Files selected for processing (1)
  • apps/mail/app/api/driver/google.ts (2 hunks)

🪧 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 plan to trigger planning for file edits and PR creation.
  • @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.

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

🧹 Nitpick comments (1)
apps/mail/components/context/thread-context.tsx (1)

271-272: Implement trash-moving functionality or remove the placeholder.

Line 272 includes a TODO for moving items to trash. This is currently disabled. If you plan to support it, add an implementation that calls a backend action (similar to moveThreadsTo) or remove the placeholder action if not needed.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 6b47b5a and e0d34d4.

📒 Files selected for processing (5)
  • apps/mail/actions/mail.ts (1 hunks)
  • apps/mail/components/context/thread-context.tsx (1 hunks)
  • apps/mail/components/mail/mail-list.tsx (7 hunks)
  • apps/mail/components/ui/context-menu.tsx (1 hunks)
  • apps/mail/locales/en.json (2 hunks)
🧰 Additional context used
🧬 Code Definitions (2)
apps/mail/actions/mail.ts (1)
apps/mail/app/api/driver/google.ts (1)
  • driver (81-567)
apps/mail/components/context/thread-context.tsx (4)
apps/mail/components/mail/use-mail.ts (1)
  • useMail (15-17)
apps/mail/hooks/use-threads.ts (1)
  • useThreads (77-129)
apps/mail/lib/thread-actions.ts (2)
  • ThreadDestination (4-4)
  • moveThreadsTo (43-81)
apps/mail/actions/mail.ts (3)
  • toggleStar (115-142)
  • markAsRead (45-55)
  • markAsUnread (57-67)
🔇 Additional comments (23)
apps/mail/components/context/thread-context.tsx (1)

92-97: Confirm bulk star toggle logic.

When multiple threads are selected, isStarred is set to true only if every selected thread is starred (line 94). Verify that this behavior is desired. If you want partial star toggling (e.g., some are starred, some are not), you may need more nuanced handling instead of aggregating all threads under a single boolean.

apps/mail/locales/en.json (2)

15-31: New action messages look consistent.

These new strings provide clear user feedback for email movement, favorite toggles, and read/unread status. Good job integrating them into the action workflow. No issues spotted.


239-240: Favoriting strings align well with “STARRED” label usage.

“addFavorite” and “removeFavorite” match the rest of the star/favorite feature, ensuring consistent terminology in the UI. No issues found.

apps/mail/components/mail/mail-list.tsx (8)

13-13: Added StarOff icon import.

The import for StarOff icon from lucide-react is added, which is likely used in the ThreadContextMenu component for toggling star status of email threads.


24-24: Good use of constants for folder identification.

Using the FOLDERS constant from utils rather than hardcoded strings is a good practice for consistency and maintainability.


38-38: Imported ThreadContextMenu for right-click functionality.

This import brings in the context menu component specifically designed for thread actions, supporting the new functionality described in the PR.


41-70: Well-structured ThreadWrapper component.

The new ThreadWrapper component is well-implemented with proper TypeScript typing and serves as a clean adapter between the Thread component and ThreadContextMenu. It abstracts the context menu implementation details away from the Thread component, making the code more modular and maintainable.


94-95: Extracted folder parameter for context-aware actions.

Properly using the useParams hook to extract the folder parameter, which is then used to determine folder types for contextual actions.


111-114: Clear folder type logic.

The boolean flags for folder types create a clear, readable way to determine the current folder context, which is then used to provide appropriate actions in the context menu.


162-334: Improved component structure with content variable.

Extracting the JSX content into a variable improves readability and allows for wrapping it in the ThreadWrapper component. This is a clean refactoring approach.


335-346: Well-implemented wrapper integration.

The ThreadWrapper is properly integrated with the necessary props passed down. The refreshCallback using mutate() ensures the UI updates after thread state changes. This implementation follows best practices for component composition.

apps/mail/components/ui/context-menu.tsx (12)

1-8: Well-structured imports for the context menu component.

Clean import structure with proper separation of Radix UI primitives, React, and utility functions.


9-20: Proper component aliasing.

Good practice to alias the Radix UI primitive components, which makes them more easily consumable by other components in the application.


21-40: Well-implemented ContextMenuSubTrigger.

The component properly uses forwardRef and includes an inset prop for flexible styling. The ChevronRight icon provides visual indication of a submenu, enhancing usability.


42-55: Well-structured ContextMenuSubContent component.

Clean implementation with proper ref forwarding and styled using the cn utility function. Appropriate animations are included for state transitions.


57-72: ContextMenuContent with proper portal implementation.

Using ContextMenuPrimitive.Portal ensures the menu content is rendered at the root of the DOM, preventing z-index and overflow issues. The animations provide a polished user experience.


74-90: Flexible ContextMenuItem with inset option.

The inset prop provides additional styling flexibility for menu items, useful for creating hierarchical or aligned menu structures.


92-113: Accessible ContextMenuCheckboxItem.

Well-implemented checkbox item with proper accessibility features, including a visual indicator for the checked state and appropriate styling for disabled states.


115-135: Properly styled ContextMenuRadioItem.

The radio item implementation includes appropriate styling and a circular indicator, maintaining consistency with standard radio input UI patterns.


137-149: Semantically correct ContextMenuLabel.

The label component is styled appropriately with semibold font weight to distinguish it from regular menu items, enhancing the menu's information hierarchy.


151-161: Clean separator implementation.

Simple yet effective separator component that provides visual grouping of menu items.


163-171: Useful ContextMenuShortcut component.

This component provides a consistent way to display keyboard shortcuts in the menu, enhancing usability for power users.


173-189: Comprehensive exports.

All components are properly exported, making them easily importable by other components that need context menu functionality.

Comment on lines 114 to 142

export const toggleStar = async ({ ids }: { ids: string[] }) => {
try {
const driver = await getActiveDriver();
const { threadIds } = driver.normalizeIds(ids);

if (threadIds.length && threadIds[0]) {
const thread = await driver.get(threadIds[0]);
if (!thread?.[0]) {
return { success: false, error: 'Thread not found' };
}

const isStarred = thread[0].tags?.includes('STARRED') ?? false;

await driver.modifyLabels(threadIds, {
addLabels: isStarred ? [] : ['STARRED'],
removeLabels: isStarred ? ['STARRED'] : [],
});

return { success: true };
}

return { success: false, error: 'No thread IDs provided' };
} catch (error) {
if (FatalErrors.includes((error as Error).message)) await deleteActiveConnection();
console.error('Error toggling star:', error);
throw error;
}
};
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Address multi-thread behavior for the toggleStar function.

Currently, the code checks the star status of only the first thread (line 121), while the label modification (line 128) targets all threads in threadIds. This can lead to inconsistencies if multiple threads have different star states. Consider iterating over each thread, determining its existing star state individually, and then applying the label changes accordingly. For example:

-export const toggleStar = async ({ ids }: { ids: string[] }) => {
-  try {
-    const driver = await getActiveDriver();
-    const { threadIds } = driver.normalizeIds(ids);
-    if (threadIds.length && threadIds[0]) {
-      const thread = await driver.get(threadIds[0]);
-      if (!thread?.[0]) {
-        return { success: false, error: 'Thread not found' };
-      }
-      const isStarred = thread[0].tags?.includes('STARRED') ?? false;
-      await driver.modifyLabels(threadIds, {
-        addLabels: isStarred ? [] : ['STARRED'],
-        removeLabels: isStarred ? ['STARRED'] : [],
-      });
-      return { success: true };
-    }
-    return { success: false, error: 'No thread IDs provided' };
-  } catch (error) {
-    if (FatalErrors.includes((error as Error).message)) await deleteActiveConnection();
-    console.error('Error toggling star:', error);
-    throw error;
-  }
-};
+export const toggleStar = async ({ ids }: { ids: string[] }) => {
+  try {
+    const driver = await getActiveDriver();
+    const { threadIds } = driver.normalizeIds(ids);
+    if (!threadIds.length) {
+      return { success: false, error: 'No thread IDs provided' };
+    }
+
+    // Process each thread individually.
+    for (const tId of threadIds) {
+      const thread = await driver.get(tId);
+      if (!thread?.[0]) {
+        continue;
+      }
+      const isStarred = thread[0].tags?.includes('STARRED') ?? false;
+      await driver.modifyLabels([tId], {
+        addLabels: isStarred ? [] : ['STARRED'],
+        removeLabels: isStarred ? ['STARRED'] : [],
+      });
+    }
+    return { success: true };
+  } catch (error) {
+    if (FatalErrors.includes((error as Error).message)) await deleteActiveConnection();
+    console.error('Error toggling star:', error);
+    throw error;
+  }
+};
📝 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
export const toggleStar = async ({ ids }: { ids: string[] }) => {
try {
const driver = await getActiveDriver();
const { threadIds } = driver.normalizeIds(ids);
if (threadIds.length && threadIds[0]) {
const thread = await driver.get(threadIds[0]);
if (!thread?.[0]) {
return { success: false, error: 'Thread not found' };
}
const isStarred = thread[0].tags?.includes('STARRED') ?? false;
await driver.modifyLabels(threadIds, {
addLabels: isStarred ? [] : ['STARRED'],
removeLabels: isStarred ? ['STARRED'] : [],
});
return { success: true };
}
return { success: false, error: 'No thread IDs provided' };
} catch (error) {
if (FatalErrors.includes((error as Error).message)) await deleteActiveConnection();
console.error('Error toggling star:', error);
throw error;
}
};
export const toggleStar = async ({ ids }: { ids: string[] }) => {
try {
const driver = await getActiveDriver();
const { threadIds } = driver.normalizeIds(ids);
if (!threadIds.length) {
return { success: false, error: 'No thread IDs provided' };
}
// Process each thread individually.
for (const tId of threadIds) {
const thread = await driver.get(tId);
if (!thread?.[0]) {
continue;
}
const isStarred = thread[0].tags?.includes('STARRED') ?? false;
await driver.modifyLabels([tId], {
addLabels: isStarred ? [] : ['STARRED'],
removeLabels: isStarred ? ['STARRED'] : [],
});
}
return { success: true };
} catch (error) {
if (FatalErrors.includes((error as Error).message)) await deleteActiveConnection();
console.error('Error toggling star:', error);
throw error;
}
};

Comment on lines +152 to +164
const handleFavorites = () => {
const targets = mail.bulkSelected.length ? mail.bulkSelected : [threadId];
const promise = toggleStar({ ids: targets }).then(() => {
setMail(prev => ({ ...prev, bulkSelected: [] }));
return mutate();
});

toast.promise(promise, {
loading: isStarred ? t('common.actions.removingFromFavorites') : t('common.actions.addingToFavorites'),
success: isStarred ? t('common.actions.removedFromFavorites') : t('common.actions.addedToFavorites'),
error: t('common.actions.failedToModifyFavorites'),
});
};
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Align all star toggles with partial or unified approach.

This block unconditionally calls toggleStar for all selected threads, but toggleStar (in its current form) only checks the first thread’s state. If you intend to star/unstar all threads consistently regardless of their individual states, consider clearly documenting this choice. Otherwise, refine the logic to handle each thread’s current star status.

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: 0

🧹 Nitpick comments (1)
apps/mail/actions/mail.ts (1)

115-154: The toggleStar implementation looks good but has an unnecessary continue statement.

The function correctly handles multiple threads by checking each thread's star status individually before applying a consistent action across all threads. This is a robust approach that addresses the concerns raised in previous reviews.

Remove the unnecessary continue statement in the catch block as it's the last statement in the loop:

      } catch (error) {
-        continue;
      }
🧰 Tools
🪛 Biome (1.9.4)

[error] 138-138: Unnecessary continue statement

Unsafe fix: Delete the unnecessary continue statement

(lint/correctness/noUnnecessaryContinue)

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between e0d34d4 and 1802839.

📒 Files selected for processing (2)
  • apps/mail/actions/mail.ts (1 hunks)
  • apps/mail/app/api/driver/google.ts (2 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
apps/mail/actions/mail.ts (1)
apps/mail/app/api/driver/google.ts (1)
  • driver (81-589)
🪛 Biome (1.9.4)
apps/mail/actions/mail.ts

[error] 138-138: Unnecessary continue statement

Unsafe fix: Delete the unnecessary continue statement

(lint/correctness/noUnnecessaryContinue)

🔇 Additional comments (5)
apps/mail/actions/mail.ts (2)

124-141: Smart thread state detection logic.

This implementation efficiently determines if all threads are starred by setting allStarred to false once any unstarred thread is found, then breaking the loop. This optimizes performance by avoiding unnecessary checks once the state is known.

🧰 Tools
🪛 Biome (1.9.4)

[error] 138-138: Unnecessary continue statement

Unsafe fix: Delete the unnecessary continue statement

(lint/correctness/noUnnecessaryContinue)


141-147:

Details

❓ Verification inconclusive

Verify the shouldStar logic is correct.

The current logic sets shouldStar = !anyValid || !allStarred, which means:

  • If no valid threads were found, star them all
  • If any thread isn't starred, star them all
  • If all threads are already starred, unstar them all

This logic will work for most cases, but consider if this behavior matches the intended UX.


🏁 Script executed:

#!/bin/bash
# Check if this function is used in any UI components to verify the expected behavior
rg --type ts "toggleStar" --glob "apps/mail/components/**/*.tsx" -A 5 -B 5

Length of output: 1798


Action Required: Confirm that the toggle logic in shouldStar meets UX expectations

The code currently calculates:

  • shouldStar = !anyValid || !allStarred, meaning:
    • If no valid threads are present, star them all.
    • If at least one valid thread is not starred, star them all.
    • Only if all valid threads are already starred will it unstar them all.

We verified that the toggleStar function (invoked in apps/mail/components/context/thread-context.tsx) uses this logic when handling favorites. Please take a moment to confirm that having the toggle star all threads when even one is unstarred is the intended behavior for our UX.

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

313-313: Improved logging for better debugging.

Enhanced log message to clearly indicate when a thread is being fetched.


317-325: Commented-out debugging code could be useful.

This commented-out block provides a way to debug label-related issues by tracking both thread and message-level labels. Consider keeping this available but disabled for troubleshooting.


472-492: Robust implementation for modifying labels at the message level.

The refactored modifyLabels method now:

  1. Works with multiple thread IDs
  2. Fetches all messages within each thread
  3. Applies label changes at the message level rather than thread level

This is a significant improvement that ensures labels are properly applied to all messages in a thread, which is essential for features like starring/unstarring.

Copy link
Collaborator

@MrgSub MrgSub left a comment

Choose a reason for hiding this comment

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

LGTM

@MrgSub MrgSub merged commit 0a1927e into staging Apr 5, 2025
3 of 5 checks passed
@hiheyhello123 hiheyhello123 deleted the context-menu branch April 5, 2025 21:33
@hiheyhello123 hiheyhello123 restored the context-menu branch April 5, 2025 21:33
@coderabbitai coderabbitai bot mentioned this pull request Apr 5, 2025
@hiheyhello123 hiheyhello123 deleted the context-menu branch April 6, 2025 07:48
@coderabbitai coderabbitai bot mentioned this pull request Apr 10, 2025
@coderabbitai coderabbitai bot mentioned this pull request Apr 17, 2025
34 tasks
This was referenced Apr 29, 2025
@coderabbitai coderabbitai bot mentioned this pull request May 29, 2025
17 tasks
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.

2 participants