Skip to content

Handle disabled, hidden and partially updated grouped channels - #6864

Open
andremion wants to merge 7 commits into
developfrom
andrerego/and-1672-grouped-lists-event-handling-gaps-develop
Open

andremion wants to merge 7 commits into
developfrom
andrerego/and-1672-grouped-lists-event-handling-gaps-develop

Conversation

@andremion

@andremion andremion commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Goal

Closes AND-1672

Ports the grouped list event handling fixes from #6855 (v6) to develop.

Implementation

  • A disabled channel is removed from grouped lists, and re-enabling it adds it back. The checks read the typed Channel.disabled.
  • A new message un-hides a channel only when the server does: a regular message or a reply that is not shadowed, has no campaign id, and was not sent in a blocked channel or by a blocked user. The same rule applies to both channel logics (legacy and new), the stored channel and grouped lists. The new logic also applies it to thread replies shown in the channel.
  • notification.message_new, channel updates and truncations keep the stored hidden state, since their channels carry no state of the current user. blocked was already kept.
  • A grouped list queried without watch no longer watches a partially updated channel. It adds the stored copy with the update applied, or skips a channel it has no local copy of.
  • A channel that an event adds to a list without active channel state now gets its state and is stored with the query, like channels from a query result.
  • Group badges don't move for hidden or disabled channels, which the backend doesn't count.
  • ChannelPartialUpdateResponse is regenerated with last_message_at, from the head of GetStream/chat#18902, which is not merged yet. A partial update keeps the later of the local and the event value, in the stored channel and in the channel state. A channel that is not in the list is checked against the cutoff when it has a local copy and the event carries last_message_at; otherwise only its group is checked.

Testing

Manual, on a grouped channel list, changing channels from another client or the dashboard:

  1. Disable a listed channel: it leaves the tab. Enable it again: it comes back.
  2. Hide a channel, then send a message in it from a user the current user blocked: it stays hidden. Send a regular message from another user: it comes back.
  3. On a grouped list queried without watch, partially update a channel so it moves into the group. A channel that was never loaded is not watched and shows after the next refresh. A channel stored locally appears without a watch request.

Validation:

  • ./gradlew spotlessCheck detekt apiCheck testDebugUnitTest passes, after a rebase on the latest develop.
  • Each behaviour was mutation-checked: reverting it makes at least one unit test fail.

Summary by CodeRabbit

  • Bug Fixes
    • Hidden channels now remain hidden after system, campaign, shadowed, or otherwise ineligible messages. Eligible regular messages and replies can still reveal them.
    • Disabled channels are excluded from grouped unread results.
    • Partial channel updates preserve the newest message timestamp and respect watch settings while retaining existing channel state.

The backend leaves disabled channels out of grouped queries. A channel update or partial update that disables a listed channel now removes it, re-enabling it adds it back, and events for a disabled channel no longer add it. The grouped checks read the typed `Channel.disabled`.
…the server

A new message shows a hidden channel again only when it is a regular message or a reply that is not shadowed, has no campaign id, and was not sent in a blocked channel or by a blocked user. The channel state (both channel logics), the stored channel and grouped lists follow the same rule.
The channels in notification.message_new, channel.updated, channel.updated_by_user and both truncated events are shared by all members, so they carry no hidden value. The stored hidden state is now kept, so a later message that does not un-hide the channel keeps it hidden. The stored blocked state was already kept.
…hout watch

A partial update that moves a channel into a grouped list queried without watch now adds the stored copy with the update applied, instead of watching the channel. A channel with no local copy is skipped and shows when its page loads.
A channel that an event adds to a list without active state is now added like a channel from a query result: its state is created and the query is stored, so later events and restarts keep it up to date. A channel with active state is still only tracked, so a stored copy does not replace its live state.
The backend does not count hidden or disabled channels in grouped unread counts, so a group change of such a channel no longer moves a badge.
…the event

ChannelPartialUpdateResponse is regenerated with last_message_at, and a partial update keeps the later of the local and the event value, in the stored channel and in the channel state. A stored channel that is not in the list is checked against the cutoff when the event carries its last message time; without it, only the group is checked, as before.
@andremion andremion added the pr:improvement Improvement label Oct 21, 2026
@andremion

Copy link
Copy Markdown
Contributor Author

@CodeRabbit review

@github-actions

Copy link
Copy Markdown
Contributor

PR checklist ✅

All required conditions are satisfied:

  • Title length is OK (or ignored by label).
  • At least one pr: label exists.
  • Sections ### Goal, ### Implementation, and ### Testing are filled, or the PR is bot-authored.
  • An issue is linked (Linear ticket or GitHub issue), or the PR is bot-authored.

🎉 Great job! This PR is ready for review.

@github-actions

Copy link
Copy Markdown
Contributor

SDK Size Comparison 📏

SDK Before After Difference Status
stream-chat-android-client 6.25 MB 6.25 MB 0.00 MB 🟢
stream-chat-android-ui-components 11.57 MB 11.57 MB 0.00 MB 🟢
stream-chat-android-compose 13.23 MB 13.24 MB 0.00 MB 🟢
stream-chat-android-ai-compose 2.07 MB 2.07 MB 0.00 MB 🟢
stream-chat-android-ai-ondevice 1.67 MB 1.67 MB 0.00 MB 🟢

