Skip to content

Comments

fix: re-rendering#478

Merged
MrgSub merged 10 commits intostagingfrom
fix/ctrl
Mar 19, 2025
Merged

fix: re-rendering#478
MrgSub merged 10 commits intostagingfrom
fix/ctrl

Conversation

@hiheyhello123
Copy link
Contributor

@hiheyhello123 hiheyhello123 commented Mar 18, 2025

Summary by CodeRabbit

  • New Features

    • Enhanced mail selection behavior: Modifier keys (Control, Shift, Alt) are now detected dynamically, improving the responsiveness and flexibility for multi-select operations such as range and bulk selections.
    • Introduced a new global state management for keyboard key states, allowing components to check if specific keys are pressed.
    • Added a new hook, useKeyState, for simplified key state management across components.
  • Chores

    • Reorganized import statements in multiple files for improved code clarity without altering functionality.

@vercel
Copy link

vercel bot commented Mar 18, 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 Mar 19, 2025 2:16am

@hiheyhello123 hiheyhello123 requested a review from MrgSub March 18, 2025 21:53
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 18, 2025

Walkthrough

This change introduces a new hook, useKeyState, for managing global keyboard key states in the mail list component. It replaces the previous individual state variables for selection modes with a dynamic approach using the getSelectMode function. Event listeners for keyboard events have been streamlined to track key presses and releases more effectively, simplifying the mail click handling logic.

Changes

File Change Summary
apps/mail/.../mail-list.tsx Introduced useKeyState hook for key state management, removed previous state variables for selection modes, and updated mail click handling to use getSelectMode. Added onMouseDown event handler in Thread component.
apps/mail/.../use-hot-key.ts Added global key state management using a Map, implemented event listeners for keydown, keyup, and blur, and introduced useKeyState function for checking key states.
apps/mail/.../login/login-client.tsx Reordered import statements for GitHub and Google icons, as well as signIn and useSession. No functional changes made.
apps/mail/.../create/create-email.tsx Reorganized import statements for ArrowUpIcon, Paperclip, and X from lucide-react. No functional changes made.
apps/mail/.../draft/drafts.tsx Simplified return statement formatting in DraftsLayout function. No functional changes made.
apps/mail/.../ui/nav-main.tsx Reordered and re-imported modules in nav-main.tsx. No functional changes made.
apps/mail/.../use-notes.tsx Updated import structure for useSession and added a semicolon for consistency. No functional changes made.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant MailList as MailList Component
    User->>MailList: Presses Ctrl/Shift/Alt key (keydown)
    MailList-->>MailList: Set corresponding key state to true
    User->>MailList: Releases key (keyup)/Window loses focus (blur)
    MailList-->>MailList: Reset key state to false
    User->>MailList: Clicks on a mail item
    MailList-->>MailList: Check key states and update selection logic accordingly
Loading

Possibly related PRs

  • fixing the virtual mail-list #475: The changes in the main PR are related to the modifications in the MailList component found in the retrieved PR, as both involve significant updates to the same component, specifically focusing on the handling of key states and selection modes.
  • fix: reduced useSession() calls ; layout inconsistencies  #432: The changes in the main PR are related to the modifications in the MailList component of the retrieved PR, as both involve updates to the same component's logic and structure, specifically regarding key state management and session data handling.

Suggested reviewers

  • MrgSub
  • nizzyabi
  • ahmetskilinc

Poem

I'm a little rabbit with a keen coder's delight,
Hopping through key states both day and night.
With Ctrl, Shift, and Alt in the mix so true,
My code bounds forward with each click anew.
Celebrating changes with a carrot crunch in view!

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 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.

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 (3)
apps/mail/components/mail/mail-list.tsx (3)

391-428: Event handler implementation looks good but consider refactoring for DRY.

The key event handlers are well implemented, but there's some duplication in logic between handleKeyDown, handleKeyUp, and handleBlur.

Consider extracting a helper function to reset all key states:

+ const resetKeyStates = () => {
+   isCtrlKeyPressed.current = false;
+   isShiftKeyPressed.current = false;
+   isAltKeyPressed.current = false;
+ };

  const handleKeyUp = (e: KeyboardEvent) => {
    if (e.key === 'Control' || e.key === 'Meta') {
-     isCtrlKeyPressed.current = false;
+     resetKeyStates();
      setMassSelectMode(false);
    }
    // ... similar for other keys
  };

  const handleBlur = () => {
-   isCtrlKeyPressed.current = false;
-   isShiftKeyPressed.current = false;
-   isAltKeyPressed.current = false;
+   resetKeyStates();
    setMassSelectMode(false);
    setRangeSelectMode(false);
    setSelectAllBelowMode(false);
  };

455-499: Mail click handler refactoring looks good but consider consistency.

The refactoring to use key refs directly in the click handler is a good approach as it directly checks the current key state. However, there's an inconsistency between how selection mode is determined here versus the selectMode variable computed on lines 445-451.

Consider one of these approaches for consistency:

  1. Remove the state variables and selectMode computation entirely if not needed for UI rendering
  2. Update the selectMode computation to use the refs instead of state variables
  3. Create a helper function that determines the selection mode based on current key states
  // Option 2: Update selectMode computation
  const selectMode: MailSelectMode = isCtrlKeyPressed.current
    ? 'mass'
    : isShiftKeyPressed.current
      ? 'range'
      : (isAltKeyPressed.current && isShiftKeyPressed.current)
        ? 'selectAllBelow'
        : 'single';

445-451: Consider updating selectMode computation for consistency.

While not part of the changed code, the selectMode computation is now inconsistent with the actual behavior in the handleMailClick function. The selection mode is now determined by the key refs, but this computation still uses the state variables.

Update the computation to use the key refs for consistency:

  const selectMode: MailSelectMode = isCtrlKeyPressed.current
    ? 'mass'
    : isShiftKeyPressed.current
      ? 'range'
      : (isAltKeyPressed.current && isShiftKeyPressed.current)
        ? 'selectAllBelow'
        : 'single';
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 34023ac and 5850652.

📒 Files selected for processing (1)
  • apps/mail/components/mail/mail-list.tsx (6 hunks)
🔇 Additional comments (3)
apps/mail/components/mail/mail-list.tsx (3)

282-284: Good addition of key state tracking with useRef.

The introduction of these refs to track key states is a good approach as it provides more responsive handling of key combinations compared to state variables, which can be subject to batching delays.


432-443: Good cleanup implementation.

The cleanup function properly removes event listeners and resets all state variables. This is important to prevent memory leaks and unexpected behavior when the component unmounts.


519-520: Dependency array updated correctly.

Good job updating the dependency array to include items instead of selectMode. This is appropriate since the function now depends on items for range selection and no longer uses the selectMode variable.

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

🧹 Nitpick comments (1)
apps/mail/hooks/use-hot-key.ts (1)

21-23: Implement cleanup for event listeners in the hook.

The useKeyState hook correctly returns a memoized callback function to check key states, but it should also initialize the event listeners when first used and ensure they're properly cleaned up on component unmount.

 export function useKeyState() {
+  useEffect(() => {
+    initializeKeyListeners();
+  }, []);
   return useCallback((key: string) => keyStates.get(key) || false, []);
 }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 5850652 and 5025a0f.

📒 Files selected for processing (2)
  • apps/mail/components/mail/mail-list.tsx (7 hunks)
  • apps/mail/hooks/use-hot-key.ts (1 hunks)
🔇 Additional comments (8)
apps/mail/hooks/use-hot-key.ts (1)

3-4: Global state management implementation.

A new Map is implemented to track keyboard key states globally across the application.

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

142-146: Good prevention of default behavior for Ctrl/Cmd clicking.

This is a good addition to prevent browser default behavior (like text selection) when using Ctrl+click or Cmd+click for mail selection.


284-284: Implementing the new key state hook.

The new approach using useKeyState hook will provide a more centralized way to track keyboard states.


