Skip to content

Conversation

@gpunto
Copy link
Contributor

@gpunto gpunto commented Dec 24, 2025

🎯 Goal

While working on the common CI, I realized we are not running Lint only in Chat and that it yields many findings. I'm addressing the findings here, while we'll start running Lint in CI in #6050

🛠 Implementation details

  • Disable some checks
  • Address some findings (e.g. not using remember when we should)
  • Introduce lint baseline for the XML components module

🎨 UI Changes

None

🧪 Testing

The only production change is the remember in showOriginalTextAsState. To test it, you have to:

  • in the sample, set two predefined users to have different languages (e.g. it & en)
  • create a conversation between the two
  • enable translations for the conversations between the two
  • pass showOriginalTranslationEnabled = true to ChatTheme in MessagesActivity
  • send a message in the original language of the author (e.g. a message in Italian from the it user)
  • login as the other user and toggle between "show original" & "show translation"

☑️Contributor Checklist

General

  • I have signed the Stream CLA (required)
  • Assigned a person / code owner group (required)
  • Thread with the PR link started in a respective Slack channel (#android-chat-core or #android-chat-ui) (required)
  • PR is linked to the GitHub issue it resolves

Code & documentation

  • Changelog is updated with client-facing changes
  • New code is covered by unit tests
  • Comparison screenshots added for visual changes
  • Affected documentation updated (KDocs, docusaurus, tutorial)

☑️Reviewer Checklist

  • UI Components sample runs & works
  • Compose sample runs & works
  • UI Changes correct (before & after images)
  • Bugs validated (bugfixes)
  • New feature tested and works
  • Release notes and docs clearly describe changes
  • All code we touched has new or updated KDocs
  • Check the SDK Size Comparison table in the CI logs

🎉 GIF

Please provide a suitable gif that describes your work on this pull request

Summary by CodeRabbit

  • New Features

    • Added MENTIONS and THREADS tabs to channel navigation for improved content organization.
  • Bug Fixes

    • Corrected invalid color values in multimedia attachment components.
  • Chores

    • Updated lint configurations across build files to optimize build processes.
    • Optimized reactive state collection and caching for improved performance.

✏️ Tip: You can customize this high-level summary in your review settings.

@github-actions
Copy link
Contributor

github-actions bot commented Dec 24, 2025

SDK Size Comparison 📏

SDK Before After Difference Status
stream-chat-android-client 5.25 MB 5.25 MB 0.00 MB 🟢
stream-chat-android-offline 5.48 MB 5.48 MB 0.00 MB 🟢
stream-chat-android-ui-components 10.60 MB 10.60 MB 0.00 MB 🟢
stream-chat-android-compose 12.81 MB 12.81 MB 0.00 MB 🟢

@sonarqubecloud
Copy link

@gpunto gpunto marked this pull request as ready for review December 24, 2025 10:03
@gpunto gpunto requested a review from a team as a code owner December 24, 2025 10:03
@coderabbitai
Copy link

coderabbitai bot commented Dec 24, 2025

Walkthrough

The PR applies lint configuration updates across multiple build files to suppress translation-related warnings, optimizes Compose reactive flow collection by introducing remember-wrapped caching to improve recomposition behavior, extends navigation with MENTIONS and THREADS tabs, converts a Composable function to a regular function, and corrects invalid color component values.

Changes

Cohort / File(s) Summary
Lint Configuration
stream-chat-android-client/build.gradle.kts, stream-chat-android-compose/build.gradle.kts, stream-chat-android-ui-common/build.gradle.kts, stream-chat-android-ui-components/build.gradle.kts
Disables MissingTranslation and ExtraTranslation lint rules; adds lint baseline configuration for ui-components module
Reactive Flow Optimization – Sample
stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/.../ChannelsActivity.kt, ChatsActivity.kt
Pre-computes unread count flows (channels, threads) and caches them with remember; improves recomposition by memoizing flow observations
Navigation Extensions
stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/.../ChannelsActivity.kt
Adds MENTIONS and THREADS tab handling to bottom navigation with corresponding composable rendering
Reactive Flow Optimization – Core
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/MessageUtils.kt
Wraps originalTextMessageIds flow transformation with remember(cid, messageId) to cache intermediate Flow
Composable Function Conversion
stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/component/MessageInfoComponentFactory.kt
Removes @Composable annotation from readsOf; adds remember wrapper for state collection in MessageMenu
UI & Guides Updates
stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/adapter/internal/MessageListItemDiffCallback.kt, stream-chat-android-ui-guides/src/main/java/io/getstream/chat/android/guides/.../MultimediaAttachmentFactory.kt
Adds @SuppressLint("DiffUtilEquals") to DiffCallback; corrects invalid color component values (256 → 255) in CustomPlayButton backgrounds

Poem

🐰 A hopping change across the build,
Remember flows now cached and stilled,
Mentions and threads find their place,
Colors corrected, lint rules embrace—
Recomposition flows with grace!

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~20 minutes

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Address Lint findings' directly summarizes the main objective of the PR, which involves addressing Android Lint findings across multiple modules.
Description check ✅ Passed The description includes the Goal, Implementation details, UI Changes, and Testing sections from the template with substantial content in each; however, the Contributor Checklist items remain unchecked and the GIF section is incomplete.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch run-lint

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 and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/adapter/internal/MessageListItemDiffCallback.kt (1)

30-48: Add documentation comment for the lint suppression.

The @SuppressLint("DiffUtilEquals") suppression lacks explanation, which violates the coding guideline to document suppressions. However, the suppression is justified: all compared types—ThreadSeparatorItem, StartOfTheChannelItem, Channel, and Date—are either Kotlin data classes (which auto-generate correct equals() implementations) or standard Java types with proper equality checks.

Add a brief comment explaining why the suppression is safe:

// DiffUtilEquals suppressed: All compared types have correct equals() implementations.
// ThreadSeparatorItem and StartOfTheChannelItem are data classes; Channel is a data class;
// Date is java.util.Date with standard equals().
@SuppressLint("DiffUtilEquals")
override fun areContentsTheSame(oldItem: MessageListItem, newItem: MessageListItem): Boolean {
🧹 Nitpick comments (2)
stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/component/MessageInfoComponentFactory.kt (1)

269-287: Consider creating separate Date instances for preview timestamps.

The same sentDate object is mutated multiple times via apply, causing all timestamps to accumulate incorrectly. Each apply call modifies and returns the same object, so by line 287, sentDate has 6 hours added cumulatively rather than the intended distinct offsets.

🔎 Proposed fix
     val reads = listOf(
         ChannelUserRead(
             user = user1,
             lastReceivedEventDate = Date(),
             unreadMessages = 0,
-            lastRead = sentDate.apply { time += 2.hours.inWholeMilliseconds },
+            lastRead = Date(sentDate.time + 2.hours.inWholeMilliseconds),
             lastReadMessageId = null,
         ),
         ChannelUserRead(
             user = user2,
             lastReceivedEventDate = Date(),
             unreadMessages = 0,
-            lastRead = sentDate.apply { time += 3.hours.inWholeMilliseconds },
+            lastRead = Date(sentDate.time + 3.hours.inWholeMilliseconds),
             lastReadMessageId = null,
         ),
     )
     val deliveredReads = listOf(
         ChannelUserRead(
             user = user3,
             lastReceivedEventDate = Date(),
             unreadMessages = 0,
             lastRead = Date(),
             lastReadMessageId = null,
-            lastDeliveredAt = sentDate.apply { time += 1.hours.inWholeMilliseconds },
+            lastDeliveredAt = Date(sentDate.time + 1.hours.inWholeMilliseconds),
             lastDeliveredMessageId = null,
         ),
     )
stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/feature/channel/list/ChannelsActivity.kt (1)

142-144: Different caching approach: activity-level flows vs. remember-wrapped flows.

These flows are created at the activity level in onCreate (lines 142-144) and collected inside the composable (lines 155-156), which is valid and ensures flows are created once per activity instance. However, this differs from the pattern in ChatsActivity.kt where flows are created inside composables using remember blocks.

While both approaches work correctly, consider standardizing on one pattern across the codebase for consistency. The remember-based approach (as in ChatsActivity) is more idiomatic for Compose as it ties flow lifecycle to composition rather than activity.

💡 Optional refactor to align with ChatsActivity pattern

Move flow creation inside the composable with remember:

     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
 
-        val globalStateFlow = ChatClient.instance().globalStateFlow
-        val unreadChannelsCountFlow = globalStateFlow.flatMapLatest { it.channelUnreadCount }
-        val unreadThreadsCountFlow = globalStateFlow.flatMapLatest { it.unreadThreadsCount }
-
         setContent {
             var selectedTab by rememberSaveable { mutableStateOf(AppBottomBarOption.CHATS) }
+            val globalStateFlow = remember { ChatClient.instance().globalStateFlow }
+            val unreadChannelsCount by remember { globalStateFlow.flatMapLatest { it.channelUnreadCount } }
+                .collectAsStateWithLifecycle(0)
+            val unreadThreadsCount by remember { globalStateFlow.flatMapLatest { it.unreadThreadsCount } }
+                .collectAsStateWithLifecycle(0)
-            val unreadChannelsCount by unreadChannelsCountFlow.collectAsStateWithLifecycle(0)
-            val unreadThreadsCount by unreadThreadsCountFlow.collectAsStateWithLifecycle(0)

Also applies to: 155-156

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 5e4c05f and 92b5f06.

📒 Files selected for processing (11)
  • stream-chat-android-client/build.gradle.kts
  • stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/feature/channel/list/ChannelsActivity.kt
  • stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/chats/ChatsActivity.kt
  • stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/component/MessageInfoComponentFactory.kt
  • stream-chat-android-compose/build.gradle.kts
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/MessageUtils.kt
  • stream-chat-android-ui-common/build.gradle.kts
  • stream-chat-android-ui-components/build.gradle.kts
  • stream-chat-android-ui-components/lint-baseline.xml
  • stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/adapter/internal/MessageListItemDiffCallback.kt
  • stream-chat-android-ui-guides/src/main/java/io/getstream/chat/android/guides/catalog/compose/customizingimageandvideoattachments/factory/MultimediaAttachmentFactory.kt
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{kt,kts}

📄 CodeRabbit inference engine (AGENTS.md)

Format and apply Kotlin style with Spotless (4 spaces, no wildcard imports, licence headers)

Files:

  • stream-chat-android-client/build.gradle.kts
  • stream-chat-android-compose/build.gradle.kts
  • stream-chat-android-ui-common/build.gradle.kts
  • stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/adapter/internal/MessageListItemDiffCallback.kt
  • stream-chat-android-ui-components/build.gradle.kts
  • stream-chat-android-ui-guides/src/main/java/io/getstream/chat/android/guides/catalog/compose/customizingimageandvideoattachments/factory/MultimediaAttachmentFactory.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/MessageUtils.kt
  • stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/feature/channel/list/ChannelsActivity.kt
  • stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/chats/ChatsActivity.kt
  • stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/component/MessageInfoComponentFactory.kt
**/*.kt

📄 CodeRabbit inference engine (AGENTS.md)

**/*.kt: Use @OptIn annotations explicitly; avoid suppressions unless documented
Document public APIs with KDoc, including thread expectations and state notes

Files:

  • stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/adapter/internal/MessageListItemDiffCallback.kt
  • stream-chat-android-ui-guides/src/main/java/io/getstream/chat/android/guides/catalog/compose/customizingimageandvideoattachments/factory/MultimediaAttachmentFactory.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/MessageUtils.kt
  • stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/feature/channel/list/ChannelsActivity.kt
  • stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/chats/ChatsActivity.kt
  • stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/component/MessageInfoComponentFactory.kt
**/stream-chat-android-compose/**/*.kt

📄 CodeRabbit inference engine (AGENTS.md)

**/stream-chat-android-compose/**/*.kt: Compose components should follow noun-based naming (e.g., MessageList, ChannelListHeader)
Compose previews should use @StreamPreview helpers

Files:

  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/MessageUtils.kt
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
Repo: GetStream/stream-chat-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T15:00:07.506Z
Learning: Applies to **/stream-chat-android-compose/**/*.kt : Compose previews should use `StreamPreview` helpers
Learnt from: CR
Repo: GetStream/stream-chat-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T15:00:07.506Z
Learning: Applies to **/stream-chat-android-compose/**/*Test.kt : Add Paparazzi snapshots for Compose UI regressions and run `verifyPaparazziDebug`
📚 Learning: 2025-12-17T15:00:07.506Z
Learnt from: CR
Repo: GetStream/stream-chat-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T15:00:07.506Z
Learning: Applies to **/stream-chat-android-ui-components/**/*Test.kt : Record Shot baselines when behaviour changes in XML kit UI tests

Applied to files:

  • stream-chat-android-client/build.gradle.kts
  • stream-chat-android-compose/build.gradle.kts
  • stream-chat-android-ui-common/build.gradle.kts
  • stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/adapter/internal/MessageListItemDiffCallback.kt
  • stream-chat-android-ui-components/build.gradle.kts
  • stream-chat-android-ui-guides/src/main/java/io/getstream/chat/android/guides/catalog/compose/customizingimageandvideoattachments/factory/MultimediaAttachmentFactory.kt
  • stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/feature/channel/list/ChannelsActivity.kt
  • stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/chats/ChatsActivity.kt
  • stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/component/MessageInfoComponentFactory.kt
📚 Learning: 2025-12-17T15:00:07.506Z
Learnt from: CR
Repo: GetStream/stream-chat-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T15:00:07.506Z
Learning: Applies to **/stream-chat-android-compose/**/*Test.kt : Add Paparazzi snapshots for Compose UI regressions and run `verifyPaparazziDebug`

Applied to files:

  • stream-chat-android-compose/build.gradle.kts
  • stream-chat-android-ui-components/build.gradle.kts
  • stream-chat-android-ui-guides/src/main/java/io/getstream/chat/android/guides/catalog/compose/customizingimageandvideoattachments/factory/MultimediaAttachmentFactory.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/MessageUtils.kt
  • stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/chats/ChatsActivity.kt
  • stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/component/MessageInfoComponentFactory.kt
📚 Learning: 2025-12-17T15:00:07.506Z
Learnt from: CR
Repo: GetStream/stream-chat-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T15:00:07.506Z
Learning: Applies to **/stream-chat-android-compose/**/*.kt : Compose previews should use `StreamPreview` helpers

Applied to files:

  • stream-chat-android-compose/build.gradle.kts
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/MessageUtils.kt
  • stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/feature/channel/list/ChannelsActivity.kt
  • stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/chats/ChatsActivity.kt
  • stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/component/MessageInfoComponentFactory.kt
📚 Learning: 2025-12-17T15:00:07.506Z
Learnt from: CR
Repo: GetStream/stream-chat-android PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-17T15:00:07.506Z
Learning: Applies to **/stream-chat-android-compose/**/*.kt : Compose components should follow noun-based naming (e.g., `MessageList`, `ChannelListHeader`)

Applied to files:

  • stream-chat-android-compose/build.gradle.kts
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/MessageUtils.kt
  • stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/feature/channel/list/ChannelsActivity.kt
  • stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/chats/ChatsActivity.kt
  • stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/component/MessageInfoComponentFactory.kt
🔇 Additional comments (11)
stream-chat-android-ui-guides/src/main/java/io/getstream/chat/android/guides/catalog/compose/customizingimageandvideoattachments/factory/MultimediaAttachmentFactory.kt (2)

43-43: LGTM! Lint correctly caught invalid RGB color values.

The color component values have been corrected from 256 to 255, which is the maximum valid value for 8-bit RGB channels (valid range: 0-255).


58-58: LGTM! Consistent fix applied.

Same correction as Line 43, ensuring both play button overlays use valid color component values.

stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/MessageUtils.kt (1)

40-44: LGTM! Proper use of remember to cache the flow.

Wrapping the flow transformation in remember(cid, messageId) prevents unnecessary Flow recreation on every recomposition while maintaining correct invalidation when the keys change. This aligns with Compose best practices for collecting flows.

stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/component/MessageInfoComponentFactory.kt (1)

125-125: LGTM! Proper use of remember to optimize flow collection.

The remember(message) wrapper correctly prevents the flow from being recreated on every recomposition, caching it per message instance. This optimization aligns with Compose best practices and addresses the lint findings mentioned in the PR objectives.

stream-chat-android-client/build.gradle.kts (1)

43-46: LGTM - Lint configuration updated as intended.

The addition of the MissingTranslation suppression aligns with the PR objectives to address lint findings by selectively disabling checks.

stream-chat-android-compose/build.gradle.kts (1)

28-31: LGTM - Lint configuration added as intended.

The lint block correctly disables translation-related checks, aligning with the PR's goal to address lint findings.

stream-chat-android-ui-common/build.gradle.kts (1)

24-26: LGTM - Lint configuration added as intended.

The MissingTranslation suppression is correctly configured and aligns with the PR objectives.

stream-chat-android-ui-components/build.gradle.kts (1)

23-27: LGTM - Lint configuration with baseline properly configured.

The lint configuration correctly disables translation checks and references the baseline file at stream-chat-android-ui-components/lint-baseline.xml, which exists and aligns with the PR objectives of introducing a lint baseline for the XML components module. The Gradle DSL syntax and formatting are correct.

stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/chats/ChatsActivity.kt (1)

239-243: LGTM! Correct use of remember for flow caching.

The remember blocks properly cache the globalStateFlow reference and the derived flow transformations, preventing unnecessary recomputations on every recomposition. This follows Compose best practices for optimizing reactive flow collection.

stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/feature/channel/list/ChannelsActivity.kt (1)

223-225: LGTM! Proper extension of bottom navigation.

The new content branches for MENTIONS and THREADS correctly wire up the corresponding composables (MentionsContent and ThreadsContent) defined later in the file.

stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/adapter/internal/MessageListItemDiffCallback.kt (1)

19-19: LGTM: Necessary import for lint suppression.

The import is required for the @SuppressLint annotation used below.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants