Skip to content

Fix email reply, scheduling, and message handling bugs#3926

Open
jbecke wants to merge 1 commit into
mainfrom
claude/sweet-hypatia-we17ol
Open

Fix email reply, scheduling, and message handling bugs#3926
jbecke wants to merge 1 commit into
mainfrom
claude/sweet-hypatia-we17ol

Conversation

@jbecke

@jbecke jbecke commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

This PR addresses multiple bugs and edge cases in the email compose and message handling flows, with a focus on improving reliability and correctness.

Summary

Fixes several issues in email reply composition, message scheduling, recipient handling, and message scrolling. Includes improvements to error handling, state management, and email address comparison logic.

Key Changes

Compose & Scheduling:

  • Fixed schedule message flow to clear send time on failure, preventing phantom "scheduled" state
  • Added try-catch error handling around scheduleMessage and flagArchived calls
  • Fixed restoreUndoCallback to only clear when the current instance owns it, preventing interference between multiple BaseInput instances
  • Changed onMount to createEffect for form callbacks to handle re-keying when reply context changes

Email Address Handling:

  • Added emailsMatch() utility for case-insensitive email comparison
  • Updated recipient conversion logic to use case-insensitive matching throughout
  • Fixed reply-all recipient filtering to properly handle email case variations

Message Display & Scrolling:

  • Fixed scrollToMessage() to check bounds before applying reversed index calculation
  • Removed unnecessary sorting in firstUnreadMessageId memo (messages already sorted in context)
  • Improved message loading loop with disposal flag to prevent operations after unmount
  • Fixed loadMessagesUntilFound to return false when disposed instead of infinite loop

Reply Handling:

  • Fixed reply append toggle guard to check db_id instead of replying_to_id (root messages have no replying_to_id)
  • Improved buildHeaderDescriptor to handle missing sender name/email with fallback to empty string
  • Fixed $findPreviousEmailNode generator to use return instead of yield when no replyingToID

Draft Content Detection:

  • Updated prepareEmailBody to exclude quoted thread text when extracting plain text, preventing empty replies from being mistaken as having content
  • Clones body and removes .macro_quote elements before extracting text

Subject Line Handling:

  • Improved getSubjectText to use case-insensitive regex for detecting existing "Re:" prefixes
  • Added comprehensive test coverage for subject line prefix handling

HTML/Text Conversion:

  • Fixed plainTextToHtml to not double blank lines (empty lines map to '' with join providing the break)
  • Updated test expectations to match corrected behavior

UI & Hotkeys:

  • Removed stubbed reply/reply-all/forward hotkey handlers that were consuming keypresses without functionality
  • Cleaned up unused ref in ComposeToolbar

Code Quality:

  • Improved error handling with try-catch blocks in scheduling flows
  • Better null/undefined handling with optional chaining and fallbacks
  • Removed unused imports and dead code
  • Added comments explaining non-obvious logic (e.g., message time comparison, email matching)

https://claude.ai/code/session_01GJDgV1YUdUHqAB8GDXu5QQ

Bug fixes found in a package-wide review:

