Skip to content

client: Keep persisted channel lastMessageAt consistent with its latest message - #6621

Merged
gpunto merged 1 commit into
developfrom
fix/channel-stale-last-message-at
Aug 7, 2026
Merged

client: Keep persisted channel lastMessageAt consistent with its latest message#6621
gpunto merged 1 commit into
developfrom
fix/channel-stale-last-message-at

Conversation

@gpunto

@gpunto gpunto commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Goal

Channels could be sorted by a stale lastMessageAt when the persisted value lagged the channel's newest visible message, so a row would show a newer message than its position in the list reflected.

Closes #6604

Implementation

When persisting a channel, keep lastMessageAt consistent with its last message instead of trusting the stored field, so it never lags behind (nor regresses below) the newest visible message. This matches the iOS SDK's behavior.

Testing

  • ChannelMapperTest: added coverage for advancing to a newer message, not regressing when the stored value is newer, and the no-last-message case; updated the existing mapping test.
  • On device: confirmed the SDK heals real channels on launch and that stream_chat_channel_state.lastMessageAt matches the channel's last message on this build versus staying stale on develop.
  • spotlessCheck, detekt, apiCheck (no public API change), and the client testDebugUnitTest suite pass.

Summary by CodeRabbit

  • Bug Fixes
    • Improved channel activity tracking by recording the most recent timestamp between the channel update and its latest message.
    • Preserved the channel timestamp when it is newer than the last message.
    • Correctly handles channels without a last message.

@gpunto gpunto added the pr:bug Bug fix label Aug 6, 2026
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

PR checklist ✅

All required conditions are satisfied:

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

🎉 Great job! This PR is ready for review.

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

SDK Size Comparison 📏

SDK Before After Difference Status
stream-chat-android-client 5.96 MB 5.96 MB 0.00 MB 🟢
stream-chat-android-ui-components 11.25 MB 11.25 MB 0.00 MB 🟢
stream-chat-android-compose 12.73 MB 12.73 MB 0.00 MB 🟢

@sonarqubecloud

sonarqubecloud Bot commented Aug 6, 2026

Copy link
Copy Markdown

@gpunto
gpunto marked this pull request as ready for review August 6, 2026 13:09
@gpunto
gpunto requested a review from a team as a code owner August 6, 2026 13:09
@gpunto
gpunto enabled auto-merge August 6, 2026 13:09
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Channel.toEntity() now stores the later of the channel timestamp and the last message creation time. Tests cover newer messages, newer channel timestamps, and channels without messages.

Changes

Channel timestamp mapping

Layer / File(s) Summary
Merge channel and message timestamps
stream-chat-android-client/src/main/.../ChannelMapper.kt
Channel.toEntity() uses the later available timestamp for lastMessageAt.
Validate timestamp precedence
stream-chat-android-client/src/test/.../ChannelMapperTest.kt
Tests verify timestamp selection and preservation of the newest message ID.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: aleksandar-apostolov, andremion, kanat

Poem

I’m a rabbit with timestamps bright,
Mapping the newest message right.
Stale dates hop away,
Newer ones now stay,
Channel rows sort in clear sight.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the primary change: keeping persisted channel lastMessageAt consistent with the latest message.
Description check ✅ Passed The description includes clear Goal, Implementation, and Testing sections with relevant validation details.
Linked Issues check ✅ Passed The implementation and tests satisfy issue #6604 by preventing stale or regressed lastMessageAt values.
Out of Scope Changes check ✅ Passed The changes are limited to channel timestamp mapping and focused unit test coverage required by issue #6604.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/channel-stale-last-message-at

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

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
stream-chat-android-client/src/test/java/io/getstream/chat/android/client/internal/offline/repository/domain/channel/internal/ChannelMapperTest.kt (1)

90-90: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Cover the local message timestamp path.

Channel.toEntity() uses lastMessage?.getCreatedAtOrNull(). Line 90 uses lastMessage.createdAt, and the added cases set createdLocallyAt = null. This does not verify the branch where createdLocallyAt is present.

Use getCreatedAtOrNull() in the expected value and add a case with different createdLocallyAt and createdAt values.

As per coding guidelines, add or refresh tests for changed behavior; the supplied helper contract uses createdLocallyAt when it is available.

Suggested expectation update
-            lastMessageAt = listOfNotNull(channel.lastMessageAt, lastMessage.createdAt).maxOrNull(),
+            lastMessageAt = listOfNotNull(
+                channel.lastMessageAt,
+                lastMessage.getCreatedAtOrNull(),
+            ).maxOrNull(),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@stream-chat-android-client/src/test/java/io/getstream/chat/android/client/internal/offline/repository/domain/channel/internal/ChannelMapperTest.kt`
at line 90, Update the expected timestamp in ChannelMapperTest around the
lastMessageAt assertion to use lastMessage.getCreatedAtOrNull() instead of
lastMessage.createdAt, and add a test case where createdLocallyAt and createdAt
differ with createdLocallyAt populated to verify the local timestamp path.

Source: Coding guidelines

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

Nitpick comments:
In
`@stream-chat-android-client/src/test/java/io/getstream/chat/android/client/internal/offline/repository/domain/channel/internal/ChannelMapperTest.kt`:
- Line 90: Update the expected timestamp in ChannelMapperTest around the
lastMessageAt assertion to use lastMessage.getCreatedAtOrNull() instead of
lastMessage.createdAt, and add a test case where createdLocallyAt and createdAt
differ with createdLocallyAt populated to verify the local timestamp path.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: bf8877b1-fc32-4b73-94c9-286b313b89ac

📥 Commits

Reviewing files that changed from the base of the PR and between c7496c6 and e661510.

📒 Files selected for processing (2)
  • stream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/offline/repository/domain/channel/internal/ChannelMapper.kt
  • stream-chat-android-client/src/test/java/io/getstream/chat/android/client/internal/offline/repository/domain/channel/internal/ChannelMapperTest.kt

@aleksandar-apostolov aleksandar-apostolov left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM — does this also need a backport to v6?

@gpunto

gpunto commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

LGTM — does this also need a backport to v6?

Good point, backporting it in #6623

Merged via the queue into develop with commit 43e080e Aug 7, 2026
20 of 21 checks passed
@gpunto
gpunto deleted the fix/channel-stale-last-message-at branch August 7, 2026 11:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr:bug Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Channel list uses stale lastMessageAt after loading a newer message

2 participants