Skip to content

feat(llc): Implement local unread counts - #2804

Merged
renefloor merged 4 commits into
masterfrom
feat/local-unread-count
Jul 29, 2026
Merged

renefloor merged 4 commits into
masterfrom
feat/local-unread-count

Conversation

@renefloor

@renefloor renefloor commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Submit a pull request

Linear: FLU-587

CLA

  • I have signed the Stream CLA (required).
  • The code changes follow best practices
  • Code changes are tested (add some information if not applicable)

Description of the pull request

This adds an option for local only unread counts, especially interesting for things like livestreams with many updates.

Summary by CodeRabbit

  • New Features
    • Added optional client-side local unread-count tracking for channels without read events (off by default via isLocalUnreadCountEnabled).
    • When enabled, unreadCount updates on new messages, decrements on hard deletes only, and markRead/markUnread/markUnreadByTimestamp adjust local unread boundaries without network calls.
  • Documentation
    • Updated the SDK changelog and unread-count rules to reflect local behavior and how it interacts with read receipts.
  • Tests
    • Added coverage for local unread counting, boundary semantics, state merge preservation, and no-network assertions (including new client/mocks support).

@renefloor renefloor changed the title Implement local unread counts feat(ui): Implement local unread counts Jul 10, 2026
@renefloor renefloor changed the title feat(ui): Implement local unread counts feat(llc): Implement local unread counts Jul 10, 2026
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b56ee0d7-b5f2-4175-a92e-dbc2f4011b9a

📥 Commits

Reviewing files that changed from the base of the PR and between 2c7a4a6 and 831ea6c.

📒 Files selected for processing (4)
  • docs/docs_screenshots/test/src/mocks.dart
  • packages/stream_chat/CHANGELOG.md
  • packages/stream_chat/lib/src/client/channel.dart
  • packages/stream_chat/test/src/client/channel_test.dart
🚧 Files skipped from review as they are similar to previous changes (3)
  • docs/docs_screenshots/test/src/mocks.dart
  • packages/stream_chat/test/src/client/channel_test.dart
  • packages/stream_chat/lib/src/client/channel.dart

📝 Walkthrough

Walkthrough

Adds opt-in local unread-count tracking for channels without read events, including local read mutations, message-based count updates, remote-state preservation, tests, mocks, and changelog documentation.

Changes

Local unread count tracking

Layer / File(s) Summary
Unread count configuration and eligibility
packages/stream_chat/lib/src/client/client.dart, packages/stream_chat/lib/src/client/channel.dart, packages/stream_chat/lib/src/core/util/message_rules.dart, packages/stream_chat/test/src/core/util/message_rules_test.dart
Adds isLocalUnreadCountEnabled, derives usesLocalUnreadCount, and allows unread counting without read receipts when local tracking is enabled.
Local channel mutations and state merging
packages/stream_chat/lib/src/client/channel.dart, packages/stream_chat/lib/src/client/client.dart
Adds local mark-read/unread operations, local unread recomputation, hard-delete decrements, and remote-state merging that preserves local unread fields.
Behavior validation and supporting wiring
packages/stream_chat/test/src/client/channel_test.dart, packages/stream_chat/test/src/mocks.dart, docs/docs_screenshots/test/src/mocks.dart, packages/stream_chat/CHANGELOG.md
Tests local unread behavior and updates test mocks and changelog documentation for the new flag.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant MessageEvent
  participant Channel
  participant MessageRules
  participant ChannelClientState
  MessageEvent->>Channel: Receive message.new or message.deleted
  Channel->>MessageRules: Check unread eligibility
  MessageRules-->>Channel: Return eligibility
  Channel->>ChannelClientState: Update local unread count
  Channel->>ChannelClientState: Apply local read boundary
  ChannelClientState-->>Channel: Return recomputed unread state
Loading

Possibly related PRs

  • GetStream/stream-chat-flutter#2819: Both changes affect the mark-read flow; this PR adds local unread updates while the related change updates unread-indicator dismissal behavior.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding optional local unread-count tracking.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/local-unread-count

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.

@renefloor
renefloor force-pushed the feat/local-unread-count branch from 82e91e4 to a495169 Compare July 10, 2026 08:46

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/stream_chat/test/src/client/channel_test.dart (1)

9810-10089: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a positive-path test for Channel.markUnread local recompute.

The new group thoroughly covers markRead and markUnreadByTimestamp local recompute, plus markUnread's throw-when-unknown-message case, but there's no test exercising a successful markUnread(messageId) call and asserting the resulting unreadCount — the anchor/microsecond-offset arithmetic in Channel.markUnread (subtracting 1 microsecond from the anchor's createdAt) is currently only covered indirectly.

🤖 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 `@packages/stream_chat/test/src/client/channel_test.dart` around lines 9810 -
10089, The local unread-count tests lack a successful-path case for
Channel.markUnread. Add a test in the “Local unread count” group that creates
locally known messages with distinct timestamps, calls markUnread with a valid
message ID, asserts unreadCount reflects only messages after the anchor
(including the one-microsecond offset behavior), and verifies no network request
via client.markChannelUnread.
🤖 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.

Inline comments:
In `@packages/stream_chat/lib/src/client/channel.dart`:
- Around line 3615-3637: The markReadLocally method does not reconcile pending
delivery receipts after updating read state. Add
_client.channelDeliveryReporter.reconcileDelivery([_channel]) to markReadLocally
after updateRead, matching the delivery reconciliation behavior used by related
mark-read flows.

---

Nitpick comments:
In `@packages/stream_chat/test/src/client/channel_test.dart`:
- Around line 9810-10089: The local unread-count tests lack a successful-path
case for Channel.markUnread. Add a test in the “Local unread count” group that
creates locally known messages with distinct timestamps, calls markUnread with a
valid message ID, asserts unreadCount reflects only messages after the anchor
(including the one-microsecond offset behavior), and verifies no network request
via client.markChannelUnread.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 030f7148-3f52-4392-aa77-629db23c3680

📥 Commits

Reviewing files that changed from the base of the PR and between 484ada6 and 82e91e4.

📒 Files selected for processing (8)
  • docs/docs_screenshots/test/src/mocks.dart
  • packages/stream_chat/CHANGELOG.md
  • packages/stream_chat/lib/src/client/channel.dart
  • packages/stream_chat/lib/src/client/client.dart
  • packages/stream_chat/lib/src/core/util/message_rules.dart
  • packages/stream_chat/test/src/client/channel_test.dart
  • packages/stream_chat/test/src/core/util/message_rules_test.dart
  • packages/stream_chat/test/src/mocks.dart

Comment thread packages/stream_chat/lib/src/client/channel.dart
@codecov

codecov Bot commented Jul 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 72.83%. Comparing base (688c86d) to head (831ea6c).

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2804      +/-   ##
==========================================
+ Coverage   72.77%   72.83%   +0.05%     
==========================================
  Files         428      428              
  Lines       27602    27657      +55     
==========================================
+ Hits        20088    20144      +56     
+ Misses       7514     7513       -1     

☔ View full report in Codecov by Harness.
📢 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.

Comment thread packages/stream_chat/lib/src/client/channel.dart
Comment thread packages/stream_chat/lib/src/client/channel.dart Outdated
Comment thread packages/stream_chat/lib/src/client/channel.dart
Comment thread packages/stream_chat/lib/src/client/channel.dart
@renefloor
renefloor merged commit 2507ca5 into master Jul 29, 2026
28 checks passed
@renefloor
renefloor deleted the feat/local-unread-count branch July 29, 2026 09:40
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