[Bug]: Mobile iOS: "Load earlier activity" stays under the header and snaps back when the loaded page is about one screen tall#31867

opened3d agobyEeliophan

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/mobile

Steps to reproduce

  1. On iOS, open a thread that has older history and whose first loaded page is about one screen tall. The rows fit within the full screen height, but not in the area below the navigation header.
  2. Pull down to reach "Load earlier activity" at the top of the feed.
  3. Release.

Expected behavior

The feed stays at the top. "Load earlier activity" is visible below the header and can be tapped.

Actual behavior

While the finger is down, the button shows up below the header. On release, the feed snaps back to the end and the button goes back under the header. Older history can't be loaded from this thread. The recording shows a running thread (its "Working" timer ticks every second).

Impact

Major degradation or frequent failure

Version or commit

2.2.0

Environment

iPhone 12, iOS 18.7.8 (no Liquid Glass, so the thread header is opaque), T3 Code iOS app 2.2.0

Logs or stack traces

No response

Screenshots, recordings, or supporting files

https://github.com/user-attachments/assets/1b3fe6a6-cb7a-46b6-bbed-424db8fcee9a

Workaround

No response

Likely cause (from reading the code, not yet verified on device)

The thread feed scrolls under covered bands at its edges. The composer overlay (plus the "Waiting on…" bar here) covers the bottom on every iOS version, and it reaches LegendList as part of getContentInsetEnd. On iOS 26+ with Liquid Glass, the transparent header also covers the top. Our LegendList patch passes that as contentInsetStartAdjustment. On iOS 18, as in this report, the header is opaque and the top adjustment is 0.

getIsAtEnd in the patched bundle (and isContentLess in checkAtBottom, which comes from upstream LegendList) treats contentSize < scrollLength as "the content fits", and ignores both bands. When the content is taller than the uncovered area but shorter than scrollLength, the list can scroll, yet every position reports isAtEnd: true. At the resting end position, the first row ("Load earlier activity") is pushed up under the header by up to the composer's height. Pulling down shows it. On release, ThreadFeed re-enables end following from isAtEnd, and maintainScrollAtEnd (which reacts to layout and item-layout changes, such as the ticking "Working" timer) scrolls back to the end.

A focused VM test over the patched bundle fails on main for both bands:

  • Composer only: scrollLength 800, bottom inset 200, contentSize 700, scrolled to 0. getIsAtEnd returns true. It should return false.
  • Glass header only: scrollLength 800, top adjustment 100, contentSize 760, scrolled to the start (-100). getIsAtEnd returns true. It should return false.

The relevant code is the same at the 2.2.0 version bump (4c9c7d0bb6) and on current main. The patch file has not changed since then.

Related: #28125 upgrades Legend List to 3.7.1 and removes this patch. It doesn't mention this case, and says iOS isn't tested on that branch yet.

Filed by

claude (claude-opus-5-5) in Claude Code, running inside T3 Code