-
Notifications
You must be signed in to change notification settings - Fork 228
Mark messages as unread using timestamp #3885
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
base: develop
Are you sure you want to change the base?
Conversation
WalkthroughAdds timestamp-based "mark unread" support: introduces Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Client
participant ChatChannelController
participant ReadStateHandler
participant MessageRepository
participant ChannelUpdater
participant ChannelRepository
participant APIClient
participant Database
Client->>ChatChannelController: markUnread(from: Date)
ChatChannelController->>ReadStateHandler: markUnread(from: .messageTimestamp(date), in: channel)
ReadStateHandler->>MessageRepository: getMessage(before: .messageTimestamp(date))
MessageRepository->>Database: loadMessage(beforeOrEqual: timestamp)
Database-->>MessageRepository: MessageDTO?
MessageRepository-->>ReadStateHandler: Result<MessageId?, Error>
ReadStateHandler->>ChannelUpdater: markUnread(from: .messageTimestamp(date))
ChannelUpdater->>ChannelRepository: markUnread(from: .messageTimestamp(date))
ChannelRepository->>APIClient: POST /channels/{type}/{id}/unread (body: MarkUnreadPayload)
APIClient-->>ChannelRepository: EmptyResponse
ChannelRepository->>Database: markChannelAsUnread(for: cid, from: .messageTimestamp)
Database-->>ChannelRepository: updated ChatChannel
ChannelRepository-->>ChannelUpdater: Result<ChatChannel, Error>
ChannelUpdater-->>ReadStateHandler: Result<ChatChannel, Error>
ReadStateHandler-->>ChatChannelController: Result<ChatChannel, Error>
ChatChannelController-->>Client: completion(Result<ChatChannel, Error>)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20–30 minutes
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (1)
1303-1377: markUnread controller APIs are correct; tighten timestamp doc wording
- The message-id overload now correctly wraps
messageIdin.messageId(...)before delegating toreadStateHandler, reusing the existing guards and callback pattern.- The new
markUnread(from timestamp: Date, ...)overload reuses the same guards and delegates via.messageTimestamp(timestamp), which aligns withReadStateHandler/ChannelReadDTO’s use ofMarkUnreadCriteria.The doc comment for the timestamp overload says it:
finds the first message with a creation timestamp greater than the provided timestamp
but the DB layer actually:
- resolves a message at or before the timestamp (via
loadMessage(beforeOrEqual:timestamp, ...)), and- treats messages after that resolved message as unread, doing nothing if no such message can be found.
To avoid confusion in edge cases (e.g. timestamp earlier than the oldest message), consider rephrasing along the lines of:
This method resolves the last message with a creation timestamp less than or equal to the provided timestamp, and marks all later messages as unread. If no such message can be found, the operation completes without error and leaves the unread state unchanged.
Also, since this is a new public API on
ChatChannelController, ensure there is a corresponding CHANGELOG entry and migration note.Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swift (1)
4135-4328: Mark-unread tests align with new criteria-based flow; fix misleading comment and consider timestamp coverageThe updated mark‑unread tests correctly drive
controller.markUnread(from: MessageId)and assert thatChannelUpdater_MockreceivesmarkUnread_criteria == .messageId(messageId), which matches the newMarkUnreadCriteria‑based pipeline. The various error/no‑error branches (missing channel, read events disabled, marking read in progress, missing current user) remain consistent withReadStateHandler.markUnread.One small issue: in
test_markUnread_whenIsNotMarkingAsRead_andCurrentUserIdIsPresent_whenThereAreNoMessages_whenUpdaterSucceeds, the comment saying “we fallback to the passed messageId as lastReadMessageId” contradicts the assertionXCTAssertNil(updater.markUnread_lastReadMessageId). Please update or remove the comment to reflect actual behaviour.If there isn’t already a separate test exercising
ChatChannelController.markUnread(from timestamp: Date)(possibly in another suite), it would be good to add one for parity with the message‑ID path.
🧹 Nitpick comments (7)
Sources/StreamChat/StateLayer/Chat.swift (1)
937-950: Async Chat.markUnread overloads are wired correctly; add release notesBoth
markUnread(from messageId:)and the newmarkUnread(from timestamp:)correctly delegate viaMarkUnreadCriteriatoreadStateHandlerand reuse the existingChannelNotCreatedYeterror path. This keeps the async state-layer API consistent with the controller layer.Given this is a new public API surface in
StreamChat, make sure the CHANGELOG and migration docs call out:
- the new
Chat.markUnread(from: Date)API, and- the semantic equivalence between the message-id and timestamp variants.
Tests/StreamChatTests/Database/DTOs/ChannelReadDTO_Tests.swift (1)
425-630: Timestamp-based partial unread tests cover main paths; consider one more edge caseThe new
messageTimestamptests for:
- messages present (expecting correct
lastReadAt,unreadMessageCount, andlastReadMessageId), and- explicit
lastReadAt/unreadMessagesCountoverrides,mirror the existing message-id tests and validate that timestamp criteria behave identically at the DTO level.
If you want to harden behavior further, you could add a test where the timestamp is before the earliest message in the channel, asserting that the unread state stays unchanged (matching the current
guard let message = findMessageDTO()early return).Sources/StreamChat/Repositories/MessageRepository.swift (1)
341-371: MarkUnreadCriteria handling in repository looks correct; duplication is acceptable but could be reducedThe new
getMessage(before unreadCriteria: MarkUnreadCriteria, in:cid, ...)correctly routes.messageIdto the existingloadMessage(before: ...)path and.messageTimestamptoloadMessage(beforeOrEqual: ...), reusing shared config and preserving error handling. If you touch this again, consider extracting a tiny helper to avoid repeating the fetch+.idpattern in both switch cases, but it’s fine as-is.Sources/StreamChat/Database/DTOs/MessageDTO.swift (1)
610-625: Timestamp-basedloadMessagelooks correct; consider aligning with other fetch helpersThe predicate and sort (latest channel message with
createdAt <= timestamp) match the intended semantics and mirror the existingloadMessage(before:id:cid:...)path. For consistency with other fetch helpers, you might consider also callingMessageDTO.applyPrefetchingState(to:)and/or reusing theload(request,context:)helper, but this is optional given the single-object fetch.Sources/StreamChat/Database/DTOs/ChannelReadDTO.swift (1)
141-175: Unread-by-timestamp resolution looks correct; consider unifying client-config handlingThe refactor cleanly supports both
.messageIdand.messageTimestamp, and the timestamp branch correctly reusesMessageDTO.loadMessage(beforeOrEqual:cid:deletedMessagesVisibility:shouldShowShadowedMessages:context:)so unread is anchored to the latest visible message at/before the timestamp.One small consistency tweak to consider: for the timestamp branch you currently fall back to
.alwaysVisibleandfalsewhenchatClientConfigisnil, whereas the existingloadMessage(before:id:cid:)helper guards on a non-nil config and returnsnil. Aligning these behaviors (e.g., early-return when config is missing) would make the criteria paths behave more uniformly.Tests/StreamChatTests/Repositories/ChannelRepository_Tests.swift (1)
112-137: RepositorymarkUnreadtests match new payload shape; consider adding a timestamp-criteria caseThe updated tests correctly assert that
ChannelRepository.markUnreadpasses.messageId(messageId)viaMarkUnreadPayload(criteria:userId:)and hits the expected.markUnread(cid:payload:)endpoint.Since the feature now also supports
.messageTimestamp, it would be useful to add a companion test that callsmarkUnread(from: .messageTimestamp(...))and asserts the encoded payload’scriteriais.messageTimestamp(...), to lock in both branches of the new enum.Also applies to: 139-160
Tests/StreamChatTests/Workers/ChannelUpdater_Tests.swift (1)
1727-1739: UpdatermarkUnreadtests validate id-based criteria; add a timestamp variant for full coverageThese tests correctly ensure that
ChannelUpdater.markUnreadforwards.messageId(messageId)andlastReadMessageIdtoChannelRepository_Mockand that success/error results are propagated.To fully exercise the new
MarkUnreadCriteriasurface, consider an additional test that callsmarkUnread(from: .messageTimestamp(timestamp))and assertschannelRepository.markUnreadCriteriais.messageTimestamp(timestamp)and the completion semantics remain identical. This would complement the existing id-based checks.Also applies to: 1741-1768
📜 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.
📒 Files selected for processing (25)
CHANGELOG.md(1 hunks)DemoApp/StreamChat/Components/DemoChatChannelListRouter.swift(1 hunks)Sources/StreamChat/APIClient/Endpoints/ChannelEndpoints.swift(1 hunks)Sources/StreamChat/APIClient/Endpoints/Payloads/MarkUnreadPayload.swift(1 hunks)Sources/StreamChat/Controllers/ChannelController/ChannelController.swift(1 hunks)Sources/StreamChat/Database/DTOs/ChannelReadDTO.swift(1 hunks)Sources/StreamChat/Database/DTOs/MessageDTO.swift(1 hunks)Sources/StreamChat/Database/DatabaseSession.swift(1 hunks)Sources/StreamChat/Repositories/ChannelRepository.swift(2 hunks)Sources/StreamChat/Repositories/MessageRepository.swift(2 hunks)Sources/StreamChat/StateLayer/Chat.swift(1 hunks)Sources/StreamChat/WebSocketClient/EventMiddlewares/ChannelReadUpdaterMiddleware.swift(1 hunks)Sources/StreamChat/Workers/ChannelUpdater.swift(1 hunks)Sources/StreamChat/Workers/ReadStateHandler.swift(3 hunks)Sources/StreamChatUI/ChatChannel/ChatChannelVC.swift(1 hunks)StreamChat.xcodeproj/project.pbxproj(5 hunks)TestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swift(1 hunks)TestTools/StreamChatTestTools/Mocks/StreamChat/Repositories/ChannelRepository_Mock.swift(2 hunks)TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift(3 hunks)Tests/StreamChatTests/APIClient/Endpoints/ChannelEndpoints_Tests.swift(2 hunks)Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swift(7 hunks)Tests/StreamChatTests/Database/DTOs/ChannelReadDTO_Tests.swift(6 hunks)Tests/StreamChatTests/Repositories/ChannelRepository_Tests.swift(3 hunks)Tests/StreamChatTests/Repositories/MessageRepository_Tests.swift(1 hunks)Tests/StreamChatTests/Workers/ChannelUpdater_Tests.swift(3 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.swift
📄 CodeRabbit inference engine (AGENTS.md)
**/*.swift: Write Swift code compatible with iOS deployment targets specified in Package.swift and podspec files; do not lower deployment targets without approval
Use SwiftLint with strict mode and respect .swiftlint.yml rules; justify and scope any exceptions rather than suppressing rules broadly
Run SwiftFormat for code formatting and respect repository-specific style conventions
Never commit API keys or customer data; use obvious placeholders (e.g., YOUR_STREAM_KEY) in example code
Follow the project's zero-warnings policy: fix new warnings and avoid introducing any
Files:
Tests/StreamChatTests/Database/DTOs/ChannelReadDTO_Tests.swiftSources/StreamChat/Workers/ChannelUpdater.swiftTests/StreamChatTests/Repositories/MessageRepository_Tests.swiftSources/StreamChat/WebSocketClient/EventMiddlewares/ChannelReadUpdaterMiddleware.swiftSources/StreamChat/Repositories/ChannelRepository.swiftSources/StreamChat/APIClient/Endpoints/ChannelEndpoints.swiftSources/StreamChat/Database/DTOs/ChannelReadDTO.swiftSources/StreamChat/Controllers/ChannelController/ChannelController.swiftSources/StreamChat/APIClient/Endpoints/Payloads/MarkUnreadPayload.swiftSources/StreamChat/Database/DatabaseSession.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swiftTests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swiftSources/StreamChatUI/ChatChannel/ChatChannelVC.swiftTests/StreamChatTests/APIClient/Endpoints/ChannelEndpoints_Tests.swiftSources/StreamChat/Repositories/MessageRepository.swiftSources/StreamChat/Database/DTOs/MessageDTO.swiftSources/StreamChat/Workers/ReadStateHandler.swiftTests/StreamChatTests/Workers/ChannelUpdater_Tests.swiftTests/StreamChatTests/Repositories/ChannelRepository_Tests.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Repositories/ChannelRepository_Mock.swiftSources/StreamChat/StateLayer/Chat.swiftDemoApp/StreamChat/Components/DemoChatChannelListRouter.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift
Tests/**/*.swift
📄 CodeRabbit inference engine (AGENTS.md)
Add or extend tests in the matching module's Tests/ folder for changes to StreamChat or StreamChatUI code, covering core models, API surfaces, and view controller behaviors; use fakes/mocks from test helpers provided by the repo
Files:
Tests/StreamChatTests/Database/DTOs/ChannelReadDTO_Tests.swiftTests/StreamChatTests/Repositories/MessageRepository_Tests.swiftTests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swiftTests/StreamChatTests/APIClient/Endpoints/ChannelEndpoints_Tests.swiftTests/StreamChatTests/Workers/ChannelUpdater_Tests.swiftTests/StreamChatTests/Repositories/ChannelRepository_Tests.swift
Sources/StreamChat/**/*.swift
📄 CodeRabbit inference engine (AGENTS.md)
Ensure public API changes in StreamChat and StreamChatUI include inline documentation and migration notes
Files:
Sources/StreamChat/Workers/ChannelUpdater.swiftSources/StreamChat/WebSocketClient/EventMiddlewares/ChannelReadUpdaterMiddleware.swiftSources/StreamChat/Repositories/ChannelRepository.swiftSources/StreamChat/APIClient/Endpoints/ChannelEndpoints.swiftSources/StreamChat/Database/DTOs/ChannelReadDTO.swiftSources/StreamChat/Controllers/ChannelController/ChannelController.swiftSources/StreamChat/APIClient/Endpoints/Payloads/MarkUnreadPayload.swiftSources/StreamChat/Database/DatabaseSession.swiftSources/StreamChat/Repositories/MessageRepository.swiftSources/StreamChat/Database/DTOs/MessageDTO.swiftSources/StreamChat/Workers/ReadStateHandler.swiftSources/StreamChat/StateLayer/Chat.swift
CHANGELOG*
📄 CodeRabbit inference engine (AGENTS.md)
Update CHANGELOG entries for user-visible SDK changes in StreamChat and StreamChatUI
Files:
CHANGELOG.md
Sources/StreamChatUI/**/*.swift
📄 CodeRabbit inference engine (AGENTS.md)
Ensure public API changes in StreamChatUI include inline documentation and migration notes
Files:
Sources/StreamChatUI/ChatChannel/ChatChannelVC.swift
🧠 Learnings (7)
📓 Common learnings
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Applies to CHANGELOG* : Update CHANGELOG entries for user-visible SDK changes in StreamChat and StreamChatUI
📚 Learning: 2025-11-25T12:17:18.459Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Applies to Tests/**/*.swift : Add or extend tests in the matching module's Tests/ folder for changes to StreamChat or StreamChatUI code, covering core models, API surfaces, and view controller behaviors; use fakes/mocks from test helpers provided by the repo
Applied to files:
Tests/StreamChatTests/Database/DTOs/ChannelReadDTO_Tests.swiftTests/StreamChatTests/Repositories/MessageRepository_Tests.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swiftStreamChat.xcodeproj/project.pbxprojTests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swiftTests/StreamChatTests/APIClient/Endpoints/ChannelEndpoints_Tests.swiftTests/StreamChatTests/Workers/ChannelUpdater_Tests.swiftTests/StreamChatTests/Repositories/ChannelRepository_Tests.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Repositories/ChannelRepository_Mock.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift
📚 Learning: 2025-11-25T12:17:18.459Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Applies to Sources/StreamChat/**/*.swift : Ensure public API changes in StreamChat and StreamChatUI include inline documentation and migration notes
Applied to files:
Tests/StreamChatTests/Database/DTOs/ChannelReadDTO_Tests.swiftSources/StreamChat/Workers/ChannelUpdater.swiftTests/StreamChatTests/Repositories/MessageRepository_Tests.swiftSources/StreamChat/WebSocketClient/EventMiddlewares/ChannelReadUpdaterMiddleware.swiftSources/StreamChat/Repositories/ChannelRepository.swiftSources/StreamChat/Database/DTOs/ChannelReadDTO.swiftCHANGELOG.mdSources/StreamChat/Controllers/ChannelController/ChannelController.swiftSources/StreamChat/Database/DatabaseSession.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swiftStreamChat.xcodeproj/project.pbxprojTests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swiftSources/StreamChatUI/ChatChannel/ChatChannelVC.swiftTests/StreamChatTests/APIClient/Endpoints/ChannelEndpoints_Tests.swiftSources/StreamChat/Repositories/MessageRepository.swiftTests/StreamChatTests/Workers/ChannelUpdater_Tests.swiftTests/StreamChatTests/Repositories/ChannelRepository_Tests.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Repositories/ChannelRepository_Mock.swiftSources/StreamChat/StateLayer/Chat.swiftDemoApp/StreamChat/Components/DemoChatChannelListRouter.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift
📚 Learning: 2025-11-25T12:17:18.459Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Maintain high test coverage when changing code in the Stream iOS Chat SDK
Applied to files:
Tests/StreamChatTests/Database/DTOs/ChannelReadDTO_Tests.swiftTests/StreamChatTests/Repositories/MessageRepository_Tests.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swiftTests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swiftTests/StreamChatTests/APIClient/Endpoints/ChannelEndpoints_Tests.swiftTests/StreamChatTests/Workers/ChannelUpdater_Tests.swiftTests/StreamChatTests/Repositories/ChannelRepository_Tests.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Repositories/ChannelRepository_Mock.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift
📚 Learning: 2025-11-25T12:17:18.459Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Applies to Sources/StreamChatUI/**/*.swift : Ensure public API changes in StreamChatUI include inline documentation and migration notes
Applied to files:
Tests/StreamChatTests/Database/DTOs/ChannelReadDTO_Tests.swiftSources/StreamChat/Workers/ChannelUpdater.swiftTests/StreamChatTests/Repositories/MessageRepository_Tests.swiftSources/StreamChat/WebSocketClient/EventMiddlewares/ChannelReadUpdaterMiddleware.swiftSources/StreamChat/Repositories/ChannelRepository.swiftSources/StreamChat/Database/DTOs/ChannelReadDTO.swiftCHANGELOG.mdSources/StreamChat/Controllers/ChannelController/ChannelController.swiftSources/StreamChat/Database/DatabaseSession.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swiftStreamChat.xcodeproj/project.pbxprojTests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swiftSources/StreamChatUI/ChatChannel/ChatChannelVC.swiftTests/StreamChatTests/APIClient/Endpoints/ChannelEndpoints_Tests.swiftSources/StreamChat/Repositories/MessageRepository.swiftTests/StreamChatTests/Workers/ChannelUpdater_Tests.swiftTests/StreamChatTests/Repositories/ChannelRepository_Tests.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Repositories/ChannelRepository_Mock.swiftSources/StreamChat/StateLayer/Chat.swiftDemoApp/StreamChat/Components/DemoChatChannelListRouter.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift
📚 Learning: 2025-11-25T12:17:18.459Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Applies to CHANGELOG* : Update CHANGELOG entries for user-visible SDK changes in StreamChat and StreamChatUI
Applied to files:
Sources/StreamChat/Workers/ChannelUpdater.swiftSources/StreamChat/WebSocketClient/EventMiddlewares/ChannelReadUpdaterMiddleware.swiftSources/StreamChat/Repositories/ChannelRepository.swiftSources/StreamChat/Database/DTOs/ChannelReadDTO.swiftCHANGELOG.mdSources/StreamChat/Controllers/ChannelController/ChannelController.swiftSources/StreamChat/Database/DatabaseSession.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swiftTests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swiftSources/StreamChatUI/ChatChannel/ChatChannelVC.swiftTests/StreamChatTests/Workers/ChannelUpdater_Tests.swiftTests/StreamChatTests/Repositories/ChannelRepository_Tests.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Repositories/ChannelRepository_Mock.swiftSources/StreamChat/StateLayer/Chat.swiftDemoApp/StreamChat/Components/DemoChatChannelListRouter.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift
📚 Learning: 2025-11-25T12:17:18.459Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Prioritize backwards compatibility and API stability when changing code in the Stream iOS Chat SDK
Applied to files:
CHANGELOG.mdTestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swiftTests/StreamChatTests/Repositories/ChannelRepository_Tests.swiftTestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift
🧬 Code graph analysis (20)
Tests/StreamChatTests/Database/DTOs/ChannelReadDTO_Tests.swift (2)
Sources/StreamChat/Database/DTOs/ChannelReadDTO.swift (2)
markChannelAsUnread(138-175)markChannelAsUnread(177-181)TestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swift (5)
markChannelAsUnread(358-361)markChannelAsUnread(363-372)message(291-293)user(100-102)channel(403-405)
Sources/StreamChat/Workers/ChannelUpdater.swift (6)
Sources/StreamChat/APIClient/Endpoints/ChannelEndpoints.swift (1)
markUnread(272-280)Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (3)
markUnread(1308-1337)markUnread(1348-1377)lastReadMessageId(2264-2276)Sources/StreamChat/Repositories/ChannelRepository.swift (1)
markUnread(69-103)Sources/StreamChat/Workers/ReadStateHandler.swift (2)
markUnread(48-80)markUnread(82-94)TestTools/StreamChatTestTools/Mocks/StreamChat/Repositories/ChannelRepository_Mock.swift (1)
markUnread(39-49)TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift (1)
markUnread(515-522)
Tests/StreamChatTests/Repositories/MessageRepository_Tests.swift (1)
Sources/StreamChat/Repositories/MessageRepository.swift (2)
getMessage(304-338)getMessage(341-376)
Sources/StreamChat/WebSocketClient/EventMiddlewares/ChannelReadUpdaterMiddleware.swift (1)
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (1)
messageId(2152-2154)
Sources/StreamChat/Repositories/ChannelRepository.swift (6)
Sources/StreamChat/APIClient/Endpoints/ChannelEndpoints.swift (1)
markUnread(272-280)Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (3)
markUnread(1308-1337)markUnread(1348-1377)lastReadMessageId(2264-2276)Sources/StreamChat/StateLayer/Chat.swift (2)
markUnread(937-940)markUnread(947-950)Sources/StreamChat/Workers/ChannelUpdater.swift (1)
markUnread(570-584)Sources/StreamChat/Workers/ReadStateHandler.swift (2)
markUnread(48-80)markUnread(82-94)TestTools/StreamChatTestTools/Mocks/StreamChat/Repositories/ChannelRepository_Mock.swift (1)
markUnread(39-49)
Sources/StreamChat/Database/DTOs/ChannelReadDTO.swift (3)
TestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swift (2)
loadChannelRead(374-376)message(291-293)Sources/StreamChat/Database/DTOs/MessageDTO.swift (5)
message(457-463)message(1369-1369)loadMessage(589-608)loadMessage(610-625)loadMessage(1587-1599)Sources/StreamChat/Database/DataStore.swift (1)
message(68-70)
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (5)
Sources/StreamChat/APIClient/Endpoints/ChannelEndpoints.swift (1)
markUnread(272-280)Sources/StreamChat/Repositories/ChannelRepository.swift (1)
markUnread(69-103)Sources/StreamChat/StateLayer/Chat.swift (2)
markUnread(937-940)markUnread(947-950)Sources/StreamChat/Workers/ChannelUpdater.swift (1)
markUnread(570-584)Sources/StreamChat/Workers/ReadStateHandler.swift (2)
markUnread(48-80)markUnread(82-94)
Sources/StreamChat/APIClient/Endpoints/Payloads/MarkUnreadPayload.swift (3)
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (1)
messageId(2152-2154)TestTools/StreamChatTestMockServer/MockServer/MockServerAttributes.swift (1)
userId(200-202)Sources/StreamChat/APIClient/Endpoints/ChannelEndpoints.swift (1)
encode(416-420)
Sources/StreamChat/Database/DatabaseSession.swift (2)
Sources/StreamChat/Database/DTOs/ChannelReadDTO.swift (2)
markChannelAsUnread(138-175)markChannelAsUnread(177-181)Sources/StreamChat/WebSocketClient/EventMiddlewares/ChannelReadUpdaterMiddleware.swift (1)
markChannelAsUnread(114-131)
TestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swift (2)
Sources/StreamChat/Database/DTOs/ChannelReadDTO.swift (2)
markChannelAsUnread(138-175)markChannelAsUnread(177-181)Sources/StreamChat/WebSocketClient/EventMiddlewares/ChannelReadUpdaterMiddleware.swift (1)
markChannelAsUnread(114-131)
Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swift (4)
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (3)
markUnread(1308-1337)markUnread(1348-1377)messageId(2152-2154)Sources/StreamChat/Repositories/ChannelRepository.swift (1)
markUnread(69-103)Sources/StreamChat/StateLayer/Chat.swift (2)
markUnread(937-940)markUnread(947-950)Sources/StreamChat/Workers/ReadStateHandler.swift (2)
markUnread(48-80)markUnread(82-94)
Sources/StreamChatUI/ChatChannel/ChatChannelVC.swift (2)
Sources/StreamChat/WebSocketClient/Events/EventPayload.swift (1)
event(208-210)Sources/StreamChat/WebSocketClient/Events/EventType.swift (1)
event(186-267)
Sources/StreamChat/Repositories/MessageRepository.swift (1)
Sources/StreamChat/Database/DTOs/MessageDTO.swift (3)
loadMessage(589-608)loadMessage(610-625)loadMessage(1587-1599)
Sources/StreamChat/Database/DTOs/MessageDTO.swift (1)
Sources/StreamChat/Utils/Database/NSManagedObject+Extensions.swift (1)
fetch(181-199)
Sources/StreamChat/Workers/ReadStateHandler.swift (3)
Sources/StreamChat/Repositories/MessageRepository.swift (2)
getMessage(304-338)getMessage(341-376)Sources/StreamChat/StateLayer/Chat.swift (2)
markUnread(937-940)markUnread(947-950)TestTools/StreamChatTestTools/Mocks/StreamChat/Repositories/ChannelRepository_Mock.swift (1)
markUnread(39-49)
Tests/StreamChatTests/Repositories/ChannelRepository_Tests.swift (5)
Sources/StreamChat/APIClient/Endpoints/ChannelEndpoints.swift (1)
markUnread(272-280)Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (4)
markUnread(1308-1337)markUnread(1348-1377)messageId(2152-2154)lastReadMessageId(2264-2276)Sources/StreamChat/Repositories/ChannelRepository.swift (1)
markUnread(69-103)Sources/StreamChat/StateLayer/Chat.swift (2)
markUnread(937-940)markUnread(947-950)Sources/StreamChat/Workers/ChannelUpdater.swift (1)
markUnread(570-584)
TestTools/StreamChatTestTools/Mocks/StreamChat/Repositories/ChannelRepository_Mock.swift (4)
Sources/StreamChat/APIClient/Endpoints/ChannelEndpoints.swift (1)
markUnread(272-280)Sources/StreamChat/Repositories/ChannelRepository.swift (1)
markUnread(69-103)Sources/StreamChat/Workers/ChannelUpdater.swift (1)
markUnread(570-584)TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift (1)
markUnread(515-522)
Sources/StreamChat/StateLayer/Chat.swift (2)
Sources/StreamChat/Controllers/ChannelController/ChannelController.swift (3)
markUnread(1308-1337)markUnread(1348-1377)messageId(2152-2154)Sources/StreamChat/Workers/ReadStateHandler.swift (2)
markUnread(48-80)markUnread(82-94)
DemoApp/StreamChat/Components/DemoChatChannelListRouter.swift (3)
Sources/StreamChat/Repositories/ChannelRepository.swift (1)
markUnread(69-103)Sources/StreamChat/StateLayer/Chat.swift (2)
markUnread(937-940)markUnread(947-950)Sources/StreamChat/Workers/ChannelUpdater.swift (1)
markUnread(570-584)
TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift (4)
Sources/StreamChat/Repositories/ChannelRepository.swift (1)
markUnread(69-103)Sources/StreamChat/StateLayer/Chat.swift (2)
markUnread(937-940)markUnread(947-950)Sources/StreamChat/Workers/ChannelUpdater.swift (1)
markUnread(570-584)TestTools/StreamChatTestTools/Mocks/StreamChat/Repositories/ChannelRepository_Mock.swift (1)
markUnread(39-49)
⏰ 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). (1)
- GitHub Check: Metrics
🔇 Additional comments (20)
StreamChat.xcodeproj/project.pbxproj (5)
321-322: PBXBuildFile entries forMarkUnreadPayload.swiftare consistent across targetsBoth build files (
4FC7B3F0and4FC7B3F1) reference the same fileRef and are each consumed in separate Sources lists later on, which matches the usual multi-target wiring pattern. No structural issues here.
3365-3365: PBXFileReference forMarkUnreadPayload.swiftis correctly definedThe new file reference uses the expected
sourcecode.swifttype and path, consistent with neighboring Swift files. This will let Xcode resolve the file cleanly.
5979-5983: Payloads group inclusion looks correct
MarkUnreadPayload.swiftis added under the existingPayloadsPBXGroup alongside other payload DTOs, which keeps project structure coherent.
11918-11918: Main target Sources phase wiring is correct
4FC7B3F1 /* MarkUnreadPayload.swift in Sources */is added to a Sources list with other core SDK files, so the main target will compile the new payload as expected.
13094-13094: Additional target Sources phase wiring is correct
4FC7B3F0 /* MarkUnreadPayload.swift in Sources */appears in another Sources list (likely a second target such as tests or a demo), which is the standard pattern for sharing the same file across targets.Tests/StreamChatTests/Repositories/MessageRepository_Tests.swift (2)
422-448: LGTM! Test updated correctly for new API.The existing test has been properly updated to use the new
MarkUnreadCriteria.messageIdwrapper while maintaining the same test logic.
450-478: Excellent test coverage for timestamp-based marking.The new test properly validates the timestamp-based unread marking functionality with a clear scenario: using a timestamp between two messages to retrieve the correct message.
TestTools/StreamChatTestTools/Mocks/StreamChat/Database/DatabaseSession_Mock.swift (1)
363-372: LGTM! Mock updated correctly.The mock signature has been properly updated to use
MarkUnreadCriteriaand correctly forwards the parameter to the underlying session.Sources/StreamChat/WebSocketClient/EventMiddlewares/ChannelReadUpdaterMiddleware.swift (1)
114-131: LGTM! Correctly wraps messageId in criteria enum.The call to
markChannelAsUnreadhas been properly updated to wrap themessageIdparameter in the newMarkUnreadCriteria.messageIdcase.DemoApp/StreamChat/Components/DemoChatChannelListRouter.swift (1)
466-483: LGTM! Demo action properly implements timestamp-based marking.The new demo action correctly prompts for a day offset, computes the date, and calls the new
markUnread(from: Date)API with appropriate error handling.Sources/StreamChatUI/ChatChannel/ChatChannelVC.swift (1)
601-604: LGTM! Fixes unread UI update for timestamp-based marking.This correctly handles
NotificationMarkUnreadEventby updating all unread-related UI components when the channel is marked unread via timestamp. This ensures the unread message separator and other UI elements are refreshed, addressing the issue mentioned in the PR description.TestTools/StreamChatTestTools/Mocks/StreamChat/Repositories/ChannelRepository_Mock.swift (1)
17-49: LGTM! Mock repository updated correctly.The mock has been properly updated to use
MarkUnreadCriteriaand stores the criteria for verification in tests.Sources/StreamChat/Workers/ReadStateHandler.swift (1)
48-94: LGTM! ReadStateHandler consistently updated.Both the completion-based and async variants of
markUnreadhave been properly updated to accept and forwardMarkUnreadCriteriathrough the call chain. The implementation correctly propagates the criteria to bothmessageRepositoryandchannelUpdater.Sources/StreamChat/Workers/ChannelUpdater.swift (1)
567-583: Updater–repository wiring for MarkUnreadCriteria looks goodThe
ChannelUpdater.markUnreadsignature and doc are correctly updated to takeMarkUnreadCriteriaand simply forward it toChannelRepository.markUnread, preserving behavior while enabling timestamp criteria.Sources/StreamChat/Repositories/ChannelRepository.swift (1)
61-103: Repository markUnread refactor is consistent with new payload/criteria typesThe repository correctly:
- accepts
from unreadCriteria: MarkUnreadCriteria,- builds the
MarkUnreadPayloadfor the endpoint, and- passes the same criteria into
session.markChannelAsUnread.Error propagation and the
ChannelNotCreatedYetfallback remain intact.Tests/StreamChatTests/Database/DTOs/ChannelReadDTO_Tests.swift (1)
316-424: Nice symmetry tests for partial unread when read/message are missingThe updated tests:
- switch existing partial-unread cases to
from: .messageId(...), and- add
*_messageTimestampvariants for “no read” and “no message” scenarios,which together validate that
markChannelAsUnreadbecomes a no-op (while still going through a single write) when either the read row or the target message cannot be resolved, for both criteria types.Sources/StreamChat/APIClient/Endpoints/ChannelEndpoints.swift (1)
272-279: Endpoint.markUnread now correctly uses the typed MarkUnreadPayloadSwitching
markUnreadto accept aMarkUnreadPayloadand using it as the request body keeps the HTTP contract (path/method) intact while aligning the endpoint with the new criteria/payload model and improving type safety at call sites.Tests/StreamChatTests/APIClient/Endpoints/ChannelEndpoints_Tests.swift (1)
452-494: Endpoint tests cover both message-id and timestamp mark-unread variants correctlyThe new
markUnreadendpoint tests look good: they useMarkUnreadPayloadwith.messageIdand.messageTimestampcriteria, assert fullAnyEndpointequality (path, method, requiresConnectionId, body), and confirm the expectedchannels/{type}/{id}/unreadpath. This gives solid coverage of the new payload-based API.Sources/StreamChat/Database/DatabaseSession.swift (1)
399-408: Channel read protocol update is consistent with criteria-based APISwitching
fromtoMarkUnreadCriteriaand updating the docstring to mention id or timestamp keeps the protocol aligned with the new unread model; no issues here.TestTools/StreamChatTestTools/Mocks/StreamChat/Workers/ChannelUpdater_Mock.swift (1)
103-109: Mock wiring for criteria-basedmarkUnreadlooks consistentTracking
markUnread_criteriainstead of a bare message id, resetting it incleanUp(), and assigning it in the overriddenmarkUnreadmatches the new production API and keeps the mock introspection aligned with test expectations.Also applies to: 250-256, 515-522
| if let event = event as? NotificationMarkUnreadEvent, let channel = channelController.channel, event.cid == channelController.cid, !messages.isEmpty { | ||
| updateAllUnreadMessagesRelatedComponents(channel: channel) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The existing mark unread message action is implemented in ChatChannelVC and it explicitly calls this method in the completion handler. Since I added demo app debug command then this did not get called and unread separator did not appear.
Should I clean up the explicit call? At the moment I did not touch it.
case is MarkUnreadActionItem:
dismiss(animated: true) { [weak self] in
self?.channelController.markUnread(from: message.id) { result in
if case let .success(channel) = result {
self?.updateAllUnreadMessagesRelatedComponents(channel: channel)
}
}
}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's ok imo, we have one sample implementation (customers can change/decide which one to use).
Generated by 🚫 Danger |
SDK Performance
|
martinmitrevski
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, but the code coverage can be improved
There was a problem hiding this 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 (2)
Tests/StreamChatTests/StateLayer/Chat_Tests.swift (1)
1311-1347: VerifylastMessageselection in new timestamp-based markUnread testThe new test correctly exercises
chat.markUnread(from: Date)and mirrors the ID-based variant, butlastMessageis initialized frommessages.first(same asfirstMessage), which is likely a copy-paste/semantic slip—messages.lastwould better represent the latest message and aligns with howlastMessageAtis set in themarkReadtest above. If this is unintentional, consider switching tomessages.lasthere (and in the existing ID-basedmarkUnreadtest) so the setup more realistically models "all messages read, then mark unread from the first message."Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swift (1)
4330-4408: Timestamp-based markUnread tests are good; consider adding one assertion on the criteria wiringThe new
markUnread(from: Date)tests nicely mirror the messageId variants for:
- channel not created
- read events disabled
- marking-as-read in progress
- no current user id
They validate surface behavior (error vs no error), but they don’t yet assert that the controller passes
MarkUnreadCriteria.messageTimestamp(...)through toChannelUpdater_Mock, or thatlastReadMessageIdis derived consistently with the messageId-based path.If you want tighter coverage of the new timestamp path, consider adding a positive test similar to
test_markUnread_whenIsNotMarkingAsRead_andCurrentUserIdIsPresent_whenThereAreOtherMessages_whenUpdaterSucceeds, but using a message’screatedAtas the timestamp and asserting:
updater.markUnread_criteria == .messageTimestamp(timestamp)updater.markUnread_lastReadMessageIdmatches the message immediately preceding that timestampThis would exercise the new enum case end-to-end without changing production code.
📜 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.
📒 Files selected for processing (2)
Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swift(7 hunks)Tests/StreamChatTests/StateLayer/Chat_Tests.swift(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.swift
📄 CodeRabbit inference engine (AGENTS.md)
**/*.swift: Write Swift code compatible with iOS deployment targets specified in Package.swift and podspec files; do not lower deployment targets without approval
Use SwiftLint with strict mode and respect .swiftlint.yml rules; justify and scope any exceptions rather than suppressing rules broadly
Run SwiftFormat for code formatting and respect repository-specific style conventions
Never commit API keys or customer data; use obvious placeholders (e.g., YOUR_STREAM_KEY) in example code
Follow the project's zero-warnings policy: fix new warnings and avoid introducing any
Files:
Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swiftTests/StreamChatTests/StateLayer/Chat_Tests.swift
Tests/**/*.swift
📄 CodeRabbit inference engine (AGENTS.md)
Add or extend tests in the matching module's Tests/ folder for changes to StreamChat or StreamChatUI code, covering core models, API surfaces, and view controller behaviors; use fakes/mocks from test helpers provided by the repo
Files:
Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swiftTests/StreamChatTests/StateLayer/Chat_Tests.swift
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Applies to CHANGELOG* : Update CHANGELOG entries for user-visible SDK changes in StreamChat and StreamChatUI
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Applies to Sources/StreamChat/**/*.swift : Ensure public API changes in StreamChat and StreamChatUI include inline documentation and migration notes
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Applies to Sources/StreamChatUI/**/*.swift : Ensure public API changes in StreamChatUI include inline documentation and migration notes
📚 Learning: 2025-11-25T12:17:18.459Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Applies to Tests/**/*.swift : Add or extend tests in the matching module's Tests/ folder for changes to StreamChat or StreamChatUI code, covering core models, API surfaces, and view controller behaviors; use fakes/mocks from test helpers provided by the repo
Applied to files:
Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swiftTests/StreamChatTests/StateLayer/Chat_Tests.swift
📚 Learning: 2025-11-25T12:17:18.459Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Applies to Sources/StreamChatUI/**/*.swift : Ensure public API changes in StreamChatUI include inline documentation and migration notes
Applied to files:
Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swiftTests/StreamChatTests/StateLayer/Chat_Tests.swift
📚 Learning: 2025-11-25T12:17:18.459Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Applies to Sources/StreamChat/**/*.swift : Ensure public API changes in StreamChat and StreamChatUI include inline documentation and migration notes
Applied to files:
Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swiftTests/StreamChatTests/StateLayer/Chat_Tests.swift
📚 Learning: 2025-11-25T12:17:18.459Z
Learnt from: CR
Repo: GetStream/stream-chat-swift PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T12:17:18.459Z
Learning: Maintain high test coverage when changing code in the Stream iOS Chat SDK
Applied to files:
Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swiftTests/StreamChatTests/StateLayer/Chat_Tests.swift
🔇 Additional comments (1)
Tests/StreamChatTests/Controllers/ChannelController/ChannelController_Tests.swift (1)
4135-4328: Mark-unread-by-messageId tests correctly updated to new API and criteria wiringThe updated tests now call
controller.markUnread(from: MessageId.unique)and validateupdater.markUnread_criteria == .messageId(messageId)in the success paths. This keeps existing behavior intact while exercising the newMarkUnreadCriteriaplumbing, and the scenarios (channel missing, feature disabled, current user present/absent, updater error/success) remain well covered. No issues here.
Public Interface public class Chat
- @discardableResult public func reply(to parentMessageId: MessageId,text: String,showReplyInChannel: Bool = false,attachments: [AnyAttachmentPayload] = [],quote quotedMessageId: MessageId? = nil,mentions: [UserId] = [],pinning: MessagePinning? = nil,extraData: [String: RawJSON] = [:],silent: Bool = false,skipPushNotification: Bool = false,skipEnrichURL: Bool = false,messageId: MessageId? = nil)async throws -> ChatMessage
+ public func markUnread(from timestamp: Date)async throws
- @discardableResult public func loadReplies(for messageId: MessageId,pagination: MessagesPagination)async throws -> [ChatMessage]
+ @discardableResult public func reply(to parentMessageId: MessageId,text: String,showReplyInChannel: Bool = false,attachments: [AnyAttachmentPayload] = [],quote quotedMessageId: MessageId? = nil,mentions: [UserId] = [],pinning: MessagePinning? = nil,extraData: [String: RawJSON] = [:],silent: Bool = false,skipPushNotification: Bool = false,skipEnrichURL: Bool = false,messageId: MessageId? = nil)async throws -> ChatMessage
- public func loadReplies(before replyId: MessageId?,for parentMessageId: MessageId,limit: Int? = nil)async throws
+ @discardableResult public func loadReplies(for messageId: MessageId,pagination: MessagesPagination)async throws -> [ChatMessage]
- public func loadReplies(after replyId: MessageId?,for parentMessageId: MessageId,limit: Int? = nil)async throws
+ public func loadReplies(before replyId: MessageId?,for parentMessageId: MessageId,limit: Int? = nil)async throws
- public func loadReplies(around replyId: MessageId,for parentMessageId: MessageId,limit: Int? = nil)async throws
+ public func loadReplies(after replyId: MessageId?,for parentMessageId: MessageId,limit: Int? = nil)async throws
- public func loadOlderReplies(for parentMessageId: MessageId,limit: Int? = nil)async throws
+ public func loadReplies(around replyId: MessageId,for parentMessageId: MessageId,limit: Int? = nil)async throws
- public func loadNewerReplies(for parentMessageId: MessageId,limit: Int? = nil)async throws
+ public func loadOlderReplies(for parentMessageId: MessageId,limit: Int? = nil)async throws
- @discardableResult public func translateMessage(_ messageId: MessageId,to language: TranslationLanguage)async throws -> ChatMessage
+ public func loadNewerReplies(for parentMessageId: MessageId,limit: Int? = nil)async throws
- public func mute(expiration: Int? = nil)async throws
+ @discardableResult public func translateMessage(_ messageId: MessageId,to language: TranslationLanguage)async throws -> ChatMessage
- public func unmute()async throws
+ public func mute(expiration: Int? = nil)async throws
- public func hide(clearHistory: Bool = false)async throws
+ public func unmute()async throws
- public func show()async throws
+ public func hide(clearHistory: Bool = false)async throws
- public func pin(scope: ChannelPinningScope = .me)async throws
+ public func show()async throws
- public func unpin(scope: ChannelPinningScope = .me)async throws
+ public func pin(scope: ChannelPinningScope = .me)async throws
- public func subscribe(toEvent event: E.Type,handler: @escaping (E) -> Void)-> AnyCancellable
+ public func unpin(scope: ChannelPinningScope = .me)async throws
- public func subscribe(_ handler: @escaping (Event) -> Void)-> AnyCancellable
+ public func subscribe(toEvent event: E.Type,handler: @escaping (E) -> Void)-> AnyCancellable
- public func sendEvent(_ payload: EventPayload)async throws
+ public func subscribe(_ handler: @escaping (Event) -> Void)-> AnyCancellable
- public func enableSlowMode(cooldownDuration: Int)async throws
+ public func sendEvent(_ payload: EventPayload)async throws
- public func disableSlowMode()async throws
+ public func enableSlowMode(cooldownDuration: Int)async throws
- public func truncate(systemMessage: String? = nil,hardDelete: Bool = true,skipPush: Bool = false)async throws
+ public func disableSlowMode()async throws
- public func keystroke(parentMessageId: MessageId? = nil)async throws
+ public func truncate(systemMessage: String? = nil,hardDelete: Bool = true,skipPush: Bool = false)async throws
- public func stopTyping(parentMessageId: MessageId? = nil)async throws
+ public func keystroke(parentMessageId: MessageId? = nil)async throws
- public func update(name: String?,imageURL: URL?,team: String?,members: Set<UserId> = [],invites: Set<UserId> = [],extraData: [String: RawJSON] = [:])async throws
+ public func stopTyping(parentMessageId: MessageId? = nil)async throws
- public func updatePartial(name: String? = nil,imageURL: URL? = nil,team: String? = nil,members: [UserId] = [],invites: [UserId] = [],extraData: [String: RawJSON] = [:],unsetProperties: [String] = [])async throws
+ public func update(name: String?,imageURL: URL?,team: String?,members: Set<UserId> = [],invites: Set<UserId> = [],extraData: [String: RawJSON] = [:])async throws
- public func deleteFile(at url: URL)async throws
+ public func updatePartial(name: String? = nil,imageURL: URL? = nil,team: String? = nil,members: [UserId] = [],invites: [UserId] = [],extraData: [String: RawJSON] = [:],unsetProperties: [String] = [])async throws
- public func deleteImage(at url: URL)async throws
+ public func deleteFile(at url: URL)async throws
- public func uploadAttachment(with localFileURL: URL,type: AttachmentType,progress: ((Double) -> Void)? = nil)async throws -> UploadedAttachment
+ public func deleteImage(at url: URL)async throws
- @discardableResult public func loadWatchers(with pagination: Pagination)async throws -> [ChatUser]
+ public func uploadAttachment(with localFileURL: URL,type: AttachmentType,progress: ((Double) -> Void)? = nil)async throws -> UploadedAttachment
- @discardableResult public func loadMoreWatchers(limit: Int? = nil)async throws -> [ChatUser]
+ @discardableResult public func loadWatchers(with pagination: Pagination)async throws -> [ChatUser]
+ @discardableResult public func loadMoreWatchers(limit: Int? = nil)async throws -> [ChatUser]
public class ChatChannelController: DataController, DelegateCallable, DataStoreProvider
- public func loadChannelReads(pagination: Pagination? = nil,completion: @escaping (Error?) -> Void)
+ public func markUnread(from timestamp: Date,completion: ((Result<ChatChannel, Error>) -> Void)? = nil)
- public func loadMoreChannelReads(limit: Int? = nil,completion: @escaping (Error?) -> Void)
+ public func loadChannelReads(pagination: Pagination? = nil,completion: @escaping (Error?) -> Void)
- public func enableSlowMode(cooldownDuration: Int,completion: ((Error?) -> Void)? = nil)
+ public func loadMoreChannelReads(limit: Int? = nil,completion: @escaping (Error?) -> Void)
- public func disableSlowMode(completion: ((Error?) -> Void)? = nil)
+ public func enableSlowMode(cooldownDuration: Int,completion: ((Error?) -> Void)? = nil)
- public func startWatching(isInRecoveryMode: Bool,completion: ((Error?) -> Void)? = nil)
+ public func disableSlowMode(completion: ((Error?) -> Void)? = nil)
- public func stopWatching(completion: ((Error?) -> Void)? = nil)
+ public func startWatching(isInRecoveryMode: Bool,completion: ((Error?) -> Void)? = nil)
- public func freezeChannel(completion: ((Error?) -> Void)? = nil)
+ public func stopWatching(completion: ((Error?) -> Void)? = nil)
- public func unfreezeChannel(completion: ((Error?) -> Void)? = nil)
+ public func freezeChannel(completion: ((Error?) -> Void)? = nil)
- public func pin(scope: ChannelPinningScope = .me,completion: ((Error?) -> Void)? = nil)
+ public func unfreezeChannel(completion: ((Error?) -> Void)? = nil)
- public func unpin(scope: ChannelPinningScope = .me,completion: ((Error?) -> Void)? = nil)
+ public func pin(scope: ChannelPinningScope = .me,completion: ((Error?) -> Void)? = nil)
- public func uploadAttachment(localFileURL: URL,type: AttachmentType,progress: ((Double) -> Void)? = nil,completion: @escaping ((Result<UploadedAttachment, Error>) -> Void))
+ public func unpin(scope: ChannelPinningScope = .me,completion: ((Error?) -> Void)? = nil)
- public func enrichUrl(_ url: URL,completion: @escaping (Result<LinkAttachmentPayload, Error>) -> Void)
+ public func uploadAttachment(localFileURL: URL,type: AttachmentType,progress: ((Double) -> Void)? = nil,completion: @escaping ((Result<UploadedAttachment, Error>) -> Void))
- public func loadPinnedMessages(pageSize: Int = .messagesPageSize,sorting: [Sorting<PinnedMessagesSortingKey>] = [],pagination: PinnedMessagesPagination? = nil,completion: @escaping (Result<[ChatMessage], Error>) -> Void)
+ public func enrichUrl(_ url: URL,completion: @escaping (Result<LinkAttachmentPayload, Error>) -> Void)
- public func currentCooldownTime()-> Int
+ public func loadPinnedMessages(pageSize: Int = .messagesPageSize,sorting: [Sorting<PinnedMessagesSortingKey>] = [],pagination: PinnedMessagesPagination? = nil,completion: @escaping (Result<[ChatMessage], Error>) -> Void)
- public func deleteFile(url: String,completion: ((Error?) -> Void)? = nil)
+ public func currentCooldownTime()-> Int
- public func deleteImage(url: String,completion: ((Error?) -> Void)? = nil)
+ public func deleteFile(url: String,completion: ((Error?) -> Void)? = nil)
- public func getFirstUnreadMessageId(for channel: ChatChannel)-> MessageId?
+ public func deleteImage(url: String,completion: ((Error?) -> Void)? = nil)
- public func setPushPreference(level: PushPreferenceLevel,completion: ((Result<PushPreference, Error>) -> Void)? = nil)
+ public func getFirstUnreadMessageId(for channel: ChatChannel)-> MessageId?
- public func snoozePushNotifications(until date: Date,completion: ((Result<PushPreference, Error>) -> Void)? = nil)
+ public func setPushPreference(level: PushPreferenceLevel,completion: ((Result<PushPreference, Error>) -> Void)? = nil)
+ public func snoozePushNotifications(until date: Date,completion: ((Result<PushPreference, Error>) -> Void)? = nil) |
SDK Size
|
StreamChat XCSize
Show 7 more objects
|
StreamChatUI XCSize
|
|



🔗 Issue Links
Fixes: IOS-1277
🎯 Goal
Mark messages as unread using timestamp
📝 Summary
fromargument isDateChatChannelController.markUnread(from:completion:)Chat.markUnread(from:)MarkUnreadCriteriaenum for managing the difference of message id and message timestamp in existing methodsChatChannelVCnot updating the unread message separator (existing mark unread using a message id explicitly calls the update separator method)🛠 Implementation
Use the existing mark unread endpoint, just that we pass
message_timestampinstead ofmessage_id. Backend can throw errors when passing in random timestamps and there are no messages to match against.🎨 Showcase
🧪 Manual Testing Notes
Explain how this change can be tested manually, if applicable.
☑️ Contributor Checklist
docs-contentrepoSummary by CodeRabbit
New Features
UI
Bug Fixes
✏️ Tip: You can customize this high-level summary in your review settings.