fix(mobile): keep a parked chat reader steady while a reply streams (#39209)

React Native Web repeats the last scroll event 100 ms after scrolling
stops. The page list cleared its record of its own scroll write on the
first matching event, so the repeat after each place-keeping correction
counted as the reader scrolling. For a parked reader that froze the
streaming reply below for 300 ms about every 0.7 s and released it in
one step, which showed as lurches of 20-40 px.
This commit is contained in:
Kelvin Amoaba
2026-11-07 04:55:58 +00:00
committed by GitHub
parent 2980a713c8
commit 0e5d2cb5ea
2 changed files with 17 additions and 1 deletions
@@ -433,6 +433,21 @@ it('holds back a change below a scrolling reader until they rest, then keeps the
expect(chat0.scroller.top(mobileNativeChatRowId('m4'))).toBe(line)
})
it('a parked reader stays parked when React Native Web repeats the scroll event of its own correction', () => {
const rows = Array.from({ length: 12 }, (_, i) => say(`m${i}`, i % 2 ? 'assistant' : 'user', 'x'))
const chat0 = mount(rows)
chat0.read(() => chat0.scroller.showFrom(40))
const line = chat0.scroller.top(mobileNativeChatRowId('m4'))
const n1 = say('n1', 'assistant', 'reply')
chat0.show([...rows, n1])
chat0.scrolled()
// React Native Web sends the last scroll event again 100 ms after scrolling stops.
chat0.scrolled()
chat0.show([...rows, n1, say('n2', 'assistant', 'more')])
expect(chat0.drawn(mobileNativeChatRowId('n2'))).toBe(1)
expect(chat0.scroller.top(mobileNativeChatRowId('m4'))).toBe(line)
})
it('releases a held change once a chat hidden mid-scroll is shown again and the reader rests', () => {
const rows = Array.from({ length: 12 }, (_, i) => say(`m${i}`, i % 2 ? 'assistant' : 'user', 'x'))
const chat0 = mount(rows)
@@ -361,8 +361,9 @@ export function MobileNativeChatPageList({
return
}
const byReader = own.current === null || Math.abs(s.scrollTop - own.current) >= 1
own.current = null
// RN Web repeats the last scroll event 100 ms after scrolling stops; keep our write so that repeat is ours too.
if (byReader) {
own.current = null
readerScrolledAt.current = Date.now()
}
onEndDistance(Math.max(0, -s.scrollTop), byReader)