fix: channel orchestrator fixes - #1815
Merged
isekovanic merged 8 commits intoAug 11, 2026
Merged
Conversation
isekovanic
requested review from
MartinCupela,
arnautov-anton,
oliverlaz,
santhoshvai,
szuperaz and
vishalnarkhede
as code owners
August 7, 2026 22:30
…nnel-manager' into fix/channel-orchestrator-fixes # Conflicts: # src/pagination/paginators/BasePaginator.ts
MartinCupela
approved these changes
Aug 11, 2026
isekovanic
merged commit Aug 11, 2026
60917e8
into
feat/channel-orchestrator-to-channel-manager
4 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
CLA
Description of the changes, What, Why and How?
Follow-up fixes on top of the
ChannelPaginator/orchestrator migration, all found via on-device testing of the RN SDK. They address reconnect and cold-start scenarios where the channel list would blank, collapse, or stop refreshing.1. fix: properly respect reset and keepPreviousItems
A
reload()(pull-to-refresh / reconnect) refetched the current page instead of page 1: the query shape (carryingoffset/cursor) was derived before the reset restored the initial offset.resetandkeepPreviousItemswere also conflated, so akeepPreviousItemsrefresh paginated instead of resetting.To resolve this, we restore the initial
offset/cursorbefore deriving the query shape onreset: 'yes'and decoupleresetfromkeepPreviousItemsso a refresh can keep items visible without paginating.2. fix: pending item flush update using intermediate state
Follow-up to #1, a
keepPreviousItemsrefresh cleared the item index, so any item ingested during the refresh (i.e an offline-send replay'smessage.newon reconnect) rebuilt the list from empty and collapsed it to just that item.To resolve this, we keep A
keepPreviousItemsrefresh is now fully non-destructive so it surfaces loading without clearing the index/intervals, so concurrent ingests merge into the existing list. The index is cleared only on a genuine reset (resetwithoutkeepPreviousItems).3. fix: cold start double loading state
On cold start, if a channel changed while the app was fully closed, the list flashed loading state into cached channels into a second loading state. The deferred post sync requery went through the first page reset path and repreloaded from the offline DB, but the sync had invalidated that cache, so the preload returned nothing and blanked the list.
To resolve this, we run the post sync re query as a non-destructive refresh (
keepPreviousItems) so the cached channels stay visible while the fresh page loads.4. fix: cold start with pending tasks wiping cached channel list
Cold start with pending offline sends collapsed the list to just the channel(s) with pending sends until the sends completed. The cold-start preload set the displayed items but never populated the item index, so the pending-send replay's
message.newingested into an empty index and rebuilt the list from scratch.To fix this, we seed the preload through the path that populates the index (so a concurrent ingest merges instead of collapsing), and mark loading complete so the deferred query isn't rejected.
5. fix: race condition when the sync process finishes faster than preload
On cold start, if the offline sync finished during the offline-DB preload (it runs independently, off
connection.changed), the deferred "run the real query after sync" callback was registered after the sync had already drained its callback queue — so it never fired. Channels showed from cache but were never queried withwatch, so they stayed unwatched (list reordered on member-level events, but per-channel state froze).To fix this, we make sure that after the preload, we recheck the sync status; if the sync already finished, run the real (watching) query directly instead of scheduling a callback that will never fire. Mirrors the legacy
ChannelManagera bit more closely as this was something left out during the migration.**6. fix: stop boosting channels on new messages so pins stay on top **
A new message in an unpinned channel jumped it above the pinned channels.
updateListsboosted the messaged channel onmessage.new/notification.message_newand the boost aware comparator floats any boosted item above any non-boosted one before the sort is ever consulted and so the messaged channel jumped above the pinned (non-boosted) ones. The boost was a workaround from before60566820taughtingestItemto relocate an in-place mutated channel by its changed sort value; now that a message bumpslast_message_atand the sort floats the channel to the top of its partition on its own, boosting it is redundant and, for a pinned/partitioned sort, wrong.To fix this, we stop boosting on
message.new/notification.message_newand let the sort relocate the messaged channel (to the top of its partition, below pinned). Boosting is kept fornotification.added_to_channel/channel.visible, where the channel'slast_message_atis stale and the sort can't surface it on its own. The boost primitive itself is untouched and stays available for integrators to selectively boost specific channels. I'm not sure if this was the right direction to go in, however boosting on new messages has the potential to completely break the contract of what the BE returns. We should honestly probably have no default boosting at all (as far as I understand it it's meant to be a temporary sorting mechanism that allows for example integrators to put a channel higher up in case we want to do this for certain channels, like for example if a user marks a channel as "notifiable" or something that would jump it to the top in case it was deeper down)Changelog
reload()(pull-to-refresh / reconnect) re-fetching the current page instead of resetting to the first page inBasePaginator, and decoupledresetfromkeepPreviousItems.keepPreviousItemsrefresh (e.g. an offline-send replay on reconnect).