…4077)
* Fix jumping to mid page impacting channel.latestMessages which is now used for channel previews
* Fix on loading first page while channel is in mid-page, showing the mid-page messages
* Update CHANGELOG
* Update CHANGELOG.md
* Apply mid-page state cleanup to Chat state layer
Move cleanStaleMidPageStateIfNeeded from ChatChannelController to ChannelUpdater
(callback + async overloads) and call it from Chat.get(watch:) so the async-await
state layer gets the same flicker-prevention as the controller layer.
* Refresh message observers when channel page bounds change
After a mid-page jump no longer wipes `channel.messages`, the message
observers' predicate (which filters by `channel.oldestMessageAt` /
`channel.newestMessageAt`) needs to be re-evaluated against rows that
were already in the FRC results. Core Data does not propagate parent
property changes to child rows, so we nudge each linked message with a
no-op KVO change, which makes the FRC re-test the predicate and drop
out-of-bounds messages from the observed page.
* Add regression test for FRC bounds-change in mid-page jump
Reproduces the bug at the same `BackgroundListDatabaseObserver` layer
that powers `ChatChannelController.messages`: after a mid-page jump
shrinks the channel's page bounds, previously-cached newest messages
must drop out of the active page even though they remain linked to
the channel for `channel.latestMessages`/preview purposes.
Verified the test fails (10 instead of 5 observed) when the touch nudge
in `ChannelUpdater.update` is removed, and passes once it's restored.
* Move FRC nudge for page-bounds changes into ChannelDTO.willSave
Mirrors the existing `truncatedAt` precedent: any change to the channel's
message-page bounds (`oldestMessageAt` / `newestMessageAt`) marks the
linked messages dirty so NSFetchedResultsController re-evaluates its
predicate. This keeps the workaround centralized in the DTO layer and
covers every path that mutates the bounds (including future ones),
removing the ad-hoc bookkeeping previously added to ChannelUpdater.update.
* Tighten latestMessages assertion to ordered ID sequence
Replaces the loose first-only + prefix checks with a single equality
against the full expected `latest-0, latest-1, ...` sequence, so the
test verifies ordering and per-index correspondence in one shot.
* Hide stale mid-page cleanup inside ChannelUpdater.update
Folds the cleanup helper into a synchronous prelude that `update` runs
before dispatching the request. Uses `writableContext.performAndWait`
so any database observers the caller starts in parallel with the
network call see a clean cache. The fast path (no first-page fetch /
no stale state) stays free of any DB write.
`ChatChannelController.synchronize` and `Chat.get(watch:)` collapse
back to a single `update` call. The helper is kept as a discrete
internal method so its four behaviors (clear / skip-no-state /
skip-recovery / skip-pagination) can be unit-tested directly without
driving a full update round-trip.
The ChannelController-level cleanup tests are dropped because the
controller suite uses a mocked updater and can no longer observe the
embedded behavior; coverage moves to ChannelUpdater_Tests where the
real updater runs end-to-end.
* Remove unnecessary comments
* Move private functions
* Remove unnecessary double check
* Drive cleanup tests through update() to keep helper private
The four stale mid-page cleanup tests called the helper directly, which
prevented marking it private. Drive them through the public update()
entry point instead — the cleanup runs synchronously at the top of
update() before the network request reaches the API spy, so the same
seed → call → read → assert pattern works end-to-end.
StreamChat
🐞 Fixed
StreamCDNRequester#4075ChatChannel.latestMessagesbeing wiped on mid-page pagination #4077CurrentUserController.setPushPreferenceandsnoozePushNotificationsnot updating the local push preference state #4085StreamChatUI
🐞 Fixed