Skip to content

Conversation

@VelikovPetar
Copy link
Contributor

@VelikovPetar VelikovPetar commented Dec 10, 2025

🎯 Goal

Part 1 of the ChannelLogic/JumpToMessage refactoring.
Sets the baseline on which the new and optimised channel logic will be built:

  1. Extracts a ChannelLogic interface with the common operations (to be implemented by the current and the new implementation)
  2. Refactors the current implementation to implement the ChannelLogic interface (now named ChannelLogicImpl)
  3. Extracts the event handling logic from ChannelLogic to a separate ChannelEventHandler class

Additionally, fixes message.updated/message.deleted showing the channel (this event doesn't update the channel visibility)

🛠 Implementation details

  1. Extract ChannelLogic and its implementation ChannelLogicImpl
  2. Extract ChannelEventHandler
  3. Bug-fixes (see previous section)

🎨 UI Changes

NA

🧪 Testing

Test various scenarios related to channel / message list: pagination, reactions, quoting, pinning, updating, threading - everything should work as expected.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed message.updated/message.deleted and notification events that could unhide channels; improved pagination and gap‑fill behavior.
  • Documentation

    • Deprecated an unused messages property and clarified related docs.
  • Improvements

    • Refined event handling, typing state flow, and channel visibility/update processing; minor API adjustments for visibility/typing behavior.
  • Tests

    • Expanded and updated tests covering channel events, threads, typing, visibility, polls, reminders, and read events.

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

@github-actions
Copy link
Contributor

github-actions bot commented Dec 10, 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 🟢

@VelikovPetar VelikovPetar marked this pull request as ready for review December 10, 2025 10:58
@VelikovPetar VelikovPetar requested a review from a team as a code owner December 10, 2025 10:58
@VelikovPetar VelikovPetar changed the title ChannelLogic refactor (part 1) ChannelLogic refactor (part 1) and event handling bug fixes Dec 10, 2025
@coderabbitai
Copy link

coderabbitai bot commented Dec 18, 2025

Walkthrough

Adds ChannelEventHandler and ChannelLogicImpl, converts ChannelLogic to an interface, changes stateLogic from function to property, renames toggleHidden→setHidden, simplifies typing handling, adds ThreadInfo test factories, updates many listener/logic tests, and appends a changelog entry.

Changes

Cohort / File(s) Summary
Changelog
CHANGELOG.md
Added entry under stream-chat-android-state noting fix for message.updated/message.deleted events unhiding channels.
Test factories & fixtures
stream-chat-android-client-test/src/main/java/.../Mother.kt, stream-chat-android-core/src/testFixtures/kotlin/.../Mother.kt
Added randomThreadInfo(...); extended randomNotificationMarkReadEvent(...) to accept thread: ThreadInfo?; added related imports.
Channel state small edits
stream-chat-android-client/src/main/java/.../ChannelState.kt
Marked oldMessages deprecated; fixed doc wording.
Event utils comment
stream-chat-android-state/src/main/java/.../ChatEventUtils.kt
Clarified that MarkAllReadEvent doesn’t provide lastReadMessageId (global event).
ChatClient loader renames
stream-chat-android-state/src/main/java/.../ChatClient.kt
Replaced internal loader calls to use loadBefore/loadAfter/loadAround named methods.
Listener updates (property access & API rename)
stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/listener/internal/...
*EditMessageListenerState.kt*, *HideChannelListenerState.kt*, *PushPreferencesListenerState.kt*, *SendGiphyListenerState.kt*, *QueryChannelListenerState.kt*
Replaced stateLogic() calls with stateLogic property; replaced toggleHidden(...)setHidden(...); simplified query result delegation in QueryChannelListenerState.
ChannelLogic split
stream-chat-android-state/src/main/java/.../ChannelLogic.kt, .../ChannelLogicImpl.kt
Converted ChannelLogic to an interface and added ChannelLogicImpl implementing watch/load/pagination/offline-online queries, gap-filling, and event delegation.
ChannelEventHandler added
stream-chat-android-state/src/main/java/.../ChannelEventHandler.kt
New internal ChannelEventHandler mapping many ChatEvent types to ChannelStateLogic/ChannelMutableState operations (upsert/update/delete messages, reactions, membership, visibility, reads, polls, reminders, typing, etc.).
ChannelStateLogic API adjustments
stream-chat-android-state/src/main/java/.../ChannelStateLogic.kt
Renamed toggleHiddensetHidden; adjusted typing-related calls and renamed a typo method udpateMessageCountupdateMessageCount.
ChannelMutableState changes
stream-chat-android-state/src/main/java/.../ChannelMutableState.kt
Removed multi-event typing storage; replaced updateTypingEvents(...) with updateTypingEvent(...); deprecated oldMessages; removed related cleanup.
Registry & state tweaks
stream-chat-android-state/src/main/java/.../LogicRegistry.kt, .../StateRegistry.kt
LogicRegistry now instantiates ChannelLogicImpl; switched to stateLogic property access; removed explicit constructor keyword in StateRegistry.
Tests: updated & added
stream-chat-android-state/src/test/java/...
*WhenHandleEvent.kt*, *DeleteMessageListenerStateTest.kt*, *EditMessageListenerStateTest.kt*, *HideChannelListenerStateTest.kt*, *PushPreferencesListenerStateTest.kt*, *ChannelLogicTest.kt*, *ChannelStateLogicTest.kt*, *ChannelEventHandlerTest.kt*
Updated mocks/stubs to use stateLogic property and setHidden(...); switched tests to construct ChannelLogicImpl; added comprehensive ChannelEventHandlerTest; adjusted typing and message upsert/update assertions.

Sequence Diagram(s)

%%{init: {"themeVariables": {"actorBorder":"#2b2b2b", "actorBkg":"#f6f8fa", "signal":"#8b5cf6"}}}%%
sequenceDiagram
    autonumber
    participant Caller as Caller
    participant Logic as ChannelLogicImpl
    participant Handler as ChannelEventHandler
    participant StateLogic as ChannelStateLogic
    participant Mutable as ChannelMutableState

    Caller->>Logic: handleEvent(event)
    Logic->>Handler: handle(event)
    alt new message / notification
        Handler->>StateLogic: upsertMessage(message)
        StateLogic->>Mutable: upsertMessage(message)
    else message update
        Handler->>StateLogic: updateMessage(message)
        StateLogic->>Mutable: updateMessage(message)
    else message delete
        Handler->>StateLogic: deleteMessage(message)
        StateLogic->>Mutable: deleteMessage(message)
    else visibility change
        Handler->>StateLogic: setHidden(flag)
        StateLogic->>Mutable: setHidden(flag)
    else typing / read / presence
        Handler->>StateLogic: updateTypingEvent / updateRead / updateDelivered(...)
        StateLogic->>Mutable: apply updates
    end
    Mutable-->>StateLogic: state changed
    StateLogic-->>Logic: acknowledge
    Logic-->>Caller: done
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • gpunto
  • aleksandar-apostolov

Poem

🐰 I hopped through code with nimble feet,
Swapped calls for properties, tidy and neat.
Events now march to a handler’s beat,
Threads and tests aligned — refinements complete.
Carrot-powered CI, ever so sweet. 🥕✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.11% 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 accurately and concisely summarizes the two main components of this pull request: a ChannelLogic refactoring (part 1) and event handling bug fixes, directly reflecting the changeset.
Description check ✅ Passed The PR description covers the goal, implementation details, testing guidance, and acknowledges UI changes are not applicable. However, it lacks details on changelog updates, test coverage, and contributor/reviewer checklist items required by the template.
✨ 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 feature/AND-887_channel_logic_refactoring

📜 Recent 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 e7a4a4c and a9d290e.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelStateLogic.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelStateLogicTest.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • CHANGELOG.md
🧰 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-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelStateLogicTest.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelStateLogic.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-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelStateLogicTest.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelStateLogic.kt
**/src/test/**/*.kt

📄 CodeRabbit inference engine (AGENTS.md)

**/src/test/**/*.kt: Use backtick test names (for example: fun message list filters muted channels()) for readability
Use deterministic tests with runTest + virtual time for concurrency-sensitive logic (uploads, sync, message state)
Keep helper extensions private/internal in test files

Files:

  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelStateLogicTest.kt
🧠 Learnings (1)
📚 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-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelStateLogicTest.kt
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: compare-sdk-sizes / Compare SDK sizes
  • GitHub Check: Debug build
  • GitHub Check: Unit tests
  • GitHub Check: API check
  • GitHub Check: Build / compose apks
🔇 Additional comments (4)
stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelStateLogic.kt (3)

294-300: LGTM! Typing state delegation simplified.

The change from updateTypingEvents to updateTypingEvent successfully delegates to the new single-argument API on ChannelMutableState, streamlining the typing state update flow.


571-577: LGTM! Improved naming clarity.

The rename from toggleHidden to setHidden better reflects the function's behavior—it sets the hidden state to a specific value rather than toggling it. The updated parameter documentation is also clearer.


953-955: LGTM! Typo corrected.

The method name is now correctly spelled as updateMessageCount.

stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelStateLogicTest.kt (1)

200-208: LGTM! Test correctly updated for new API.

The test expectation properly reflects the change from updateTypingEvents(any(), any()) to updateTypingEvent(any()), aligning with the simplified typing state API in the production code.


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: 4

🧹 Nitpick comments (4)
stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandler.kt (1)

75-86: Consider documenting the getCurrentUserId parameter.

The KDoc documents cid and stateLogic but omits getCurrentUserId. As per coding guidelines, public APIs should include complete KDoc.

🔎 Suggested documentation improvement:
 /**
  * Class responsible for updating the local database based on channel-related events.
  *
  * @param cid The channel identifier.
  * @param stateLogic The [ChannelStateLogic] instance used to update the channel state.
- * @param getCurrentUserId Returns the currently logged in user ID.
+ * @param getCurrentUserId A function that returns the currently logged in user ID, or null if not logged in.
  */
CHANGELOG.md (1)

37-39: Polish wording for clarity in the new fixed items

The current bullets are understandable but could read more clearly in English. Consider tightening them slightly:

Suggested text tweaks
- Fix `notification.thread_message_new` event upserting the message in the channel even when not sent to channel. [#6038](...)
+ Fix `notification.thread_message_new` event upserting the message in the channel even when it was not sent to the channel. [#6038](...)

- Fix `message.updated` and `message.deleted` events unhiding the channel. [#6038](...)
+ Fix `message.updated` and `message.deleted` events unintentionally unhiding the channel. [#6038](...)

- Fix inserting instead of updating a message breaking pagination. [#6038](...)
+ Fix inserting a message instead of updating it, which was breaking pagination. [#6038](...)
stream-chat-android-client-test/src/main/java/io/getstream/chat/android/client/test/Mother.kt (1)

296-296: Consider defaulting thread to null for backward compatibility.

The default thread = randomThreadInfo() means tests that don't explicitly set thread will now receive a random ThreadInfo. This could cause unexpected behavior in existing tests that assume a channel-level read event (no thread).

For example, in WhenHandleEvent.kt line 245, the test explicitly sets thread = null to work around this. Consider using thread: ThreadInfo? = null as the default to maintain backward compatibility.

🔎 Suggested change:
 public fun randomNotificationMarkReadEvent(
     createdAt: Date = Date(),
     user: User = randomUser(),
     cid: String = randomCID(),
     channelType: String = randomString(),
     channelId: String = randomString(),
     totalUnreadCount: Int = randomInt(),
     unreadChannels: Int = randomInt(),
     lastReadMessageId: String? = randomString(),
     threadId: String? = randomString(),
-    thread: ThreadInfo? = randomThreadInfo(),
+    thread: ThreadInfo? = null,
     unreadThreads: Int? = randomInt(),
     unreadThreadMessages: Int? = randomInt(),
 ): NotificationMarkReadEvent = NotificationMarkReadEvent(
stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelLogicImpl.kt (1)

226-268: Consider adding error handling for gap-filling coroutine.

The coroutineScope.launch block lacks error handling. If the recursive fillTheGap call or runChannelQueryOnline fails, the error is silently swallowed. Consider adding a CoroutineExceptionHandler or logging failures.

🔎 Suggested improvement:
     private fun fillTheGap(
         messageLimit: Int,
         loadedMessages: List<Message>,
         requestedMessages: List<Message>,
     ) {
         if (loadedMessages.isEmpty() || requestedMessages.isEmpty() || messageLimit <= 0) return
         coroutineScope.launch {
+            try {
                 val loadedMessageIds = loadedMessages
                     .filter { it.getCreatedAtOrNull() != null }
                     .sortedBy { it.getCreatedAtOrDefault(NEVER) }
                     .map { it.id }
                 // ... rest of the logic
-                }?.onSuccess { fillTheGap(messageLimit, loadedMessages, it.messages) }
+                }?.onSuccess { fillTheGap(messageLimit, loadedMessages, it.messages) }
+            } catch (e: Exception) {
+                logger.e(e) { "[fillTheGap] Failed to fill message gap" }
+            }
         }
     }
📜 Review details

Configuration used: defaults

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 59ae0b6 and ffbcb2d.

📒 Files selected for processing (26)
  • CHANGELOG.md (1 hunks)
  • stream-chat-android-client-test/src/main/java/io/getstream/chat/android/client/test/Mother.kt (4 hunks)
  • stream-chat-android-client/src/main/java/io/getstream/chat/android/client/channel/state/ChannelState.kt (2 hunks)
  • stream-chat-android-core/src/testFixtures/kotlin/io/getstream/chat/android/Mother.kt (2 hunks)
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/event/handler/internal/utils/ChatEventUtils.kt (1 hunks)
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/extensions/ChatClient.kt (4 hunks)
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/listener/internal/EditMessageListenerState.kt (2 hunks)
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/listener/internal/HideChannelListenerState.kt (3 hunks)
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/listener/internal/PushPreferencesListenerState.kt (1 hunks)
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/listener/internal/QueryChannelListenerState.kt (1 hunks)
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/listener/internal/SendGiphyListenerState.kt (1 hunks)
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandler.kt (1 hunks)
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelLogic.kt (2 hunks)
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelLogicImpl.kt (1 hunks)
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelStateLogic.kt (3 hunks)
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/internal/LogicRegistry.kt (4 hunks)
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/state/StateRegistry.kt (1 hunks)
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/state/channel/internal/ChannelMutableState.kt (3 hunks)
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/channel/controller/WhenHandleEvent.kt (8 hunks)
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/listener/internal/DeleteMessageListenerStateTest.kt (1 hunks)
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/listener/internal/EditMessageListenerStateTest.kt (5 hunks)
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/listener/internal/HideChannelListenerStateTest.kt (4 hunks)
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/listener/internal/PushPreferencesListenerStateTest.kt (1 hunks)
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandlerTest.kt (1 hunks)
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelLogicTest.kt (1 hunks)
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelStateLogicTest.kt (1 hunks)
🧰 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-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelStateLogicTest.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/listener/internal/EditMessageListenerState.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/channel/controller/WhenHandleEvent.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/listener/internal/QueryChannelListenerState.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/listener/internal/EditMessageListenerStateTest.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/internal/LogicRegistry.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/listener/internal/PushPreferencesListenerState.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/listener/internal/HideChannelListenerState.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/extensions/ChatClient.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelStateLogic.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/listener/internal/PushPreferencesListenerStateTest.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandler.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/event/handler/internal/utils/ChatEventUtils.kt
  • stream-chat-android-core/src/testFixtures/kotlin/io/getstream/chat/android/Mother.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelLogicTest.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelLogicImpl.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/state/StateRegistry.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/listener/internal/DeleteMessageListenerStateTest.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelLogic.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandlerTest.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/listener/internal/SendGiphyListenerState.kt
  • stream-chat-android-client-test/src/main/java/io/getstream/chat/android/client/test/Mother.kt
  • stream-chat-android-client/src/main/java/io/getstream/chat/android/client/channel/state/ChannelState.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/listener/internal/HideChannelListenerStateTest.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/state/channel/internal/ChannelMutableState.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-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelStateLogicTest.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/listener/internal/EditMessageListenerState.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/channel/controller/WhenHandleEvent.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/listener/internal/QueryChannelListenerState.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/listener/internal/EditMessageListenerStateTest.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/internal/LogicRegistry.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/listener/internal/PushPreferencesListenerState.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/listener/internal/HideChannelListenerState.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/extensions/ChatClient.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelStateLogic.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/listener/internal/PushPreferencesListenerStateTest.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandler.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/event/handler/internal/utils/ChatEventUtils.kt
  • stream-chat-android-core/src/testFixtures/kotlin/io/getstream/chat/android/Mother.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelLogicTest.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelLogicImpl.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/state/StateRegistry.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/listener/internal/DeleteMessageListenerStateTest.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelLogic.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandlerTest.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/listener/internal/SendGiphyListenerState.kt
  • stream-chat-android-client-test/src/main/java/io/getstream/chat/android/client/test/Mother.kt
  • stream-chat-android-client/src/main/java/io/getstream/chat/android/client/channel/state/ChannelState.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/listener/internal/HideChannelListenerStateTest.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/state/channel/internal/ChannelMutableState.kt
**/src/test/**/*.kt

📄 CodeRabbit inference engine (AGENTS.md)

**/src/test/**/*.kt: Use backtick test names (for example: fun message list filters muted channels()) for readability
Use deterministic tests with runTest + virtual time for concurrency-sensitive logic (uploads, sync, message state)
Keep helper extensions private/internal in test files

Files:

  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelStateLogicTest.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/channel/controller/WhenHandleEvent.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/listener/internal/EditMessageListenerStateTest.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/listener/internal/PushPreferencesListenerStateTest.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelLogicTest.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/listener/internal/DeleteMessageListenerStateTest.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandlerTest.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/listener/internal/HideChannelListenerStateTest.kt
🧠 Learnings (4)
📚 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-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelStateLogicTest.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/channel/controller/WhenHandleEvent.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/listener/internal/EditMessageListenerStateTest.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/extensions/ChatClient.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/listener/internal/PushPreferencesListenerStateTest.kt
  • stream-chat-android-core/src/testFixtures/kotlin/io/getstream/chat/android/Mother.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelLogicTest.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/listener/internal/DeleteMessageListenerStateTest.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandlerTest.kt
  • stream-chat-android-client-test/src/main/java/io/getstream/chat/android/client/test/Mother.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/listener/internal/HideChannelListenerStateTest.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 **/src/test/**/*.kt : Use deterministic tests with `runTest` + virtual time for concurrency-sensitive logic (uploads, sync, message state)

Applied to files:

  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelStateLogicTest.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/channel/controller/WhenHandleEvent.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/listener/internal/EditMessageListenerStateTest.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelLogicTest.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandlerTest.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-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelStateLogicTest.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/listener/internal/EditMessageListenerStateTest.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/listener/internal/PushPreferencesListenerStateTest.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandlerTest.kt
  • stream-chat-android-client-test/src/main/java/io/getstream/chat/android/client/test/Mother.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 **/src/test/**/*.kt : Use backtick test names (for example: `fun `message list filters muted channels`()`) for readability

Applied to files:

  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/channel/controller/WhenHandleEvent.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/listener/internal/EditMessageListenerStateTest.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandlerTest.kt
🧬 Code graph analysis (4)
stream-chat-android-state/src/test/java/io/getstream/chat/android/state/channel/controller/WhenHandleEvent.kt (1)
stream-chat-android-client-test/src/main/java/io/getstream/chat/android/client/test/Mother.kt (1)
  • randomNotificationMarkReadEvent (286-314)
stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelLogicImpl.kt (1)
stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelLogic.kt (2)
  • handleEvent (171-171)
  • updateDataForChannel (150-157)
stream-chat-android-client-test/src/main/java/io/getstream/chat/android/client/test/Mother.kt (1)
stream-chat-android-core/src/testFixtures/kotlin/io/getstream/chat/android/Mother.kt (1)
  • randomThreadInfo (1008-1036)
stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/state/channel/internal/ChannelMutableState.kt (3)
stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelLogic.kt (1)
  • upsertMessage (131-131)
stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelStateLogic.kt (1)
  • upsertMessage (334-341)
stream-chat-android-client/src/main/java/io/getstream/chat/android/client/channel/ChannelMessagesUpdateLogic.kt (2)
  • upsertMessage (23-40)
  • upsertMessage (26-26)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: API check
  • GitHub Check: compare-sdk-sizes / Compare SDK sizes
  • GitHub Check: Unit tests
  • GitHub Check: Debug build
  • GitHub Check: Build / compose apks
🔇 Additional comments (45)
stream-chat-android-state/src/main/java/io/getstream/chat/android/state/extensions/ChatClient.kt (4)

337-337: LGTM! Clean refactoring to new loader method.

The change from the old loader to loadBefore(messageId = null, limit = messageLimit) is correct. Passing null as the messageId appropriately indicates loading from the oldest available messages.


358-358: LGTM! Correct delegation to new loader method.

The change to loadAfter(messageId = baseMessageId, limit = messageLimit) correctly delegates to the new loader implementation, maintaining the same behavior.


383-383: LGTM! Appropriate refactoring to new loader.

The change to loadAround(messageId) correctly aligns with the new loader naming convention while preserving the function's behavior.


477-477: LGTM! Internal refactoring to new loader.

The change from calling loadMessagesAroundId to calling loadAround(messageId) directly is a clean refactoring that maintains the same behavior while eliminating an unnecessary indirection.

stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/listener/internal/QueryChannelListenerState.kt (1)

79-79: Centralized error handling is properly implemented.

The delegation to logic.channel(...).onQueryChannelResult(request, result) successfully centralizes result handling. The implementation in ChannelLogicImpl handles both Result.Success and Result.Failure cases, delegating to channelStateLogic.propagateChannelQuery() and channelStateLogic.propagateQueryError() respectively, maintaining full behavioral consistency with the previous explicit implementation.

stream-chat-android-state/src/main/java/io/getstream/chat/android/state/event/handler/internal/utils/ChatEventUtils.kt (1)

72-79: LGTM!

The added comment clarifies why lastReadMessageId is explicitly set to null for MarkAllReadEvent, which is helpful for maintainability.

stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/listener/internal/EditMessageListenerState.kt (2)

65-67: LGTM!

The property access pattern change is consistent with the refactored ChannelLogic interface.


48-50: The stateLogic access patterns are correctly implemented. ChannelLogic exposes stateLogic as a property (accessed without parentheses), while ThreadLogic exposes stateLogic as a function (called with parentheses). The code correctly reflects these different interface definitions.

stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/state/channel/internal/ChannelMutableState.kt (3)

213-214: LGTM!

The deprecation annotation clearly communicates that oldMessages will be removed in future versions, giving consumers time to migrate.


428-430: LGTM!

The simplified updateTypingEvent method with a single parameter is cleaner than the previous multi-argument version.


540-552: Key fix for pagination integrity.

This guarded update correctly distinguishes between upsert (insert or update) and update-only operations. By skipping updates for non-existent messages, this prevents message.updated and message.deleted events from inadvertently inserting out-of-order messages that would break pagination.

stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandler.kt (4)

149-172: Core bug fix: Using updateMessage instead of upsertMessage for update/delete events.

This correctly distinguishes between events that should insert messages (message.new, notification.message_new, notification.thread_message_new) and those that should only update existing messages (message.updated, message.deleted). This prevents pagination breakage when updates introduce out-of-order messages.


174-183: Fix for notification.thread_message_new handling.

Correctly guards upsert with showInChannel check, ensuring thread-only replies don't appear in the main channel message list. The hidden state update is also correctly guarded by !shadowed.


300-321: Well-designed message update helpers.

The upsertMessage helper preserves createdLocallyAt for proper message sorting and retains ownReactions. The updateMessage helper correctly returns early if the message doesn't exist, preventing phantom message insertions.


243-253: Thread-scoped read events correctly filtered.

Read events are properly filtered to exclude thread-scoped reads (event.thread == null), ensuring channel-level read state isn't affected by thread reads.

stream-chat-android-client/src/main/java/io/getstream/chat/android/client/channel/state/ChannelState.kt (2)

63-64: LGTM!

The deprecation annotation on the public interface aligns with the implementation in ChannelMutableState.kt.


135-136: LGTM!

Good catch fixing the documentation typo from "performatic" to "performant".

stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/state/StateRegistry.kt (1)

56-56: LGTM: Idiomatic constructor declaration.

Removing the explicit constructor keyword is standard Kotlin style when there are no annotations or visibility modifiers on the primary constructor itself.

stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/listener/internal/PushPreferencesListenerStateTest.kt (1)

42-42: LGTM: Mock setup correctly reflects the API change.

The mock configuration properly reflects the change from stateLogic() function to stateLogic property, consistent with the broader ChannelLogic refactor.

stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/listener/internal/PushPreferencesListenerState.kt (1)

53-53: LGTM: Property access aligns with the refactored API.

The change from function call to property access is correct and consistent with the ChannelLogic interface evolution throughout this PR.

stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/internal/LogicRegistry.kt (1)

32-32: LGTM! Clean refactoring to interface-based design.

The changes correctly implement the ChannelLogic interface pattern:

  • Import of ChannelLogicImpl (Line 32)
  • Instantiation using ChannelLogicImpl while returning ChannelLogic interface (Lines 119-127)
  • Property-based access to stateLogic instead of function calls (Lines 135, 207)

This aligns with the PR's goal of extracting ChannelLogic as an interface with ChannelLogicImpl as the concrete implementation.

Also applies to: 119-127, 135-136, 207-208

stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/listener/internal/HideChannelListenerState.kt (1)

57-57: LGTM! API improvements applied correctly.

The changes correctly apply the refactored API:

  • Property access for stateLogic (Lines 57, 74)
  • Renamed method from toggleHidden() to setHidden() (Lines 57, 85) - the new name is more explicit about the operation

Also applies to: 74-74, 85-85

stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/listener/internal/EditMessageListenerStateTest.kt (1)

50-50: LGTM! Test mocks updated for property-based access.

All mock setups correctly changed from on(it.stateLogic()) to on(it.stateLogic) to match the new property-based API.

Also applies to: 89-89, 129-129, 169-169, 209-209

stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelStateLogicTest.kt (1)

210-210: LGTM! Test assertion updated for simplified typing API.

The verification correctly updated from updateTypingEvents(...) (plural, two arguments) to updateTypingEvent(...) (singular, one argument), reflecting the API simplification in ChannelMutableState.

stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/listener/internal/DeleteMessageListenerStateTest.kt (1)

45-45: LGTM! Mock setup updated for property-based access.

The mock correctly changed from on(it.stateLogic()) to on(it.stateLogic) to align with the new property-based API.

stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/listener/internal/SendGiphyListenerState.kt (1)

41-41: LGTM! Null-safe property access applied correctly.

The null-safe chain correctly updated from ?.stateLogic()? to ?.stateLogic? while maintaining proper null handling.

stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/listener/internal/HideChannelListenerStateTest.kt (1)

40-40: LGTM! Test updated for both API changes.

The test correctly reflects both refactoring changes:

  • Mock setup uses property access: on(it.stateLogic) (Line 40)
  • Verifications use renamed method: setHidden(true/false) (Lines 51, 63, 75)

Also applies to: 51-51, 63-63, 75-75

stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelLogicTest.kt (1)

54-60: LGTM! Test correctly uses concrete implementation.

The test properly instantiates ChannelLogicImpl with all required dependencies while keeping the sut variable typed as ChannelLogic interface. This is good practice for testing the implementation while maintaining interface-based design.

stream-chat-android-state/src/test/java/io/getstream/chat/android/state/channel/controller/WhenHandleEvent.kt (6)

91-94: LGTM: Mock delegation for upsertMessage is correctly configured.

The stub bridges channelStateLogic.upsertMessage() to channelMutableState.upsertMessage(), ensuring the mock propagates messages to state for tests that verify message presence.


105-110: LGTM: Updated to use ChannelLogicImpl.

The test correctly instantiates the new ChannelLogicImpl concrete implementation, aligning with the refactor that extracts ChannelLogic as an interface.


134-134: LGTM: Reflects API rename from toggleHidden to setHidden.

The verification correctly uses the renamed method setHidden(false).


153-154: Correct distinction between upsert and update operations.

The test now verifies that:

  1. Initial message insertion calls upsertMessage (times(1))
  2. Subsequent update event calls updateMessage (not upsert)

This aligns with the PR's bug fix to prevent pagination breakage when updates introduced out-of-order messages.


177-181: LGTM: Poll preservation tests updated to verify updateMessage.

Both poll preservation tests correctly verify that message updates (with or without polls) use updateMessage rather than upsertMessage, consistent with the event handling refinement.

Also applies to: 205-209


245-245: LGTM: Explicit thread = null for channel-level read events.

Setting thread = null explicitly clarifies this is a channel-level (not thread-level) read event, making the test intent clearer.

stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelStateLogic.kt (3)

191-198: LGTM: New updateMessage method for in-place message updates.

This method correctly delegates to mutableState.updateMessage(), providing a distinct path from upsertMessage(). This supports the PR's fix to prevent pagination breakage by not inserting out-of-order messages during updates.


582-586: LGTM: Renamed toggleHidden to setHidden with improved documentation.

The method name setHidden is more accurate since it sets a specific value rather than toggling. The parameter documentation is also improved from "Boolean." to "Whether the channel is hidden."


303-308: Simplification of typing state update is correct.

The updateTypingEvent method in ChannelMutableState properly handles internal state management by updating the _typing mutable state flow. The parameter rawTypingEvents is not needed since the typingEvent object contains all required information for state updates. Both channel-local and global typing state are correctly updated via mutableState.updateTypingEvent() and globalMutableState.tryEmitTypingEvent().

stream-chat-android-client-test/src/main/java/io/getstream/chat/android/client/test/Mother.kt (1)

71-71: LGTM: Imports added for ThreadInfo support.

The imports correctly bring in ThreadInfo and randomThreadInfo to support the new parameter in randomNotificationMarkReadEvent.

Also applies to: 90-90

stream-chat-android-core/src/testFixtures/kotlin/io/getstream/chat/android/Mother.kt (2)

1008-1036: LGTM: Well-structured randomThreadInfo factory function.

The factory follows the established pattern of other random* functions in this file:

  • All parameters have sensible defaults using existing random helpers
  • Parameters correctly map to ThreadInfo constructor
  • Public visibility is appropriate for test fixtures

As per coding guidelines for test files, this helper correctly uses public visibility for shared test utilities.


62-62: LGTM: Import added for ThreadInfo model.

stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelLogicImpl.kt (5)

43-58: LGTM: Well-documented class with clear responsibilities.

The KDoc clearly explains the purpose and dependencies. The class properly implements ChannelLogic interface and encapsulates channel state management.


60-62: Initialization order is correct but subtle.

The initialization relies on mutableState being initialized first (line 60), then eventHandler uses cid (line 61) which delegates to mutableState.cid. This works because Kotlin initializes properties in declaration order.


172-183: LGTM: Event handling delegated to ChannelEventHandler.

The refactor correctly delegates event handling to the new ChannelEventHandler, keeping ChannelLogicImpl focused on channel operations while centralizing event-to-state mapping.


100-118: LGTM: Watch method with proper loading guard.

The loading guard at line 103 prevents duplicate watch requests, which protects against UI bugs that could DDOS the API. Good defensive programming.


213-216: Singleton architecture is intentional per Stream Chat design; testing requires mocking tools like MockK.

ChatClient.instance() is the documented, intended pattern for this SDK—ChatClient is a singleton: you'll create it once and re-use it across your application. The coupling is architecturally justified for state consistency, avoiding duplicate connections, and simplifying resource management. Testing this requires mock tools (MockK, Mockito with extensions for Kotlin finals), which is standard practice in this codebase. The code meets all Kotlin style guidelines: proper indentation, no wildcard imports, license header present, and KDoc documentation included.

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

🧹 Nitpick comments (1)
stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandler.kt (1)

318-327: Potential inconsistency between getMessage and mutableState.getMessageById.

The getMessage helper on line 326 only looks up messages from visibleMessages, while other code paths (lines 150, 155, 331, 336) use mutableState.getMessageById(). This inconsistency could cause updateMessage to skip updates for messages that exist in the full message state but are not currently visible (e.g., during search or pagination).

Consider using mutableState.getMessageById() for consistency:

🔎 Proposed fix
     private fun getMessage(id: String): Message? {
-        return mutableState.visibleMessages.value[id]?.copy()
+        return mutableState.getMessageById(id)?.copy()
     }
📜 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 de40e26 and 72f61d9.

📒 Files selected for processing (2)
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandler.kt (1 hunks)
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandlerTest.kt (1 hunks)
🧰 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-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandlerTest.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandler.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-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandlerTest.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandler.kt
**/src/test/**/*.kt

📄 CodeRabbit inference engine (AGENTS.md)

**/src/test/**/*.kt: Use backtick test names (for example: fun message list filters muted channels()) for readability
Use deterministic tests with runTest + virtual time for concurrency-sensitive logic (uploads, sync, message state)
Keep helper extensions private/internal in test files

Files:

  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandlerTest.kt
🧠 Learnings (5)
📚 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-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandlerTest.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 **/src/test/**/*.kt : Use deterministic tests with `runTest` + virtual time for concurrency-sensitive logic (uploads, sync, message state)

Applied to files:

  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandlerTest.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-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandlerTest.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 **/src/test/**/*.kt : Use backtick test names (for example: `fun `message list filters muted channels`()`) for readability

Applied to files:

  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandlerTest.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-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandler.kt
🧬 Code graph analysis (2)
stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandlerTest.kt (2)
stream-chat-android-core/src/testFixtures/kotlin/io/getstream/chat/android/Mother.kt (9)
  • randomCID (102-102)
  • randomString (95-99)
  • randomMessage (303-400)
  • randomUser (183-229)
  • randomPoll (866-908)
  • randomDate (708-708)
  • randomChannel (424-484)
  • randomReaction (241-267)
  • randomMember (652-678)
stream-chat-android-client-test/src/main/java/io/getstream/chat/android/client/test/Mother.kt (6)
  • randomNewMessageEvent (516-542)
  • randomNotificationMessageNewEvent (434-454)
  • randomMemberAddedEvent (390-408)
  • randomNotificationRemovedFromChannelEvent (664-682)
  • randomMessageReadEvent (246-264)
  • randomChannelUserBannedEvent (702-721)
stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandler.kt (3)
stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelLogic.kt (2)
  • upsertMessage (115-115)
  • getMessage (108-108)
stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelStateLogic.kt (2)
  • upsertMessage (334-341)
  • updateMessage (196-198)
stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/state/channel/internal/ChannelMutableState.kt (2)
  • upsertMessage (536-538)
  • updateMessage (545-552)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Build / compose apks
  • GitHub Check: compare-sdk-sizes / Compare SDK sizes
  • GitHub Check: API check
  • GitHub Check: Debug build
  • GitHub Check: Unit tests
  • GitHub Check: ktlint
🔇 Additional comments (5)
stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandlerTest.kt (5)

99-120: LGTM!

Test class setup is well-structured with proper mocking. The use of backtick test names follows coding guidelines, and the @Suppress("LargeClass") annotation is appropriate given the comprehensive coverage needed.


122-228: LGTM!

Comprehensive test coverage for NewMessageEvent and MessageUpdatedEvent scenarios, including edge cases like preserving createdLocallyAt for current user messages, poll enrichment, and handling non-existing messages.


229-324: LGTM!

Good coverage of MessageDeletedEvent (hard vs soft delete) and NotificationMessageNewEvent (inside search vs not) scenarios. The tests correctly verify the distinction between upsertMessage and updateMessage calls per PR objectives.


326-938: LGTM!

Excellent comprehensive coverage across all event types: thread messages, reactions, members, watchers, channel updates, typing, read/delivery receipts, invitations, bans, and polls. The tests correctly verify edge cases like thread-specific reads not updating channel read state (lines 704-720).


940-1096: LGTM!

Complete test coverage for reminder events, user presence, mute updates, and message deletion scenarios. The tests properly verify edge cases like operations on non-existing messages.

@VelikovPetar VelikovPetar marked this pull request as draft December 22, 2025 10:37
# Conflicts:
#	stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelLogic.kt
@VelikovPetar VelikovPetar marked this pull request as ready for review December 23, 2025 10:31
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: 1

🧹 Nitpick comments (1)
stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/state/channel/internal/ChannelMutableState.kt (1)

213-214: Consider enhancing the deprecation annotation with a level parameter.

The deprecation message is clear, but Kotlin's @Deprecated annotation supports a level parameter (DeprecationLevel.WARNING, ERROR, or HIDDEN) to indicate the deprecation severity. For a newly deprecated API, WARNING (the default) is appropriate, but explicitly specifying it improves clarity and makes future escalation easier to track.

🔎 Suggested enhancement
-    @Deprecated("This property is not used anymore and will be removed in future versions.")
+    @Deprecated(
+        message = "This property is not used anymore and will be removed in future versions.",
+        level = DeprecationLevel.WARNING,
+    )
     override val oldMessages: StateFlow<List<Message>> = messagesTransformation(_oldMessages!!.mapState { it.values })
📜 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 72f61d9 and e7a4a4c.

📒 Files selected for processing (7)
  • CHANGELOG.md
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandler.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelLogicImpl.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelStateLogic.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/state/channel/internal/ChannelMutableState.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/channel/controller/WhenHandleEvent.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandlerTest.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • CHANGELOG.md
🧰 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-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandlerTest.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandler.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelStateLogic.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelLogicImpl.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/channel/controller/WhenHandleEvent.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/state/channel/internal/ChannelMutableState.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-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandlerTest.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandler.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelStateLogic.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelLogicImpl.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/channel/controller/WhenHandleEvent.kt
  • stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/state/channel/internal/ChannelMutableState.kt
**/src/test/**/*.kt

📄 CodeRabbit inference engine (AGENTS.md)

**/src/test/**/*.kt: Use backtick test names (for example: fun message list filters muted channels()) for readability
Use deterministic tests with runTest + virtual time for concurrency-sensitive logic (uploads, sync, message state)
Keep helper extensions private/internal in test files

Files:

  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandlerTest.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/channel/controller/WhenHandleEvent.kt
🧠 Learnings (5)
📚 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-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandlerTest.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 **/src/test/**/*.kt : Use deterministic tests with `runTest` + virtual time for concurrency-sensitive logic (uploads, sync, message state)

Applied to files:

  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandlerTest.kt
  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/channel/controller/WhenHandleEvent.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-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandlerTest.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 **/src/test/**/*.kt : Use backtick test names (for example: `fun `message list filters muted channels`()`) for readability

Applied to files:

  • stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandlerTest.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-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandler.kt
🧬 Code graph analysis (3)
stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandler.kt (3)
stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/state/channel/internal/ChannelMutableState.kt (1)
  • upsertMessage (536-538)
stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelStateLogic.kt (1)
  • upsertMessage (325-332)
stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelLogic.kt (1)
  • upsertMessage (115-115)
stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelLogicImpl.kt (1)
stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelLogic.kt (2)
  • handleEvent (155-155)
  • updateDataForChannel (134-141)
stream-chat-android-state/src/test/java/io/getstream/chat/android/state/channel/controller/WhenHandleEvent.kt (1)
stream-chat-android-client-test/src/main/java/io/getstream/chat/android/client/test/Mother.kt (2)
  • randomMessageUpdateEvent (456-472)
  • randomNotificationMarkReadEvent (286-314)
🔇 Additional comments (28)
stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/state/channel/internal/ChannelMutableState.kt (1)

428-430: LGTM!

The simplified updateTypingEvent function is a clean improvement. The rename from plural to singular correctly reflects the single-parameter signature, and the null-safe access to _typing?.value properly handles the post-destroy() case.

stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelStateLogic.kt (3)

294-300: LGTM! Simplified typing state update.

The change from updateTypingEvents(...) to updateTypingEvent(typingEvent) simplifies the API by delegating the raw typing events handling to the internal implementation. This aligns with the broader refactoring to consolidate event handling.


573-577: Good API rename from toggleHidden to setHidden.

The rename is more semantically accurate since this method sets the hidden state to a specific value rather than toggling it. The updated parameter documentation clearly describes the purpose.


928-930: Typo fix looks good.

The method name correction from udpateMessageCount to updateMessageCount fixes the typo.

stream-chat-android-state/src/test/java/io/getstream/chat/android/state/channel/controller/WhenHandleEvent.kt (5)

89-95: Mock delegation setup is correct.

The upsertMessage mock delegation ensures that messages are properly propagated to the channelMutableState during tests, which is necessary for the test assertions to work correctly with the new ChannelLogicImpl structure.


105-111: Test updated to use ChannelLogicImpl.

The test correctly instantiates ChannelLogicImpl instead of the now-interface ChannelLogic, aligning with the refactoring that extracts ChannelLogic as an interface.


134-135: Verify call updated for renamed API.

The change from toggleHidden(false) to setHidden(false) correctly reflects the API rename in ChannelStateLogic.


149-155: Message update test expectations refined.

The test now properly verifies that both the original message and the updated message from the event are upserted separately, reflecting the correct event handling flow.


244-250: Read event test correctly sets thread = null.

This ensures the test covers channel-level read events (not thread reads), which should update the channel read state. This aligns with the event handling logic that only updates channel reads when thread == null.

stream-chat-android-state/src/test/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandlerTest.kt (7)

99-120: Well-structured test setup.

The test class properly sets up mocks for ChannelStateLogic and ChannelMutableState, and initializes the ChannelEventHandler with the required dependencies. The @Suppress("LargeClass") annotation is appropriate given the comprehensive event coverage.


122-138: Good test for createdLocallyAt preservation logic.

This test verifies that for messages from the current user, the createdLocallyAt timestamp is preserved from the existing message to ensure proper sorting. This is an important edge case for message synchronization.


181-214: Comprehensive poll enrichment tests.

These tests verify the important behavior that when a MessageUpdatedEvent arrives without a poll, the original poll is preserved from the existing message. This prevents data loss during message updates.


229-275: Message deletion tests cover both hard and soft delete scenarios.

Good coverage of both hardDelete = true (message removed from state) and hardDelete = false (message upserted with deleted state) behaviors.


637-664: Thread-aware read event handling is correctly tested.

These tests verify that MessageReadEvent only updates the channel read state when thread == null, preventing thread reads from affecting channel-level read state.


884-944: Reminder event tests verify proper message update flow.

The tests correctly verify that reminder events update the message's reminder field, and handle the edge case where the message doesn't exist in state.


326-346: Test correctly verifies the handler's actual behavior.

The test appropriately validates that NotificationThreadMessageNewEvent triggers a message upsert. The handler implementation contains only this single operation (line 174: is NotificationThreadMessageNewEvent -> upsertMessage(event.message)), and the test reflects that. No additional visibility or channel logic exists to verify.

Likely an incorrect or invalid review comment.

stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelEventHandler.kt (7)

75-86: Well-documented internal class.

The KDoc properly documents the class purpose and constructor parameters, including the callback for obtaining the current user ID.


96-116: Clean event routing structure.

The handle method properly routes events to either handleCidEvent for channel-specific events or handles global events (user presence, read all, mutes, message deletion) directly. Unhandled events are safely ignored.


149-162: MessageUpdatedEvent handling preserves poll and enriches reply.

The logic correctly:

  1. Retrieves the original message to preserve the poll if missing from the event
  2. Enriches the replyTo field from state if available
  3. This addresses the PR objective of fixing message update events

However, note that ownReactions is preserved in the upsertMessage helper but not explicitly here.


164-172: MessageDeletedEvent correctly differentiates hard vs soft delete.

Hard deletes remove the message from state, while soft deletes upsert the message with deletion metadata. Message count is updated when provided.


174-178: Reaction events upsert messages without visibility changes.

This aligns with the PR objective that reaction events should not change channel visibility. The events simply update the message with the new reaction state.


291-305: upsertMessage helper preserves local state appropriately.

The helper:

  1. Preserves createdLocallyAt for current user messages when requested
  2. Preserves ownReactions from the existing message to prevent reaction state loss
  3. Calls both upsertMessage and delsertPinnedMessage for proper pinned message handling

307-309: Defensive copy in getMessage.

The copy() call ensures the returned message is a defensive copy, preventing external modifications from affecting the internal state.

stream-chat-android-state/src/main/java/io/getstream/chat/android/state/plugin/logic/channel/internal/ChannelLogicImpl.kt (5)

43-72: Well-structured implementation class.

The class properly implements the ChannelLogic interface with:

  • Clear constructor parameters with KDoc
  • Proper initialization of ChannelEventHandler with dependencies
  • Read-only accessors for cid, state, and stateLogic

The TooManyFunctions and LargeClass suppressions are acceptable given the comprehensive functionality.


100-118: Watch method prevents duplicate requests.

The check for mutableState.loading.value prevents concurrent watch requests, which protects against UI bugs that could DDOS the API. The error message is clear and informative.


172-183: Event handling delegates to ChannelEventHandler.

The handleEvent method logs the event and delegates to the extracted ChannelEventHandler. This is a clean separation of concerns from the refactoring.


185-201: Query coordination between offline and online sources.

The runChannelQuery method:

  1. Captures loaded messages before the query
  2. Attempts offline query first
  3. Runs online query and triggers gap-filling on success
  4. Falls back to offline result if online fails

This provides good offline-first behavior with eventual consistency.


265-288: Offline query correctly skips newer/around pagination.

The method returns null for newer or around-id queries since local data may be stale. This ensures fresh data from the backend for these scenarios.

@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

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

See analysis details on SonarQube Cloud

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