-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Accidental swipe in message viewer marks unread email as read #10747
Description
Accidental swipe in message viewer marks unread email as read
Description
When using the swipe left/right gesture to navigate between messages in the message viewer,
a fast but unintentional gesture - a wrist flick, an accidental pocket interaction, or a
quick motion while trying to scroll down - can silently trigger a full page navigation. The
newly-visited message is immediately marked as read, even if the user swipes back in the
same motion.
This is a significant issue for inbox-zero workflows and anyone managing a high-volume inbox,
because unread count and read state are meaningful signals that get corrupted by incidental
gestures.
Steps to reproduce
- Ensure "Mark messages as read when opened" is enabled (Settings → Account → Reading).
- Have at least two unread messages in a folder.
- Open the first message.
- Make a quick, fast flick gesture to the left (toward the next message) - not a slow, deliberate
drag, but a short, fast wrist motion. - Swipe back immediately to return to the original message.
Expected behavior
The adjacent message remains unread. Navigation should only occur - and mark-as-read should
only trigger - on a clearly deliberate, sustained swipe gesture.
Actual behavior
The adjacent message is marked as read. Even if the user returns to it immediately, the
SEEN flag has already been set (both locally and queued for IMAP sync). The unread indicator
disappears.
Root cause (technical)
The message viewer uses ViewPager2 for left/right navigation, with a custom
ItemTouchHelper in MessageViewContainerFragment to manage swipe sensitivity:
// MessageViewContainerFragment.kt
private const val VIEW_PAGER_SWIPE_THRESHOLD = 0.4f
private const val VIEW_PAGER_SWIPE_VELOCITY_THRESHOLD = 0.8fThe ItemTouchHelper.onSwiped callback fires on either condition:
- The swipe covers >= 40% of screen width, OR
- The swipe velocity exceeds 0.8x the ItemTouchHelper default
Condition (2) is the problem. At 0.8x of the default, a casual wrist flick consistently
triggers a page change even though the screen barely moves. onSwiped then calls
viewPager.currentItem += 1, which causes the adjacent MessageViewFragment to become
the active page and call markMessageAsOpened() in onResume().
Proposed fix - Phase 1 (minimal)
Raise the velocity threshold significantly so that only a forceful, deliberate flick completes
a swipe. The distance threshold (40%) is already reasonable and can stay.
private const val VIEW_PAGER_SWIPE_THRESHOLD = 0.4f
private const val VIEW_PAGER_SWIPE_VELOCITY_THRESHOLD = 3.0f // was 0.8fProposed enhancement - Phase 2 (deferred)
Decouple mark-as-read from the moment the fragment is first resumed. Instead, mark as read
only after a meaningful reading signal:
- The user has remained on the message for ≥ N seconds (e.g. 2s)
- The user has scrolled within the email body
- The user navigates back to the message list
This would be implemented with a Handler.postDelayed() in MessageViewFragment.onResume(),
cancelled in onPause() if the user leaves before the delay fires.
Proposed enhancement - Phase 3 (UX, deferred)
Add a visible read/unread state indicator to the message viewer toolbar, so users can
immediately see whether an email has been marked read during their session and act on it.
Currently, the toggle read/unread action exists but is buried in the overflow menu, or appearing as a rather confusing icon button. Surfacing the current state - and ideally a one-tap toggle - in the toolbar would make accidental
mark-as-read recoverable easily.
Environment
- Reproducible on: Thunderbird for Android (current Gplay production app)
- Affected setting: "Mark messages as read when opened" (enabled by default)
- Not affected: users with that setting disabled
Metadata
Metadata
Assignees
Labels
Type
Projects
Status