Skip to content

Handle member related events and channel truncated event in LivestreamChannelController #3775

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Aug 11, 2025

Conversation

nuno-vieira
Copy link
Member

@nuno-vieira nuno-vieira commented Aug 11, 2025

🔗 Issue Links

https://linear.app/stream/issue/IOS-1066/livestream-controller-member-and-truncation-events

🎯 Goal

Handle member related events and channel truncated event inLivestreamChannelController.

🛠 Implementation

For now, these events will still go through the DB to make sure it does not break the Channel List updates, but we make sure the data is updated inside the livestream controller as well.

🧪 Manual Testing Notes

Members:

  1. Open the Channel List
  2. Swipe
  3. Tap on "..."
  4. Show as livestream controller
  5. Add a member / Remove a member
  6. It should update the header subtitle text with the number of members, and online members

Truncation:

  1. Open the Channel List
  2. Swipe
  3. Tap on "..."
  4. Show as livestream controller
  5. Truncate a channel with and without a message
  6. It should update the messages of the channel accordingly

☑️ Contributor Checklist

  • I have signed the Stream CLA (required)
  • This change should be manually QAed
  • Changelog is updated with client-facing changes
  • Changelog is updated with new localization keys
  • New code is covered by unit tests
  • Documentation has been updated in the docs-content repo

Summary by CodeRabbit

  • New Features

    • Demo Livestream chat now shows a two-line navigation title with channel name, member count, and online watcher count, with proper pluralization.
  • Improvements

    • Livestream channels now update in real time for member changes and channel truncation. Messages are reset appropriately when a channel is truncated.
  • Documentation

    • Changelog updated to note new event-handling capabilities.
  • Tests

    • Expanded test coverage for member events, channel truncation, cross-channel event filtering, and paused-state behavior.

@nuno-vieira nuno-vieira requested a review from a team as a code owner August 11, 2025 14:42
Copy link

coderabbitai bot commented Aug 11, 2025

Walkthrough

Adds member-related and channel truncation event handling to LivestreamChannelController, introduces a datastore-backed channel refresh path, updates demo app navigation title to show members/online counts, extends test tools to override memberCount in dummy payloads, updates changelog, and adds comprehensive tests for the new event flows.

Changes

Cohort / File(s) Summary
Changelog updates
CHANGELOG.md
Added entries for handling member-related and truncation events in LivestreamChannelController under Upcoming.
Demo app UI title view
DemoApp/Screens/Livestream/DemoLivestreamChatChannelVC.swift
Added custom navigation titleView with title and subtitle showing channel name, member count, and online count; updated on channel changes.
Controller event handling
Sources/StreamChat/Controllers/ChannelController/LivestreamChannelController.swift
Expanded handleChannelEvent to process member-related and truncation events; added updateChannelFromDataStore() fallback; implemented truncation logic to refresh channel and reset messages.
Test tools payloads
TestTools/StreamChatTestTools/TestData/DummyData/ChannelDetailPayload.swift
Added optional memberCount parameter to dummy() and used it when provided.
Tests for new flows
Tests/StreamChatTests/Controllers/ChannelController/LivestreamChannelController_Tests.swift
Added tests for member-related events updating from datastore, truncation behavior (with/without message), ignored events for other channels, and paused behavior counters.

Sequence Diagram(s)

sequenceDiagram
  participant Event as Incoming Event
  participant Controller as LivestreamChannelController
  participant Store as DataStore
  participant UI as Observers

  Event->>Controller: handleChannelEvent(event)
  alt Member-related event without channel payload
    Controller->>Store: fetch channel by cid
    Store-->>Controller: latest channel (if any)
    Controller->>UI: notify channel updated
  else ChannelTruncatedEvent
    Controller->>Controller: update channel from event.channel
    Controller->>Controller: replace messages (truncate message or empty)
    Controller->>UI: notify channel/messages updated
  else Other events
    Controller->>Controller: existing handling
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Assessment against linked issues

Objective Addressed Explanation
Handle member-related events in LivestreamChannelController [IOS-1066]
Handle channel truncation events in LivestreamChannelController [IOS-1066]
Add/update tests validating member-related and truncation handling [IOS-1066]

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
Add custom navigation title view showing member/online counts (DemoApp/Screens/Livestream/DemoLivestreamChatChannelVC.swift) UI enhancement in demo app; not required by controller event handling for member or truncation events.

Possibly related PRs

Suggested labels

🤞 Ready For QA

Suggested reviewers

  • laevandus
  • martinmitrevski

Poem

A hop and a bop through channels I go,
I nibble on events as they trickle and flow.
Members hop in, some hop away—
I fetch from the burrow without delay.
Truncate the trail, leave one message bright,
Then twitch my whiskers: all states set right.
🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch add/livestream-controller-members-live-updates

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@nuno-vieira nuno-vieira changed the title Handle member related events and channel truncated event inLivestreamChannelController Handle member related events and channel truncated event in LivestreamChannelController Aug 11, 2025
@Stream-SDK-Bot
Copy link
Collaborator

SDK Size

title develop branch diff status
StreamChat 8.04 MB 8.04 MB 0 KB 🟢
StreamChatUI 4.86 MB 4.86 MB 0 KB 🟢

@Stream-SDK-Bot
Copy link
Collaborator

SDK Performance

target metric benchmark branch performance status
MessageList Hitches total duration 10 ms 1.67 ms 83.3% 🔼 🟢
Duration 2.6 s 2.55 s 1.92% 🔼 🟢
Hitch time ratio 4 ms per s 0.66 ms per s 83.5% 🔼 🟢
Frame rate 75 fps 78.23 fps 4.31% 🔼 🟢
Number of hitches 1 0.2 80.0% 🔼 🟢

@Stream-SDK-Bot
Copy link
Collaborator

Stream-SDK-Bot commented Aug 11, 2025

SDK Size

title develop branch diff status
StreamChat 8.04 MB 8.04 MB 0 KB 🟢
StreamChatUI 4.86 MB 4.86 MB 0 KB 🟢

Copy link

Public Interface

🚀 No changes affecting the public interface.

@nuno-vieira nuno-vieira enabled auto-merge (squash) August 11, 2025 14:56
Copy link

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

🧹 Nitpick comments (1)
DemoApp/Screens/Livestream/DemoLivestreamChatChannelVC.swift (1)

411-422: Consider localization and edge-case formatting for subtitle

The pluralization logic is fine, but strings are hardcoded. Consider:

  • Localizing “member(s)” and “online”.
  • Optionally hiding “, 0 online” when both counts are zero to reduce noise.

Example minimal tweak (optional):

- subtitleLabel.text = "\(membersText), \(onlineText)"
+ subtitleLabel.text = memberCount == 0 && watcherCount == 0
+   ? membersText
+   : "\(membersText), \(onlineText)"
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5417035 and 8956fd9.