@sonarqubecloud

Copy link
Copy Markdown

@coderabbitai

coderabbitai Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Oct 21, 2026

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 39e2f9c3-07f3-4fb9-8fd3-1d176d3ef68b
📥 Commits

Reviewing files that changed from the base of the PR and between 9806817 and 222aac9.

📒 Files selected for processing: 37
  • stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/mapping/DomainMapping.kt
  • stream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/state/event/handler/grouped/internal/GroupAwareChatEventHandler.kt
  • stream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/state/event/handler/grouped/internal/GroupAwareChatEventHandlerFactory.kt
  • stream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/state/event/handler/grouped/internal/GroupedChannelFilter.kt
  • stream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/state/event/handler/grouped/internal/GroupedUnreadChannelsUpdater.kt
  • stream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/state/event/handler/internal/EventHandlerSequential.kt
  • stream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/state/plugin/logic/channel/internal/ChannelEventHandlerImpl.kt
  • stream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/state/plugin/logic/channel/internal/legacy/ChannelEventHandlerLegacyImpl.kt
  • stream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/state/plugin/logic/channel/internal/legacy/ChannelStateLogic.kt
  • stream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/state/plugin/logic/internal/LogicRegistry.kt
  • stream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/state/plugin/logic/querychannels/internal/GroupedPartialUpdateWatch.kt
  • stream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/state/plugin/logic/querychannels/internal/QueryChannelsLogic.kt
  • stream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/state/plugin/logic/querychannels/internal/QueryChannelsStateLogic.kt
  • stream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/state/utils/internal/ChannelPartialUpdate.kt
  • stream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/state/utils/internal/ChannelVisibility.kt
  • stream-chat-android-client/src/main/java/io/getstream/chat/android/network/models/ChannelPartialUpdateResponse.kt
  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/EventChatJsonProvider.kt
  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/api2/mapping/DomainMappingTest.kt
  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/api2/mapping/EventMappingTestArguments.kt
  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/internal/state/channel/controller/WhenHandleEvent.kt
  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/internal/state/event/handler/grouped/internal/GroupAwareChatEventHandlerTest.kt
  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/internal/state/event/handler/grouped/internal/GroupedChannelFilterTest.kt
  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/internal/state/event/handler/grouped/internal/GroupedUnreadChannelsUpdaterTest.kt
  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/internal/state/event/handler/internal/EventHandlerSequentialHiddenChannelTest.kt
  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/internal/state/event/handler/internal/EventHandlerSequentialPartialUpdateTest.kt
  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/internal/state/event/handler/internal/EventHandlerSequentialTest.kt
  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/internal/state/plugin/logic/channel/internal/ChannelEventHandlerImplTest.kt
  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/internal/state/plugin/logic/channel/internal/legacy/ChannelEventHandlerLegacyImplTest.kt
  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/internal/state/plugin/logic/channel/internal/legacy/ChannelStateLogicTest.kt
  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/internal/state/plugin/logic/internal/LogicRegistryTest.kt
  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/internal/state/plugin/logic/querychannels/internal/QueryChannelsLogicGroupedUnreadBaseTest.kt
  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/internal/state/plugin/logic/querychannels/internal/QueryChannelsLogicGroupedWatchTest.kt
  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/internal/state/plugin/logic/querychannels/internal/QueryChannelsLogicTest.kt
  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/internal/state/plugin/logic/querychannels/internal/QueryChannelsStateLogicTest.kt
  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/internal/state/utils/internal/ChannelPartialUpdateTest.kt
  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/internal/state/utils/internal/ChannelVisibilityTest.kt
  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/parser/EventArguments.kt

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 3 remain after this review.

📝 Walkthrough

Message events now reveal hidden channels only when they meet the message, campaign, channel-block, and user-block conditions. Partial channel updates carry lastMessageAt, and merges preserve the later timestamp. Grouped query handling uses watch configuration, cached channel state, and message timestamps to select update results. Grouped-channel eligibility and unread checks now use the channel’s disabled and hidden state.

Priority: ⬇️ Low

Merge Risk: ⚪ Minimal · up to 222aa

No confirmed issue prevents merging after normal checks.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 18.72% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 187 functions across 37 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title clearly summarizes the primary changes for disabled, hidden, and partially updated grouped channels.
Description check Passed The description includes the goal, implementation details, testing steps, validation results, and linked issue. It omits the template's explicit Checklist section, but the required information is othe…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Description check

Explanation

The description includes the goal, implementation details, testing steps, validation results, and linked issue. It omits the template's explicit Checklist section, but the required information is otherwise substantially complete.

✨ Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

A rabbit hops where messages flow
Some channels wake; some stay below
A timestamp joins the channel’s tale
Grouped counts follow the hidden trail
The rabbit thumps and twitches ears
Then bounds away through code review cheers ✨

Comment @coderabbitai help to get the list of available commands.

@andremion
andremion marked this pull request as ready for review October 21, 2026 15:35
@andremion
andremion requested a review from a team as a code owner October 21, 2026 15:35

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr:improvement Improvement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants