Skip to content

compose: Introduce AvatarPresenceIndicator and hide offline avatar indicator by default#6518

Merged
andremion merged 10 commits into
developfrom
andrerego/and-1257-avatar-presence-indicator
Jun 25, 2026
Merged

compose: Introduce AvatarPresenceIndicator and hide offline avatar indicator by default#6518
andremion merged 10 commits into
developfrom
andrerego/and-1257-avatar-presence-indicator

Conversation

@andremion

@andremion andremion commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Goal

Today the Compose SDK always draws a presence dot on user avatars when the indicator is requested: green when the user is online and grey when offline. This does not match the design system ("Common / Avatar Behavior") or the other Stream SDKs, which show the indicator only when the user is online.

This change hides the indicator when the user is offline by default, and gives integrators per-call control over the presence state. It is aligned with the iOS SwiftUI AvatarIndicator.

Linear: https://linear.app/stream/issue/AND-1257

Implementation

  • Added a public AvatarPresenceIndicator enum with Online, Offline, and None, mirroring the iOS SwiftUI AvatarIndicator.
  • UserAvatar and ChannelAvatar now take indicator: AvatarPresenceIndicator = None. The shared OnlineIndicator draws green for Online, grey for Offline, and nothing for None. This is the single place the dot is rendered, so every screen that shows a presence indicator is covered.
  • The default for the SDK screens changes: an online user shows a green dot, an offline user shows nothing. The grey offline dot is now an opt-in: pass AvatarPresenceIndicator.Offline, or override the avatar in ChatComponentFactory. There is no theme flag, which matches how iOS does it.
  • Non-breaking. The previous showIndicator: Boolean parameter is deprecated and kept on both composables and on UserAvatarParams / ChannelAvatarParams, alongside the new indicator. The deprecated path preserves today's behavior (green online, grey offline) so existing integrations do not change silently. The factory resolves params.indicator, falling back to the deprecated showIndicator when indicator is not set.
  • Migrated every SDK call site that showed the indicator (channel info, member info, members list, channel actions, search results, thread items) to the online-only mapping.

🎨 UI Changes

Avatars that show a presence indicator no longer draw the grey dot when the user is offline; they show nothing. Online avatars are unchanged (green dot). This is visible on the channel and contact info screens, the members list, the channel actions sheet, search results, and thread items.

The updated Paparazzi snapshots in this PR capture the before/after (the grey offline dot is removed from UserAvatarTest, ThreadListTest, and MentionListTest).

Testing

Manual steps in the Compose sample app:

  1. Open a 1:1 channel where the other user is offline. The avatar in the channel header and on the channel info screen shows no presence dot. When that user is online, it shows a green dot.
  2. Open Group Info for a group with both online and offline members. Online members show a green dot; offline members show no dot (previously grey).
  3. Long-press a 1:1 channel to open the channel actions sheet. The dot is shown only when the other user is online.
  4. Open search results and the thread list. Author avatars show the dot only when the author is online.
  5. To verify the opt-in: in a ChatTheme componentFactory override of UserAvatar, pass indicator = AvatarPresenceIndicator.Offline (or if (user.online) AvatarPresenceIndicator.Online else AvatarPresenceIndicator.Offline) and confirm the grey dot returns for offline users.

AvatarPresenceIndicatorTest covers the resolver mapping (online, offline with and without the opt-in, the channel "any member online" rule, and the null current user case).

Summary by CodeRabbit

  • New Features

    • Added richer avatar presence states, including online, offline, and hidden/none.
    • Avatars across chat screens now show presence more accurately based on user or channel status.
  • Bug Fixes

    • Replaced several always-on or always-off presence indicators with status-driven behavior.
    • Updated avatar display in lists, member views, search results, threads, pinned messages, polls, and channel info screens.
  • Tests

    • Added coverage for presence indicator behavior across users and channels.

@github-actions

github-actions Bot commented Jun 24, 2026

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.

@andremion andremion added the pr:improvement Improvement label Jun 24, 2026
@andremion

Copy link
Copy Markdown
Contributor Author

@CodeRabbit review

@coderabbitai

coderabbitai Bot commented Jun 24, 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.

@github-actions

github-actions Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

SDK Size Comparison 📏

SDK Before After Difference Status
stream-chat-android-client 5.90 MB 5.90 MB 0.00 MB 🟢
stream-chat-android-ui-components 11.15 MB 11.15 MB 0.00 MB 🟢
stream-chat-android-compose 12.61 MB 12.62 MB 0.00 MB 🟢

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Introduces an AvatarPresenceIndicator enum (Online, Offline, None) to replace the boolean showIndicator parameter in UserAvatar, ChannelAvatar, OnlineIndicator, and their params types. Resolver extensions on User and Channel compute the enum value. Deprecated boolean overloads preserve backward compatibility. All existing call sites are migrated.

Changes

AvatarPresenceIndicator enum API migration