Components
- EmailContext: archiveThread no longer archives twice (the unconditional
  mutate ran in addition to mark-done, which also archives and whose undo
  couldn't undo the extra mutate; the else branch was a verbatim duplicate)
- EmailContext: make the thread sort comparator transitive by comparing
  per-message timestamps (internal_date_ts ?? sent_at) instead of requiring
  both messages to share a field
- Block: don't crash rendering threads whose first message has a null
  subject, and make the 'Email' title fallback reachable
- Email: handleTargetMessage no longer double-scrolls (missing else before
  case 3), polling loops stop on unmount, dead checks removed, and
  firstUnreadMessageId stops re-sorting the already-sorted list
- Email: fix vacuous isDraft check (draft !== null was always true)
- EmailMessageBody: fall back to the full sanitized HTML when body_replyless
  is empty and there is no .macro_quote, instead of rendering a blank body;
  don't pass undefined body_text to StaticMarkdown
- MessageActions: fix inverted allActionsHidden check (it tested whether
  hidden actions were valid, not whether all actions were hidden)
- BaseInput: sends now honor the user-selected "from" inbox
  (form().selectedLinkId()) like draft saves already did, so the send goes
  out from the inbox shown in the UI
- BaseInput: only clear the module-level undo-send restore slot if this
  instance still owns it (two inputs can be mounted at once)
- BaseInput: re-attach onDirty/onReplyTypeApplied when the form memo re-keys
  after a send, so autosave keeps working without a remount
- BaseInput: quote toggle works when replying to a thread's root message
  (guard on db_id, not replying_to_id)
- BaseInput/Compose: failed schedule-send no longer leaves the composer
  stuck in a phantom "scheduled" state with Send disabled; schedule errors
  are caught and surfaced
- date-selector: time input validation actually validates (Number.isNaN was
  called on strings)

Utils
- prepareEmailBody: body_text now includes all user content (was truncated
  to the first top-level element after paragraph flattening) while still
  excluding the quoted thread; reply header no longer renders "undefined"
  for a missing sender; malformed data-block-params no longer aborts
  send/draft-save; data-collapsed parsed as === 'true' instead of
  Boolean('false') being true; $findPreviousEmailNode returns instead of
  falling through when no ID is given
- DocumentMentionNode: export the collapsed state as data-collapsed instead
  of a bogus DOMConversionMap attribute, so it survives HTML round trips
- subjectText: null subjects no longer become "Re: null", and existing
  Re:/RE:/re: prefixes are detected case-insensitively at the start of the
  subject only (with tests)
- emailUser/recipientConversion/mentionToCc: email comparisons are now
  case-insensitive via a shared emailsMatch helper, fixing "Me" labels,
  reply-all self-filtering and CC dedupe for case-differing addresses
- plainTextToHtml: a blank source line renders as one blank line, not two
- emailHotkeys: remove stubbed reply/reply-all/forward hotkeys that consumed
  keypresses and showed dead entries in the hotkey UI
- scrollToMessage: check for message-not-found before applying the reversed
  index mapping; drop dead helpers

Also removes dead code: _appendItemsAsMacroMentions, attachButtonRef
plumbing in ComposeToolbar, unused bodyDiv ref in ComposeBody.

https://claude.ai/code/session_01GJDgV1YUdUHqAB8GDXu5QQ
@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@jbecke, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 8 minutes and 23 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 32dc77fe-75b4-4fed-8b1a-9a1b1e3ddda7

📥 Commits

Reviewing files that changed from the base of the PR and between 5e052d1 and ef89871.

📒 Files selected for processing (21)
  • js/app/packages/block-email/component/BaseInput.tsx
  • js/app/packages/block-email/component/Block.tsx
  • js/app/packages/block-email/component/Email.tsx
  • js/app/packages/block-email/component/EmailContext.tsx
  • js/app/packages/block-email/component/EmailMessageBody.tsx
  • js/app/packages/block-email/component/MessageActions.tsx
  • js/app/packages/block-email/component/compose/Compose.tsx
  • js/app/packages/block-email/component/compose/ComposeBody.tsx
  • js/app/packages/block-email/component/compose/ComposeToolbar.tsx
  • js/app/packages/block-email/component/date-selector.tsx
  • js/app/packages/block-email/util/emailHotkeys.ts
  • js/app/packages/block-email/util/emailUser.ts
  • js/app/packages/block-email/util/mentionToCc.ts
  • js/app/packages/block-email/util/plainTextToHtml.test.ts
  • js/app/packages/block-email/util/plainTextToHtml.ts
  • js/app/packages/block-email/util/prepareEmailBody.ts
  • js/app/packages/block-email/util/recipientConversion.ts
  • js/app/packages/block-email/util/scrollToMessage.ts
  • js/app/packages/block-email/util/subjectText.test.ts
  • js/app/packages/block-email/util/subjectText.ts
  • js/lexical-core/nodes/DocumentMentionNode.ts

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

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants