Skip to content

Conversation

@xsahil03x
Copy link
Member

@xsahil03x xsahil03x commented Dec 4, 2025

Submit a pull request

Fixes: FLU-341

Description of the pull request

This PR introduces the ability to mark a channel as unread from a specific point in time.

  • Added markUnreadByTimestamp(DateTime timestamp) to Channel, StreamChatClient, and the underlying API. This allows marking all messages after a given timestamp as unread.
  • Updated comments and documentation for clarity.
  • Added comprehensive unit tests for the new and modified functionalities across the API, client, and channel layers.

Summary by CodeRabbit

  • New Features

    • Timestamp-based unread marking: mark channels unread for all messages after a given timestamp.
  • Documentation

    • Clarified unread-marking behavior to specify the range starting point and updated related docs.
  • Tests

    • Added tests covering timestamp-based unread flows and client/API interactions.

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

This commit introduces the ability to mark a channel as unread from a specific point in time and enhances the existing `markUnread` functionality.

- Added `markUnreadByTimestamp(DateTime timestamp)` to `Channel`, `StreamChatClient`, and the underlying API. This allows marking all messages after a given timestamp as unread.
- Made the `messageId` parameter in `markUnread` optional. If not provided, the entire channel is marked as unread.
- Updated comments and documentation for clarity.
- Added comprehensive unit tests for the new and modified functionalities across the API, client, and channel layers.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 4, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Adds timestamp-based "mark unread" APIs: Channel.markUnreadByTimestamp and StreamChatClient.markChannelUnreadByTimestamp. The Channel API and client gateway gain timestamp endpoints; existing message-id-based markUnread signatures/docs were tightened. Tests and changelog updated accordingly.

Changes

Cohort / File(s) Summary
API Layer
packages/stream_chat/lib/src/core/api/channel_api.dart
Added markUnreadByTimestamp(String channelId, String channelType, DateTime timestamp) which POSTs { message_timestamp: ISO8601 } to the unread endpoint. Updated markUnread(...) to require a non-null messageId and send { message_id: ... }.
Channel Implementation
packages/stream_chat/lib/src/client/channel.dart
Added Future<EmptyResponse> markUnreadByTimestamp(DateTime timestamp) on Channel. Performs read-events capability validation and delegates to client. Updated markUnread docs to clarify message-id semantics.
Client Implementation
packages/stream_chat/lib/src/client/client.dart
Changed markChannelUnread signature to require a messageId positional parameter. Added markChannelUnreadByTimestamp(String channelId, String channelType, DateTime timestamp) which delegates to the channel API.
Tests
packages/stream_chat/test/src/client/channel_test.dart, packages/stream_chat/test/src/client/client_test.dart, packages/stream_chat/test/src/core/api/channel_api_test.dart
Added tests covering timestamp-based unread flows, capability checks, and API payload verification for both message_id and message_timestamp.
Changelog / Docs
packages/stream_chat/CHANGELOG.md
Documented the new timestamp-based unread APIs and noted updates to unread behavior and method signatures.

Sequence Diagram(s)

sequenceDiagram
    participant User as User Code
    participant Channel as Channel
    participant Client as StreamChatClient
    participant ChannelAPI as ChannelApi
    participant Server as Backend

    User->>Channel: markUnreadByTimestamp(timestamp)
    activate Channel
    Channel->>Channel: check readEvents capability
    Channel->>Client: markChannelUnreadByTimestamp(channelId, channelType, timestamp)
    deactivate Channel

    activate Client
    Client->>ChannelAPI: markUnreadByTimestamp(channelId, channelType, timestamp)
    deactivate Client

    activate ChannelAPI
    ChannelAPI->>Server: POST /channels/{type}/{id}/unread
    note right of ChannelAPI: payload { "message_timestamp": "ISO8601 UTC" }
    deactivate ChannelAPI

    activate Server
    Server-->>ChannelAPI: EmptyResponse
    deactivate Server

    ChannelAPI-->>Client: EmptyResponse
    Client-->>Channel: EmptyResponse
    Channel-->>User: EmptyResponse
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Pay attention to:
    • Signature changes for markChannelUnread and consistency across client/channel/api.
    • Correct UTC ISO8601 formatting for message_timestamp.
    • Capability (readEvents) validation coverage in tests and implementation.
    • Test mocks/verification for the new POST payloads.

Possibly related PRs

Suggested reviewers

  • renefloor
  • Brazol

Poem

🐰 I hop on timestamps, light and quick,

Mark unread from when the moments tick.
IDs or times — now choices bloom,
I guard your cursor, silence the zoom.
A tiny rabbit, timestamp in paw, I cheer this patch with fluffy awe. ✨

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and concisely describes the main change: adding support for markUnreadByTimestamp functionality.
Linked Issues check ✅ Passed The PR successfully implements the ability to mark messages unread by timestamp across all required layers (Channel, Client, API) with comprehensive tests.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the markUnreadByTimestamp feature. Documentation updates to markUnread methods are supporting changes for clarity.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

📜 Recent review details

Configuration used: CodeRabbit 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 77cb84c and 108b8fd.

📒 Files selected for processing (7)
  • packages/stream_chat/CHANGELOG.md (1 hunks)
  • packages/stream_chat/lib/src/client/channel.dart (2 hunks)
  • packages/stream_chat/lib/src/client/client.dart (2 hunks)
  • packages/stream_chat/lib/src/core/api/channel_api.dart (2 hunks)
  • packages/stream_chat/test/src/client/channel_test.dart (1 hunks)
  • packages/stream_chat/test/src/client/client_test.dart (1 hunks)
  • packages/stream_chat/test/src/core/api/channel_api_test.dart (1 hunks)

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
Contributor

@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 (4)
packages/stream_chat/lib/src/client/client.dart (1)

1344-1372: Client unread helpers correctly delegate to ChannelApi

markChannelUnread and markChannelUnreadByTimestamp cleanly forward to the underlying ChannelApi methods and align with the documented semantics (whole-channel when messageId is omitted, or by timestamp). The only minor nit is the asymmetry with markChannelRead, which uses a named optional messageId; consider converging on a single parameter style long‑term for API consistency.

packages/stream_chat/lib/src/core/api/channel_api.dart (1)

326-355: Mark-unread API wiring looks correct and consistent

markUnread and markUnreadByTimestamp hit the expected /unread endpoint, conditionally include message_id or message_timestamp, and normalize timestamps to UTC ISO‑8601, matching the new tests. You might optionally clarify in the doc comment that omitting messageId marks the entire channel as unread, mirroring the changelog wording, but behavior is already correct.

packages/stream_chat/lib/src/client/channel.dart (2)

1644-1659: markUnread API and implementation look correct; consider tightening the docs

The behavior (init check, capability guard, delegation to _client.markChannelUnread) is consistent with markRead and the rest of the read APIs, and the optional positional messageId aligns with the described change (whole-channel unread when null).

You might make the behavior a bit more explicit in the doc comment, e.g. note that omitting messageId marks the entire channel as unread and passing a value marks from that message onwards. No functional changes needed.

-  /// Marks the channel as unread.
-  ///
-  /// Optionally provide a [messageId] to only mark messages from that ID
-  /// onwards as unread.
+  /// Marks the channel as unread for the current user.
+  ///
+  /// If [messageId] is omitted or `null`, the entire channel will be marked
+  /// as unread. Otherwise, only messages from the given [messageId] onwards
+  /// will be marked as unread.

1661-1675: markUnreadByTimestamp mirrors existing patterns; small doc/detail suggestions only

The method correctly mirrors markRead/markUnread/thread variants: it checks initialization, enforces canUseReadReceipts, and delegates to _client.markChannelUnreadByTimestamp, which is what you want.

Two small, non-blocking suggestions:

  • Clarify in the comment whether timestamp can be in local time and is converted to UTC by lower layers (to align with the REST contract).
  • Consider extracting a tiny helper for the repeated canUseReadReceipts guard shared by the five read/unread methods to reduce duplication, if this pattern continues to grow.
-  /// Marks the channel as unread by a given [timestamp].
-  ///
-  /// All messages after the provided timestamp will be marked as unread.
+  /// Marks the channel as unread from a given [timestamp].
+  ///
+  /// All messages created after the provided [timestamp] will be counted as
+  /// unread for the current user.
+  ///
+  /// The [timestamp] can be a local time; it is converted to UTC when sent
+  /// to the API.
📜 Review details

Configuration used: CodeRabbit 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 ba177ae and 2dba5e3.

📒 Files selected for processing (7)
  • packages/stream_chat/CHANGELOG.md (1 hunks)
  • packages/stream_chat/lib/src/client/channel.dart (2 hunks)
  • packages/stream_chat/lib/src/client/client.dart (1 hunks)
  • packages/stream_chat/lib/src/core/api/channel_api.dart (1 hunks)
  • packages/stream_chat/test/src/client/channel_test.dart (3 hunks)
  • packages/stream_chat/test/src/client/client_test.dart (2 hunks)
  • packages/stream_chat/test/src/core/api/channel_api_test.dart (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-09-25T08:19:01.469Z
Learnt from: xsahil03x
Repo: GetStream/stream-chat-flutter PR: 2394
File: packages/stream_chat_flutter/lib/src/message_action/message_actions_builder.dart:82-92
Timestamp: 2025-09-25T08:19:01.469Z
Learning: In the Stream Chat Flutter library, when deleting a message with MessageSendingStatus.failed or MessageSendingStatus.failed_update status, the _deleteMessage method in channel.dart automatically handles deletion locally via _deleteLocalMessage without making API calls, preventing 404 errors and deletingFailed states.

Applied to files:

  • packages/stream_chat/test/src/core/api/channel_api_test.dart
  • packages/stream_chat/lib/src/client/channel.dart
  • packages/stream_chat/lib/src/client/client.dart
  • packages/stream_chat/test/src/client/channel_test.dart
  • packages/stream_chat/lib/src/core/api/channel_api.dart
📚 Learning: 2025-09-25T08:19:01.469Z
Learnt from: xsahil03x
Repo: GetStream/stream-chat-flutter PR: 2394
File: packages/stream_chat_flutter/lib/src/message_action/message_actions_builder.dart:82-92
Timestamp: 2025-09-25T08:19:01.469Z
Learning: In the Stream Chat Flutter library, when deleting a message that hasn't been sent to the server yet (message.remoteCreatedAt == null) or is bounced with error, the _deleteMessage method in channel.dart automatically handles deletion locally via _deleteLocalMessage without making API calls, preventing 404 errors and deletingFailed states.

Applied to files:

  • packages/stream_chat/test/src/core/api/channel_api_test.dart
  • packages/stream_chat/lib/src/client/channel.dart
  • packages/stream_chat/lib/src/client/client.dart
  • packages/stream_chat/test/src/client/channel_test.dart
  • packages/stream_chat/lib/src/core/api/channel_api.dart
⏰ 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). (10)
  • GitHub Check: analyze_legacy_versions
  • GitHub Check: build (android)
  • GitHub Check: test
  • GitHub Check: build (ios)
  • GitHub Check: analyze
  • GitHub Check: stream_chat_localizations
  • GitHub Check: stream_chat_flutter
  • GitHub Check: stream_chat_flutter_core
  • GitHub Check: stream_chat
  • GitHub Check: stream_chat_persistence
🔇 Additional comments (6)
packages/stream_chat/CHANGELOG.md (1)

1-12: Changelog entry accurately reflects API behavior

The description of markUnreadByTimestamp and the optional messageId semantics for markUnread/markChannelUnread matches the implemented client and API methods and is clear for consumers.

packages/stream_chat/test/src/client/client_test.dart (1)

2037-2102: New client tests thoroughly cover unread entry points

The three tests for .markChannelUnread (with and without messageId) and .markChannelUnreadByTimestamp correctly assert delegation to api.channel.markUnread/markUnreadByTimestamp and ensure no extra interactions. This gives good coverage over the new optional parameter and timestamp paths.

packages/stream_chat/test/src/core/api/channel_api_test.dart (1)

608-685: ChannelApi unread tests match the new REST payload contracts

The added tests for markUnread (with and without messageId) and markUnreadByTimestamp verify the correct /unread path and distinguish the expected payload shapes, closely mirroring the implementation. This should catch regressions in how unread markers are serialized.

packages/stream_chat/test/src/client/channel_test.dart (3)

6740-6771: LGTM! Excellent test coverage for optional messageId parameter.

The test properly validates that markUnread() can be called without parameters when the readEvents capability is present. The mock setup and verification correctly assert that client.markChannelUnread is called with only channelId and channelType, confirming the optional parameter behavior.


6773-6806: LGTM! Good test coverage for backward compatibility.

This test ensures that markUnread(messageId) continues to work correctly when a messageId is explicitly provided, validating backward compatibility with the existing API usage. The mock properly includes the messageId parameter in the expected call.


6808-6864: LGTM! Comprehensive test coverage for the new markUnreadByTimestamp method.

These tests properly validate the new timestamp-based unread marking functionality:

  • First test ensures proper error handling when readEvents capability is missing
  • Second test confirms successful execution when capability is present
  • Both tests use consistent timestamp creation and follow established testing patterns

The implementation aligns with the PR objectives to add the ability to mark messages as unread from a specific timestamp.

@codecov
Copy link

codecov bot commented Dec 4, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 64.65%. Comparing base (ba177ae) to head (108b8fd).
⚠️ Report is 3 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2460      +/-   ##
==========================================
+ Coverage   64.61%   64.65%   +0.04%     
==========================================
  Files         420      420              
  Lines       26210    26226      +16     
==========================================
+ Hits        16936    16957      +21     
+ Misses       9274     9269       -5     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@xsahil03x xsahil03x changed the title feat(llc): add markUnreadByTimestamp and improve markUnread feat(llc): add support for markUnreadByTimestamp Dec 4, 2025
@xsahil03x xsahil03x merged commit 52f1ea2 into master Dec 4, 2025
13 of 14 checks passed
@xsahil03x xsahil03x deleted the feat/mark-unread-by-timestamp branch December 4, 2025 14:37
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.

3 participants