🤖 fix: jump-to-bottom button never disappears on iOS PWA#2674
🤖 fix: jump-to-bottom button never disappears on iOS PWA#2674ibetitsmike wants to merge 7 commits intomainfrom
Conversation
On iOS Safari/PWA, momentum (inertial) scrolling continues after the user lifts their finger, but no touchmove events fire during momentum scrolling. The handleScroll callback used a 100ms window after the last touchmove to distinguish user scrolls from programmatic scrolls — momentum scroll events fell outside this window and were ignored entirely. This meant that when a user scrolled up (showing the button) and then scrolled back down to the bottom via momentum, the scroll events reaching the bottom were classified as 'programmatic' and the button stayed visible forever. Fix: in the non-user-scroll path, still check if we've reached the bottom and re-enable auto-scroll. This is safe because our own programmatic scrolls (performAutoScroll, jumpToBottom) already set autoScroll=true before scrolling, so this branch only fires for momentum/inertial scrolling.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b52c958664
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Only re-enable auto-scroll during non-user-initiated scrolls when the momentum is moving downward toward the bottom. This prevents upward inertia near the bottom from incorrectly snapping the view back down while the user is trying to read older content.
|
@codex review |
|
Codex Review: Didn't find any major issues. Delightful! ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback". |
The previous momentum-based fix was insufficient — on iOS PWA, the 100ms touchmove-based user interaction window is too fragile for all scroll scenarios (slow drags, varied momentum timing, etc.). New approach: a debounced 'scroll settled' check fires 150ms after the last scroll event. If scrolling has stopped and we're at the bottom, re-enable auto-scroll. The 150ms delay ensures upward momentum from the bottom will still be producing scroll events (resetting the timer) before it clears the bottom threshold. Also adds onTouchStart as a user interaction marker alongside onTouchMove, catching edge cases where touchmove doesn't fire.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3451c547ca
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Add disableAutoScroll() that clears the pending scroll-settled timer, so programmatic disables (navigate-to-message, edit-message) aren't undone by the debounced re-enable. The scroll handler's own disable (user scrolls up) deliberately doesn't clear the timer so the debounce can recover when the user scrolls back to the bottom.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 713e3150fb
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Prevents a small upward scroll near the bottom (< threshold) from having its autoScroll disable undone 150ms later. The debounce timer is now only started/restarted when scroll movement is downward, so the user can scroll up slightly to pause reading without the jump button disappearing.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7ad296a5b3
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Add programmaticDisableRef flag set by disableAutoScroll() to block the scroll-settled debounce from re-arming during programmatic scrolls (scrollIntoView after navigate-to-message/edit-message). The flag is cleared when the user touches the scroll container (markUserInteraction), allowing the debounce to work again for subsequent user scrolls.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1fd84c88e2
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
onTouchStart fires for ALL touches including taps on message links, which opens the user-interaction window before scrollIntoView in handleNavigateToMessage. This caused programmatic scrolls near the bottom to be misclassified as user scrolls and re-enable auto-scroll. onTouchMove is the correct event — it only fires during actual drag/scroll gestures, not for taps.
|
@codex review |
|
Codex Review: Didn't find any major issues. Delightful! ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback". |
Summary
Fixes the "Jump to bottom" button staying visible permanently on iOS PWA after it first appears.
Background
On iOS Safari/PWA, the
handleScrollcallback inuseAutoScrollused a 100ms window after the lasttouchmoveto distinguish user scrolls from programmatic scrolls. Multiple iOS scroll scenarios cause this window to expire before the scroll position settles at the bottom:touchmoveevents stop but inertial scroll continuestouchmoveandscrolleventsImplementation
Debounced scroll-settled check (
useAutoScroll.ts): After each downward scroll event, start a 150ms debounce timer. When scrolling stops and the viewport is at the bottom, re-enable auto-scroll. The 150ms delay avoids re-enabling during upward momentum from the bottom.Guards to prevent the debounce from interfering with intentional programmatic disables:
disableAutoScroll(): New method for code-driven disables (navigate-to-message, edit-message). Sets aprogrammaticDisableRefflag that blocks debounce re-arming, and clears any pending timer. Used inChatPane.tsxinstead ofsetAutoScroll(false).programmaticDisableRef: Cleared when the user touches the scroll container (markUserInteractionviaonTouchMove), allowing the debounce to work for subsequent user scrolls.Generated with
mux• Model:anthropic:claude-opus-4-6• Thinking:high• Cost:$1.39