📒 Files selected for processing (5)
  • CHANGELOG.md (1 hunks)
  • DemoApp/Screens/Livestream/DemoLivestreamChatChannelVC.swift (4 hunks)
  • Sources/StreamChat/Controllers/ChannelController/LivestreamChannelController.swift (2 hunks)
  • TestTools/StreamChatTestTools/TestData/DummyData/ChannelDetailPayload.swift (2 hunks)
  • Tests/StreamChatTests/Controllers/ChannelController/LivestreamChannelController_Tests.swift (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
Tests/StreamChatTests/Controllers/ChannelController/LivestreamChannelController_Tests.swift (7)
Tests/StreamChatTests/Database/DTOs/ChannelDTO_Tests.swift (1)
  • channel (1772-1774)
Sources/StreamChat/WebSocketClient/Events/EventType.swift (1)
  • event (182-260)
TestTools/StreamChatTestTools/TestData/DummyData/ChannelDetailPayload.swift (1)
  • dummy (11-56)
TestTools/StreamChatTestTools/SpyPattern/Spy/DatabaseContainer_Spy.swift (1)
  • writeSynchronously (175-182)
Sources/StreamChat/Controllers/ChannelController/LivestreamChannelController.swift (2)
  • synchronize (215-226)
  • eventsController (748-754)
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (3)
  • synchronize (243-246)
  • synchronize (1816-1840)
  • message (2048-2053)
TestTools/StreamChatTestTools/SpyPattern/Spy/APIClient_Spy.swift (1)
  • test_simulateResponse (108-111)
Sources/StreamChat/Controllers/ChannelController/LivestreamChannelController.swift (3)
DemoApp/Screens/Livestream/DemoLivestreamChatChannelVC.swift (1)
  • channel (212-214)
Tests/StreamChatTests/Database/DTOs/ChannelDTO_Tests.swift (1)
  • channel (1772-1774)
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (1)
  • message (2048-2053)
DemoApp/Screens/Livestream/DemoLivestreamChatChannelVC.swift (3)
Sources/StreamChat/Controllers/ChannelController/LivestreamChannelController.swift (5)
  • livestreamChannelController (11-13)
  • livestreamChannelController (1096-1099)
  • livestreamChannelController (1101-1104)
  • livestreamChannelController (1106-1109)
  • livestreamChannelController (1111-1114)
Tests/StreamChatTests/Controllers/ChannelController/LivestreamChannelController_Tests.swift (4)
  • livestreamChannelController (734-740)
  • livestreamChannelController (742-748)
  • livestreamChannelController (750-756)
  • livestreamChannelController (758-764)
Sources/StreamChat/Controllers/ChannelController/LivestreamChannelController+Combine.swift (4)
  • livestreamChannelController (60-65)
  • livestreamChannelController (67-72)
  • livestreamChannelController (74-79)
  • livestreamChannelController (81-86)
⏰ 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). (2)
  • GitHub Check: Test E2E UI (Debug) (0)
  • GitHub Check: Test E2E UI (Debug) (1)
🔇 Additional comments (7)
CHANGELOG.md (1)

8-9: Changelog entries look correct and consistent

Both items accurately describe the scope of this PR and link to the right PR. No action needed.

TestTools/StreamChatTestTools/TestData/DummyData/ChannelDetailPayload.swift (1)

29-30: Good addition to support explicit memberCount in tests

Providing memberCount with a sensible default to members.count is perfect for precise test setup. No issues spotted.

Also applies to: 52-53

DemoApp/Screens/Livestream/DemoLivestreamChatChannelVC.swift (3)

53-81: Navigation title UI implementation is solid

Stacked title/subtitle with proper fonts and priorities is clean and predictable for a titleView. Looks good.


156-159: Correctly initializes the custom title view

Setting titleView and calling updateNavigationTitle() during layout ensures an initial value. LGTM.


344-350: Right place to refresh header on channel updates

Updating the avatar and calling updateNavigationTitle() on didUpdateChannel keeps the header in sync. Good.

Tests/StreamChatTests/Controllers/ChannelController/LivestreamChannelController_Tests.swift (2)

1234-1358: Great coverage for all member-related event variants

Iterating across all 8 event types and verifying datastore-driven updates is thorough and effective. Nicely structured.


1360-1453: Truncation behavior tests are clear and robust

Validates both with/without truncation message. This matches the controller behavior precisely. LGTM.

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 (8)
CHANGELOG.md (1)

8-9: Changelog entries look good; consider clarifying scope.

Both entries are clear. If space allows, append a short note that the events are handled via the data store to avoid breaking Channel List updates (as implemented), to make the behavioral nuance discoverable from the changelog.

TestTools/StreamChatTestTools/TestData/DummyData/ChannelDetailPayload.swift (1)

29-29: Good addition; source-compatible and flexible for tests.

  • Adding memberCount: Int? = nil with a default keeps existing callers intact.
  • The fallback memberCount ?? members.count is sensible.

Optional: add a brief doc note that memberCount overrides the derived count when provided, to avoid confusion in tests.

Also applies to: 52-53

DemoApp/Screens/Livestream/DemoLivestreamChatChannelVC.swift (2)

53-81: Custom title view setup is clean; add minor accessibility affordances.

Consider:

  • Setting titleStackView.isAccessibilityElement = true and assigning accessibilityLabel with combined title/subtitle for better VoiceOver experience.
  • Setting numberOfLines = 1 and lineBreakMode = .byTruncatingTail on labels to avoid awkward wrapping in narrow nav bars.

I can draft a snippet if you want.


411-422: Localize the subtitle string.

The “members, online” text is user-facing. For consistency with the SDK/ demo localizations, move it to a localization key and use pluralized forms. You already handle pluralization logic; just wire it to L10n.

Sources/StreamChat/Controllers/ChannelController/LivestreamChannelController.swift (3)

917-924: Truncation handling is correct; consider resetting pagination state.

You replace messages appropriately and update the channel. After truncation, previous/next pagination state may be invalid. Consider resetting paginationStateHandler to its initial state to avoid follow-up pagination using stale cursors.


986-994: DB refresh helper is straightforward; guard against event–DB ordering.

If middleware writes aren’t guaranteed to complete before this delegate runs, dataStore.channel(cid:) might briefly return stale data. If that’s a possibility, schedule the read on the next run loop tick:

private func updateChannelFromDataStore() {
    guard let cid = cid else { return }
    callback { [weak self] in
        guard let self, let updated = self.dataStore.channel(cid: cid) else { return }
        self.channel = updated
    }
}

This keeps mutations on the main thread and improves the odds the DB has applied the event.


917-924: Apply message limit uniformly for all added messages.

Currently, applyMessageLimit() is only called in the MessageNewEvent branch. To enforce limits also for NewMessagePendingEvent, move the limit application into handleNewMessage(_:) after insertion (while keeping the pause guard).

For example (outside the selected range):

private func handleNewMessage(_ message: ChatMessage) {
    // ... existing checks ...
    messages.insert(message, at: 0)
    if !isPaused { applyMessageLimit() }
}

Also applies to: 986-994

Tests/StreamChatTests/Controllers/ChannelController/LivestreamChannelController_Tests.swift (1)

934-970: Add a test to assert message limit is enforced for pending events as well.

Since applyMessageLimit currently runs only for MessageNewEvent, a regression could let the array grow during NewMessagePendingEvent. Recommend adding a sibling test that sends 100 messages, then a NewMessagePendingEvent, and asserts the capped size.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5417035 and 8956fd9.

📒 Files selected for processing (5)
  • CHANGELOG.md (1 hunks)
  • DemoApp/Screens/Livestream/DemoLivestreamChatChannelVC.swift (4 hunks)
  • Sources/StreamChat/Controllers/ChannelController/LivestreamChannelController.swift (2 hunks)
  • TestTools/StreamChatTestTools/TestData/DummyData/ChannelDetailPayload.swift (2 hunks)
  • Tests/StreamChatTests/Controllers/ChannelController/LivestreamChannelController_Tests.swift (1 hunks)
⏰ 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). (2)
  • GitHub Check: Test E2E UI (Debug) (0)
  • GitHub Check: Test E2E UI (Debug) (1)
🔇 Additional comments (5)
DemoApp/Screens/Livestream/DemoLivestreamChatChannelVC.swift (2)

156-159: Initializing title view early is fine; ensure updates on first channel load.

This calls updateNavigationTitle() before synchronize completes, so it will briefly show an empty title/subtitle. That’s acceptable for demo. If you prefer no flash, call it from didFinishSynchronizing only.


349-350: Good: refresh title on channel updates.

This keeps the header in sync with member/watcher counts after events.

Tests/StreamChatTests/Controllers/ChannelController/LivestreamChannelController_Tests.swift (3)

1234-1358: Strong coverage for member-related events via data-store-backed updates.

Nice table-driven loop verifying all eight event types refresh the channel from the DB. This should prevent regressions in memberCount handling.


1360-1413: Truncation behavior test is solid.

Validates both channel update and message replacement as intended.


1415-1453: Good: asserts messages are cleared when truncation message is absent.

This pins down the no-message truncation path.

@nuno-vieira nuno-vieira merged commit 73974a5 into develop Aug 11, 2025
14 checks passed
@nuno-vieira nuno-vieira deleted the add/livestream-controller-members-live-updates branch August 11, 2025 15:43
@Stream-SDK-Bot Stream-SDK-Bot mentioned this pull request Aug 13, 2025
@testableapple testableapple added the 🟢 QAed A PR that was QAed label Aug 13, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🟢 QAed A PR that was QAed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants