Channel.lastMessageAt as a computed property derived from the currently loaded messages#6118
Conversation
Co-Authored-By: Claude <[email protected]>
SDK Size Comparison 📏
|
Co-Authored-By: Claude <[email protected]>
WalkthroughThe PR converts Changes
Sequence DiagramsequenceDiagram
actor EventSource as Message Event<br/>(NewMessageEvent)
participant EH as ChannelEventHandler
participant CSL as ChannelStateLogic
participant Utils as ChannelUtils
participant CM as ChannelData
EventSource->>EH: Handle new message event
EH->>CSL: updateLastMessageAt(message)
CSL->>Utils: calculateNewLastMessageAt(message, currentLastMessageAt, skipFlag)
Utils-->>CSL: Date? (computed timestamp)
CSL->>CM: Update channelData.lastMessageAt
CM-->>CSL: ChannelData updated
CSL-->>EH: Complete
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
stream-chat-android-core/src/main/java/io/getstream/chat/android/models/ChannelData.kt (1)
216-233:⚠️ Potential issue | 🟡 MinorPreserve existing
lastMessageAtwhen event payload omits it.Line 232 directly assigns
that.lastMessageAtwithout null-checking, which overwrites a valid value if the event omits the field. This breaks channel ordering that depends onlastMessageAt. The function already handles this correctly formessageCount(line 231) using the Elvis operator—apply the same pattern here:Suggested fix
- lastMessageAt = that.lastMessageAt, + lastMessageAt = that.lastMessageAt ?: this.lastMessageAt,stream-chat-android-core/src/main/java/io/getstream/chat/android/models/Channel.kt (1)
361-380:⚠️ Potential issue | 🟡 MinorPreserve existing lastMessageAt when merge payload is null.
Both
messageCountandlastMessageAtare nullable fields, butmessageCountalready uses the Elvis operator to preserve the existing value when null (line 378). ThelastMessageAtfield should follow the same pattern to prevent data loss when event payloads omit this field.💡 Suggested adjustment
- lastMessageAt = that.lastMessageAt, + lastMessageAt = that.lastMessageAt ?: this.lastMessageAt,
🤖 Fix all issues with AI agents
In
`@stream-chat-android-state/src/main/java/io/getstream/chat/android/state/utils/internal/ChannelUtils.kt`:
- Around line 45-63: The calculateNewLastMessageAt function should prefer server
timestamps to avoid under-ordering: replace the current messageDate =
message.createdLocallyAt ?: message.createdAt ?: return currentLastMessageAt
with logic that uses message.createdAt when available (or the max of
message.createdAt and message.createdLocallyAt when both exist) so the chosen
messageDate uses the server timestamp if present; keep falling back to
createdLocallyAt only if createdAt is null and preserve the existing checks
around shadowed, system, and thread messages.
🧹 Nitpick comments (1)
stream-chat-android-core/src/main/java/io/getstream/chat/android/models/Channel.kt (1)
209-350: Add KDoc for the new builder setter.
withLastMessageAtis a new public API surface; please add a short KDoc to keep API docs consistent.✍️ Suggested addition
+ /** + * Sets the last message timestamp for the channel. + */ public fun withLastMessageAt(lastMessageAt: Date?): Builder = apply { this.lastMessageAt = lastMessageAt }As per coding guidelines Document public APIs with KDoc, including thread expectations and state notes.
...t-android-state/src/main/java/io/getstream/chat/android/state/utils/internal/ChannelUtils.kt
Show resolved
Hide resolved
…ageat_calculation
|
Channel.lastMessageAt as a computed property derived from the currently loaded messages



🎯 Goal
We are currently calculating
Channel.lastMessageAtas a computed property derived from the currently loaded messages. This can be wrong, if for example we have a deleted message as the last one in the channel, and we only fetch the last message from the channel.In this PR, this logic is reworked so that the
lastMessageAtis initially populated from theQueryChannelscall, and later updated by the relevant events (message.new,notification.message_new). This also aligns the logic with iOS.🛠 Implementation details
lastMessageAtfrom a computed property (derived from messages list) to a stored property onChannelandChannelDatacalculateNewLastMessageAtutility function with proper filtering logic that skips shadowed messages, system messages (when configured), and thread replies not shown in channelChannelStateLogic,ChannelEventHandler, andEventHandlerSequentialto properly calculate and updatelastMessageAtwhen messages changelastMessageAt🎨 UI Changes
Observe the Other Group channel
before.mp4
after.mp4
🧪 Testing
calculateNewLastMessageAtfunctionlastMessageAtworks correctlySummary by CodeRabbit
Improvements
Tests