Chat list keeps taking updates after a row with a spinner (stuck "pending", bubble under the composer) #4

Merged
fmodf merged 1 commit from fix/list-stuck-updates into master 2026-10-19 20:17:14 +00:00
Owner

Two reports, one cause:

  • a file sent into a chat had arrived, but the app still showed it as pending;
  • the input bar sat over the messages: a tall reply ended up under the composer, with the jump-down button showing.

Cause. ScrollEngine's DifferenceKit update cleared isApplyingUpdate in a CATransaction completion block. That block runs only after every animation added in the transaction finishes.

  • A row inserted with a spinner (an upload, a pending send) adds the spinner's rotation, which never finishes. So the block never ran.
  • isApplyingUpdate stayed set and isBusy held. Every later update waited in pendingParent until a drag drained it: the row's Confirmed (new stamp, status cleared), the picture's grown size, the next message.

Fix. The update finishes from performBatchUpdates(nil)'s completion, queued after DifferenceKit's batches. That waits for the collection view's own animations only. Stick-to-bottom, the viewport signals, the top load and the drain run there as before.

Tested in the iPhone 18 Pro simulator (iOS 27.0), with a test account on another.im in a group of its own.

Before:

  • New (uploading), Edited (pending), Status (pending) ×2 and Confirmed all reached the chat.
  • The updates after the insert were parked with isApplyingUpdate=1.
  • The photo stayed "Message pending" with the jump-down button up, until the chat was reopened.

After:

  • The photo turns sent in place and the list stays at the bottom.
  • A message typed right after, with the keyboard up, shows in full.

gmake test: 359 tests, 0 failures.

Not reproduced with an incoming message from another member (the test group has one). No unit test: the coordinator is private to the representable and needs a laid-out collection view.

Touches ScrollEngine.swift, as does #3; the two merge cleanly.

🤖 Generated with Claude Code

Two reports, one cause: - a file sent into a chat had arrived, but the app still showed it as pending; - the input bar sat over the messages: a tall reply ended up under the composer, with the jump-down button showing. **Cause.** `ScrollEngine`'s DifferenceKit update cleared `isApplyingUpdate` in a `CATransaction` completion block. That block runs only after every animation added in the transaction finishes. - A row inserted with a spinner (an upload, a pending send) adds the spinner's rotation, which never finishes. So the block never ran. - `isApplyingUpdate` stayed set and `isBusy` held. Every later update waited in `pendingParent` until a drag drained it: the row's `Confirmed` (new stamp, status cleared), the picture's grown size, the next message. **Fix.** The update finishes from `performBatchUpdates(nil)`'s completion, queued after DifferenceKit's batches. That waits for the collection view's own animations only. Stick-to-bottom, the viewport signals, the top load and the drain run there as before. **Tested** in the iPhone 18 Pro simulator (iOS 27.0), with a test account on another.im in a group of its own. Before: - New (uploading), Edited (pending), Status (pending) ×2 and Confirmed all reached the chat. - The updates after the insert were parked with `isApplyingUpdate=1`. - The photo stayed "Message pending" with the jump-down button up, until the chat was reopened. After: - The photo turns sent in place and the list stays at the bottom. - A message typed right after, with the keyboard up, shows in full. `gmake test`: 359 tests, 0 failures. Not reproduced with an incoming message from another member (the test group has one). No unit test: the coordinator is private to the representable and needs a laid-out collection view. Touches `ScrollEngine.swift`, as does #3; the two merge cleanly. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
The chat list keeps taking updates after a row with a spinner goes in: a sent file turns from pending to sent, and a bubble that grows is kept above the composer. Two reports (in Russian): a file sent into a chat "already arrived but our client still shows it as pending", and "the input bar climbs over the messages" (a tall incoming reply left under the composer, with the jump-down button showing). ScrollEngine's DifferenceKit update cleared isApplyingUpdate in a CATransaction completion block, which runs once every animation added in the transaction has finished. A row inserted with a ProgressView (an upload, a pending send) adds the spinner's rotation, which never finishes, so the block never ran: isApplyingUpdate stayed set, isBusy held, and every later update was parked in pendingParent -- the row's Confirmed (moved to the server's stamp, status cleared), the picture's grown size, the next message -- until a drag drained them. The update now finishes from performBatchUpdates(nil)'s completion, queued after DifferenceKit's batches, which waits for the collection view's own animations only; the stick-to-bottom, the viewport signals, the top load and the drain run there as before. Seen in the iPhone 18 Pro simulator (iOS 27.0) with a test account on another.im in a room of its own, with a log of the chat's events and of ScrollEngine's parking: before, New (uploading), Edited (pending), two Status (pending) and Confirmed all reached the chat, the updates after the insert were parked with isApplyingUpdate=1, and the photo stayed "Message pending" with the jump-down button up until the chat was opened again, which showed it sent; after, the photo turns sent in place, the list stays at the bottom, and a message typed with the keyboard up right after shows in full. gmake test: 359 tests, 0 failures. No unit test: the coordinator is private to the representable and needs a laid-out collection view. c241e36f71
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fmodf force-pushed fix/list-stuck-updates from c241e36f71 to ac157a5dc3 2026-10-19 20:10:30 +00:00 Compare
fmodf merged commit 3afdff81ad into master 2026-10-19 20:17:14 +00:00
fmodf deleted branch fix/list-stuck-updates 2026-10-19 20:17:14 +00:00
Sign in to join this conversation.
No reviewers
No labels
done
fixed
returned
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
another-im/anotherim-swift!4
No description provided.