Layer / File(s) Summary
AvatarPresenceIndicator enum and resolver extensions
...avatar/AvatarPresenceIndicator.kt, ...avatar/AvatarPresenceIndicatorTest.kt
Defines the AvatarPresenceIndicator public enum (Online, Offline, None) and internal resolver extensions for User and Channel delegating to resolveAvatarPresenceIndicator. Tests cover all resolution branches including showWhenOffline and null current user.
UserAvatar, ChannelAvatar, and OnlineIndicator core refactor
...avatar/UserAvatar.kt, ...avatar/ChannelAvatar.kt, ...avatar/OnlineIndicator.kt
UserAvatar and ChannelAvatar replace showIndicator: Boolean with indicator: AvatarPresenceIndicator, threading the enum through SimpleGroupAvatar, StackedGroupAvatar, WithChannelIndicator, and OnlineIndicator. OnlineIndicator switches from boolean to enum-based color selection with early return for None. Deprecated boolean overloads are added on both composables.
ChatComponentFactoryParams and ChatComponentFactory wiring
...theme/ChatComponentFactoryParams.kt, ...theme/ChatComponentFactory.kt, api/stream-chat-android-compose.api
UserAvatarParams and ChannelAvatarParams gain a nullable indicator: AvatarPresenceIndicator? taking precedence over the deprecated showIndicator boolean. ChatComponentFactory computes the effective indicator from params, falling back to avatarPresenceIndicator(showWhenOffline=true) when showIndicator is true. API surface updated accordingly.
Call site migrations across screens and components
...channel/info/ChannelInfoMemberInfoModalSheet.kt, ...channel/info/DirectChannelInfoScreen.kt, ...channel/info/GroupChannelInfoScreen.kt, ...channels/info/ChannelActionsSheet.kt, ...channels/list/SearchResultItem.kt, ...components/channels/ChannelMembersItem.kt, ...threads/ThreadItem.kt, ...attachments/preview/internal/MediaGalleryPhotosMenu.kt, ...channel/info/AddMembersScreen.kt, ...components/poll/PollAnswers.kt, ...components/poll/PollVoteItem.kt, ...messages/composer/internal/suggestions/MentionSuggestionItem.kt, ...pinned/internal/DefaultPinnedMessageItemContent.kt
All call sites using showIndicator = true now pass indicator = user.avatarPresenceIndicator() or channel.avatarPresenceIndicator(currentUser). Call sites using showIndicator = false remove the argument, relying on the None default.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • GetStream/stream-chat-android#6513: Modifies the same DirectChannelInfoScreen and GroupChannelInfoScreen avatar container call sites to add isMuted support, directly overlapping with this PR's avatar parameter changes in those screens.

Suggested labels

pr:new-feature

Suggested reviewers

  • gpunto

Poem

🐇 Hoppity hop, the boolean's gone,
Three states now shine where one plodded on —
Online, Offline, or quietly None,
Each little avatar knows what it's done.
The enum has leapt, the old flag is done! 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: introducing AvatarPresenceIndicator and changing offline avatar dots by default.
Description check ✅ Passed The description includes Goal, Implementation, UI Changes, and Testing, which covers the required template sections well.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch andrerego/and-1257-avatar-presence-indicator

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

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/avatar/UserAvatar.kt`:
- Around line 96-102: The deprecation quick-fix in UserAvatar’s `@Deprecated`
ReplaceWith mapping is incorrect because it does not preserve the showIndicator
= false path and may not import AvatarPresenceIndicator for the caller. Update
the ReplaceWith expression in UserAvatar so it maps false to
AvatarPresenceIndicator.None and true to the existing online/offline logic, and
add AvatarPresenceIndicator to the imports list so the IDE replacement resolves
correctly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 923afc44-2557-4086-8804-f138effea796

📥 Commits

Reviewing files that changed from the base of the PR and between 2a4b714 and 0a9819f.

⛔ Files ignored due to path filters (6)
  • stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.avatar_UserAvatarTest_user_avatar.png is excluded by !**/*.png
  • stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.mentions_MentionListTest_loaded_mention_list.png is excluded by !**/*.png
  • stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.mentions_MentionListTest_loading_more_mention_list.png is excluded by !**/*.png
  • stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.threads_ThreadListTest_loaded_threads.png is excluded by !**/*.png
  • stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.threads_ThreadListTest_loaded_threads_with_unread_banner.png is excluded by !**/*.png
  • stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.threads_ThreadListTest_loading_more_threads.png is excluded by !**/*.png
📒 Files selected for processing (21)
  • stream-chat-android-compose/api/stream-chat-android-compose.api
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/internal/MediaGalleryPhotosMenu.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/channel/info/AddMembersScreen.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/channel/info/ChannelInfoMemberInfoModalSheet.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/channel/info/DirectChannelInfoScreen.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/channel/info/GroupChannelInfoScreen.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/channels/info/ChannelActionsSheet.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/channels/list/SearchResultItem.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/avatar/AvatarPresenceIndicator.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/avatar/ChannelAvatar.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/avatar/OnlineIndicator.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/avatar/UserAvatar.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/channels/ChannelMembersItem.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollAnswers.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollVoteItem.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/composer/internal/suggestions/MentionSuggestionItem.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/pinned/internal/DefaultPinnedMessageItemContent.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/ChatComponentFactory.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/ChatComponentFactoryParams.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/threads/ThreadItem.kt
  • stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/avatar/AvatarPresenceIndicatorTest.kt
💤 Files with no reviewable changes (6)
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollVoteItem.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/composer/internal/suggestions/MentionSuggestionItem.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollAnswers.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/internal/MediaGalleryPhotosMenu.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/channel/info/AddMembersScreen.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/pinned/internal/DefaultPinnedMessageItemContent.kt

@andremion andremion marked this pull request as ready for review June 24, 2026 15:56
@andremion andremion requested a review from a team as a code owner June 24, 2026 15:56
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
56.7% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@gpunto gpunto left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LG, just left a doubt but probably it can be ignored

@andremion andremion merged commit 69b4b3b into develop Jun 25, 2026
18 of 19 checks passed
@andremion andremion deleted the andrerego/and-1257-avatar-presence-indicator branch June 25, 2026 12:01
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