376-387: Clean implementation of selection mode determination.

The getSelectMode function effectively simplifies the selection logic by dynamically determining the mode based on current key states, which is much cleaner than maintaining multiple state variables.


390-392: Simplified mail click handling.

Using the getSelectMode function to determine the selection mode on demand when a mail is clicked is an excellent improvement that removes the need to track and update selection mode state variables.


451-451: Updated dependency array in useCallback.

The dependency array for handleMailClick has been properly updated to include the getSelectMode function.


473-473: Dynamically applying select-none class based on Shift key state.

This is a good optimization to disable text selection when using Shift key for range selection.


496-496: Dynamically determining selection mode for Thread component.

The selectMode prop is now dynamically determined using the getSelectMode function, which ensures the Thread component always has the current selection mode.

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/components/mail/mail-list.tsx (1)

497-497: Consider memoizing the selection mode value.

While the getSelectMode function itself is memoized, calling it directly in the JSX might cause unnecessary recalculations during renders. Consider storing the result in a memoized value.

-selectMode={getSelectMode()}
+selectMode={selectMode}

With this addition above the return statement:

const selectMode = useMemo(() => getSelectMode(), [getSelectMode]);
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 5025a0f and 3abb313.

📒 Files selected for processing (1)
  • apps/mail/components/mail/mail-list.tsx (8 hunks)
🔇 Additional comments (8)
apps/mail/components/mail/mail-list.tsx (8)

37-37: Good addition of the useKeyState hook.

Using a centralized hook for key state management is a good approach for handling keyboard interactions consistently across the application.


71-71: Good implementation of key state in the Thread component.

This allows for more responsive UI interactions based on keyboard state.


143-147: Proper handling of modifier keys during mouse events.

Preventing the default browser behavior when Control or Meta keys are pressed is important to avoid text selection while performing multi-selection operations.


285-285: Consistent use of useKeyState hook in the MailList component.

This maintains consistency with the pattern established in the Thread component.


377-388: Well-structured selection mode logic.

The getSelectMode implementation elegantly handles different keyboard combinations and follows a clear priority order for selection modes:

  1. Control/Meta for mass selection
  2. Shift for range selection
  3. Alt+Shift for selecting all below
  4. Default to single selection

This is a significant improvement over managing separate state variables.


392-393: Clean implementation of dynamic selection mode.

Getting the selection mode at the moment of click ensures the most up-to-date keyboard state is used for determining selection behavior.


474-474: Good UX enhancement to prevent text selection.

Adding the 'select-none' class when Shift is pressed prevents accidental text selection during range selection operations, which improves the user experience.


452-452: Updated dependency array to include getSelectMode.

Good job including getSelectMode in the dependency array of handleMailClick's useCallback. This ensures the callback is properly updated when the key detection logic changes.

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/hooks/use-hot-key.ts (1)

47-49: Consider consolidating initialization logic.

The initKeyListeners() is called in both useKeyState and useHotKey. While this redundancy ensures listeners are initialized regardless of which hook is used, it would be cleaner to have a single source of initialization.

Consider creating a shared initialization hook or moving all initialization to the initKeyListeners function with a module-level call:

let listenersInit = false;

function initKeyListeners() {
  if (typeof window !== 'undefined' && !listenersInit) {
    // Event listener setup...
    listenersInit = true;
  }
}

+// Initialize once at module level
+if (typeof window !== 'undefined') {
+  // Use setTimeout to ensure this runs after module evaluation
+  setTimeout(() => initKeyListeners(), 0);
+}

export function useKeyState() {
-  useEffect(() => {
-    initKeyListeners();
-  }, []);
  
  return useCallback((key: string) => keyStates.get(key) || false, []);
}

export const useHotKey = (
  // ...
) => {
  // ...
  
-  useEffect(() => {
-    initKeyListeners();
-  }, []);
  
  // ...
};
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 3abb313 and 26df1e0.

📒 Files selected for processing (1)
  • apps/mail/hooks/use-hot-key.ts (2 hunks)
🔇 Additional comments (4)
apps/mail/hooks/use-hot-key.ts (4)

3-4: Global state introduction looks good.

Introducing a Map to track key states is a clean approach for maintaining global keyboard state information.


5-6: Flag to prevent duplicate initialization is appropriate.

Using a boolean flag to ensure listeners are only initialized once is a good practice to prevent memory leaks from multiple event listeners.


27-33: Well-implemented hook for key state.

The useKeyState hook is well-designed with proper initialization in useEffect and a memoized getter using useCallback to prevent unnecessary re-renders.


7-25: ⚠️ Potential issue

Fix parameter order in blur event handler.

There's a bug in the blur event handler. The Map's forEach method provides value first, then key: forEach(value, key), but your code is using the parameters in reverse order.

window.addEventListener('blur', () => {
-  keyStates.forEach((_, key) => {
+  keyStates.forEach((value, key) => {
    keyStates.set(key, false);
  });
});

Likely an incorrect or invalid review comment.

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/hooks/use-hot-key.ts (1)

27-29: Consider using useEffect for initialization.

While setTimeout works, it's more React-idiomatic to initialize listeners in a useEffect hook inside a component that first needs this functionality.

-if (typeof window !== 'undefined') {
-  setTimeout(() => initKeyListeners(), 0);
-}

+// Move initialization to components that use the hook
+// Then inside the component:
+// useEffect(() => {
+//   if (typeof window !== 'undefined') {
+//     initKeyListeners();
+//   }
+// }, []);
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 26df1e0 and e1fce14.

📒 Files selected for processing (7)
  • apps/mail/app/(auth)/login/login-client.tsx (1 hunks)
  • apps/mail/components/create/create-email.tsx (1 hunks)
  • apps/mail/components/draft/drafts.tsx (1 hunks)
  • apps/mail/components/mail/mail-list.tsx (8 hunks)
  • apps/mail/components/ui/nav-main.tsx (1 hunks)
  • apps/mail/hooks/use-hot-key.ts (2 hunks)
  • apps/mail/hooks/use-notes.tsx (2 hunks)
✅ Files skipped from review due to trivial changes (5)
  • apps/mail/components/draft/drafts.tsx
  • apps/mail/components/ui/nav-main.tsx
  • apps/mail/components/create/create-email.tsx
  • apps/mail/hooks/use-notes.tsx
  • apps/mail/app/(auth)/login/login-client.tsx
🔇 Additional comments (8)
apps/mail/hooks/use-hot-key.ts (3)

3-6: Good approach for tracking key states.

Using a global Map to track keyboard states and a flag to prevent multiple listener initializations is a clean approach.


7-25: Event listener implementation looks good.

The implementation properly handles keydown, keyup, and blur events to maintain accurate key states and cleans up when the window loses focus.


31-33: Simple and effective hook implementation.

The useKeyState hook provides a clean API for components to check key states. The useCallback ensures stable references.

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

143-147: Nice handling of modifier key interactions.

Preventing default behavior when Control or Meta keys are pressed avoids text selection issues during multi-select operations.


377-388: Excellent refactoring of selection mode logic.

Replacing multiple state variables with a single function that dynamically determines the selection mode based on key states is a clean improvement that reduces state management complexity.


392-393: Good integration with handleMailClick.

Using getSelectMode() to determine the selection mode on demand ensures accurate handling based on the current key state.


474-474: Smart UI enhancement for selection experience.

Adding select-none class when Shift is pressed prevents text selection during range selection operations, improving the user experience.


497-497: Consistent application of selection mode.

Passing the dynamically determined selection mode to the Thread component ensures consistent behavior throughout the component tree.

@MrgSub MrgSub merged commit c2b655c into staging Mar 19, 2025
5 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Mar 20, 2025
34 tasks
@hiheyhello123 hiheyhello123 deleted the fix/ctrl branch March 20, 2025 20:22
@coderabbitai coderabbitai bot mentioned this pull request Apr 7, 2025
14 tasks
This was referenced Apr 29, 2025
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