Add GroupedQueryChannels and grouped unread counts#6437
Draft
VelikovPetar wants to merge 43 commits into
Draft
Conversation
…Handler.handleChatEvent`.
…to feature/grouped-channels-endpoint
# Conflicts: # stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/querychannels/internal/QueryChannelsLogicTest.kt # stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/querychannels/internal/QueryChannelsStateLogicTest.kt
Contributor
PR checklist ✅All required conditions are satisfied:
🎉 Great job! This PR is ready for review. |
Contributor
SDK Size Comparison 📏
|
Contributor
|
DB Entities have been updated. Do we need to upgrade DB Version? |
- Guard GroupAwareChatEventHandlerFactory auto-install in LogicRegistry to be idempotent (preserve any factory already installed on the state). - Defensively reset channelsOffset on first-page applyGroupedResult. - Rename QueryChannelsIdentifier.Grouped.group to .groupKey for naming consistency with the rest of the grouped surface. - Add grouped-channels tests: QueryChannelsLogicGroupedTest covers applyGroupedResult and loadOfflineGroupedChannels; extend LogicRegistryTest with Grouped identifier coverage. - Doc tweaks: SyncManager.updateGroupedQueryChannels assumption, DefaultChannelGroupResolver currentGroup intent, drop stale @Suppress("LongMethod") on observeQueryChannelsInternal. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
# Conflicts: # stream-chat-android-state/src/test/java/io/getstream/chat/android/state/event/handler/internal/EventHandlerSequentialTest.kt
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.


Goal
Add support for the server-driven grouped-channels API (
POST /channels/grouped), where the backend partitions the channel list into named groups (e.g.direct,support) and returns per-group channels, pagination cursors, and unread counts. Surface those grouped unread counts on relevant chat events, and provide a ComposeChannelListViewModelpath that drives a UI off a group key without the consumer needing to know about filter/sort.Implementation
ChatClient.queryGroupedChannels(limit, groups, watch, presence)returningGroupedChannels(per-groupchannels+unreadChannels+next/prevcursors). Per-group request options viaGroupedChannelsGroupQuery. Backed byPOST /channels/grouped(ChannelApi).QueryGroupedChannelsListener; theStatePluginimplementation merges returned per-group unread counts intoGlobalState.groupedUnreadChannelsand routes each returned group into a state keyed by a new sealedQueryChannelsIdentifier:QueryChannelsIdentifier.Standard(filter, sort)— existing offset-paginated pathQueryChannelsIdentifier.Grouped(groupKey)— new cursor-paginated pathQueryChannelsLogicbranches on identifier.applyGroupedResultreplaces channels on the first page (resettingchannelsOffsetdefensively to keep the Standard offset paginator from picking up stale state), appends on subsequent pages (driven off the request'snextcursor), and persists per-group state under agroupKey-derived DB key.HasGroupedUnreadChannelsmarker onNewMessageEvent,NotificationMessageNewEvent,NotificationMarkReadEvent,NotificationMarkUnreadEvent,NotificationChannelDeletedEvent,NotificationChannelTruncatedEvent.EventHandlerSequentialupdatesGlobalState.groupedUnreadChannelswhenever an inbound event carries the map.GroupedUnreadChannelsUpdateris the single calculator: events with a non-null map replace the current state,channel.updated/channel.updated_by_userevents migrate per-group counts when the channel'sgroupfield changes, andqueryGroupedChannelsresults merge per-group counts.GroupAwareChatEventHandlerclassifies channel-bearing events using a pluggableChannelGroupResolver. The default resolver readschannel.extraData["group"]and always includes an"all"sentinel. Channels are routed Add/Remove/Skip per inbound group. TheLogicRegistryauto-install of the default factory is idempotent — it won't clobber a factory another caller has already installed on the state. Member/CID events delegate toDefaultChatEventHandlerunchanged.ChannelListViewModel(chatClient, groupKey, ...)constructor + matchingChannelViewModelFactory(chatClient, groupKey, ...). Wires the VM to the identifier-keyed state viainitGroupedQueryChannelsAsState, with a group-aware event handler factory keyed ongroupKey. Pagination uses cursor-basedqueryGroupedChannels(groups = mapOf(groupKey to GroupedChannelsGroupQuery(next = cursor))). The Standard path is untouched.SyncManager.restoreActiveChannels()splits standard vs grouped reconnect paths. Grouped queries are refreshed via a singlequeryGroupedChannels()call; manually-watched channels are re-watched viaWatchedChannelRecord/WatchedChannelStateFlow(weak-referenced fromStateRegistry). Recovery assumes all active grouped queries share the same request-levellimit/watch/presenceflags — the first captured config wins.QueryChannelsSpec: new optionalgroupKeyfield for grouped identity.cidsremains a mutablevarfor backward compatibility with prior versions; the two-arg constructor and 2-argcopyare preserved for source/binary compat.groupKeycolumn onQueryChannelsEntity. Uses the existingfallbackToDestructiveMigrationstrategy.Testing
Unit-test coverage added for each layer:
ChatClientGroupedChannelsApiTestsMoshiChatApiTest,QueryGroupedChannelsResponseAdapterTestgrouped_unread_channelsfield):EventMappingTestArgumentsGroupAwareChatEventHandlerTest,DefaultChannelGroupResolverTestGroupedUnreadChannelsUpdaterTestQueryGroupedChannelsListenerStateTestSyncManagerTestStateRegistryTest,QueryChannelsMutableStateTestLogicRegistryTestQueryChannelsLogicgrouped behavior:QueryChannelsLogicGroupedTestcoversapplyGroupedResult(first-page replace, subsequent-page append, cursor/end-of-channels, DB persistence, defensivechannelsOffsetreset, no-op on Standard) andloadOfflineGroupedChannels(cache load, race-condition guard, null cache, no-op on Standard)ChatClientStateCallsTestgroupedUnreadChannelspropagation:EventHandlerSequentialTestManually verified the Compose sample app in both Standard and Grouped modes: initial render, cursor pagination, event-driven Add/Remove/Skip across groups, reconnect/recovery, and grouped unread counts updating from inbound events.