-
Notifications
You must be signed in to change notification settings - Fork 233
Grouped channels endpoint (v5) #4076
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?
Changes from all commits
dcf4bf0
e1c4005
17eb957
6939d94
652a0fe
141fc1e
e6e63ce
90ee63e
9bcda48
55a1a6f
965a983
55ca140
9bf95ba
819226e
f872fb1
94487d1
9a193c4
8e4f47b
9bf6d49
bb55a75
82a808b
0c4e36a
86e6109
74a49bb
c796751
9053022
ff593a1
1b188ad
c6a08ff
b80d551
a3c6248
1fc76e7
5c5a28e
708e6b8
3a39732
a798045
c970569
89a082c
51b5bb2
95de344
9e2a4ca
714d8ab
d636460
60971db
1d5043d
394155c
2d4190c
24abf5b
2a59ed5
3c33a56
6989801
f94acb3
ca29986
a86861e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -281,7 +281,9 @@ extension NSManagedObjectContext { | |
| dto.deletedAt = payload.deletedAt?.bridgeDate | ||
| dto.updatedAt = payload.updatedAt.bridgeDate | ||
| dto.defaultSortingAt = (payload.lastMessageAt ?? payload.createdAt).bridgeDate | ||
| dto.lastMessageAt = payload.lastMessageAt?.bridgeDate | ||
| if let lastMessageAt = payload.lastMessageAt { | ||
| dto.lastMessageAt = lastMessageAt.bridgeDate | ||
| } | ||
|
Comment on lines
+284
to
+286
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @laevandus This can be dangerous, AFAIK, in some scenarios, we do want lastMessageAt to be reset to nil. This most likely will break some scenarios. Why was this unwrapping added?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is from the initial batch of changes when Martin started with it. I think I should remove it because the initial implementation had logic around it. |
||
| dto.memberCount = Int64(clamping: payload.memberCount) | ||
|
|
||
| if let messageCount = payload.messageCount { | ||
|
|
@@ -356,7 +358,7 @@ extension NSManagedObjectContext { | |
| dto.reads.formUnion(reads) | ||
|
|
||
| try payload.messages.forEach { _ = try saveMessage(payload: $0, channelDTO: dto, syncOwnReactions: true, cache: cache) } | ||
|
|
||
| var pendingMessages = Set<MessageDTO>() | ||
| try payload.pendingMessages?.forEach { | ||
| let pending = try saveMessage( | ||
|
|
@@ -444,7 +446,7 @@ extension NSManagedObjectContext { | |
| } | ||
|
|
||
| func delete(query: ChannelListQuery) { | ||
| guard let dto = channelListQuery(filterHash: query.filter.filterHash) else { return } | ||
| guard let dto = channelListQuery(query) else { return } | ||
|
|
||
| delete(dto) | ||
| } | ||
|
|
@@ -477,7 +479,7 @@ extension ChannelDTO { | |
|
|
||
| request.sortDescriptors = sortDescriptors.isEmpty ? [ChannelListSortingKey.defaultSortDescriptor] : sortDescriptors | ||
|
|
||
| let matchingQuery = NSPredicate(format: "ANY queries.filterHash == %@", query.filter.filterHash) | ||
| let matchingQuery = NSPredicate(format: "ANY queries.filterHash == %@", query.queryHash) | ||
| let notDeleted = NSPredicate(format: "deletedAt == nil") | ||
|
|
||
| var subpredicates: [NSPredicate] = [ | ||
|
|
@@ -497,8 +499,13 @@ extension ChannelDTO { | |
| } | ||
|
|
||
| request.predicate = NSCompoundPredicate(type: .and, subpredicates: subpredicates) | ||
| request.fetchLimit = query.pagination.pageSize | ||
| request.fetchBatchSize = query.pagination.pageSize | ||
| request.fetchLimit = query.pagination.pageSize == .unsetPageSize ? 0 : query.pagination.pageSize | ||
| // For grouped queries `pageSize` is `.unsetPageSize`, which would disable batching. | ||
| // Fall back to the standard channels page size to keep memory bounded as the linked set | ||
| // grows across pagination. | ||
| request.fetchBatchSize = query.pagination.pageSize == .unsetPageSize | ||
| ? .channelsPageSize | ||
| : query.pagination.pageSize | ||
|
Comment on lines
+502
to
+508
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this needed? Not very clear from the docs π€ |
||
| return request | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,6 +12,38 @@ class ChannelListQueryDTO: NSManagedObject { | |
| /// Serialized `Filter` JSON which can be used in cases the query needs to be repeated, i.e. for newly created channels. | ||
| @NSManaged var filterJSONData: Data | ||
|
|
||
| /// Next-page cursor returned by the grouped channels endpoint for this group. | ||
| /// `nil` means there is no next page (either never paginated or the backend | ||
| /// signaled exhaustion). Only meaningful for queries that carry a `groupKey`. | ||
| @NSManaged var next: String? | ||
|
|
||
| /// The `watch` flag that the original grouped-channels request was issued with. | ||
| /// | ||
| /// Persisted so that subsequent paginated fetches (via `ChannelList`) and the | ||
| /// `SyncRepository` refetch after reconnect can reuse the same value the caller | ||
| /// passed to `ChatClient.queryGroupedChannels(groups:limit:presence:watch:)` instead of | ||
| /// silently downgrading to `false`. | ||
| /// | ||
| /// When `watch` is `false`, ordinary channel and member WebSocket events | ||
| /// (`message.new`, `channel.updated`, etc.) still arrive for channels the current | ||
| /// user is a member of. What `watch == true` additionally enables is the | ||
| /// watcher-scoped event stream β most notably typing indicators | ||
| /// (`typing.start` / `typing.stop`) β so any UI that needs typing indicators | ||
| /// on a grouped channel must pass `watch: true` on the initial query. | ||
| /// | ||
| /// Only meaningful for queries that carry a `groupKey`; filter-based queries | ||
| /// derive watching from `ChannelListQuery.options`. | ||
| @NSManaged var watch: Bool | ||
|
|
||
| /// The `presence` flag that the original grouped-channels request was issued with. | ||
| /// | ||
| /// Persisted so that subsequent paginated fetches and sync refetches reuse the | ||
| /// same value, keeping presence info in responses and presence updates on the | ||
| /// WebSocket consistent across the lifetime of the group subscription. | ||
| /// | ||
| /// Only meaningful for queries that carry a `groupKey`. | ||
| @NSManaged var presence: Bool | ||
|
|
||
| // MARK: - Relationships | ||
|
|
||
| @NSManaged var channels: Set<ChannelDTO> | ||
|
|
@@ -35,21 +67,21 @@ class ChannelListQueryDTO: NSManagedObject { | |
| } | ||
|
|
||
| extension NSManagedObjectContext { | ||
| func channelListQuery(filterHash: String) -> ChannelListQueryDTO? { | ||
| ChannelListQueryDTO.load(filterHash: filterHash, context: self) | ||
| func channelListQuery(_ query: ChannelListQuery) -> ChannelListQueryDTO? { | ||
| ChannelListQueryDTO.load(filterHash: query.queryHash, context: self) | ||
| } | ||
|
|
||
| func saveQuery(query: ChannelListQuery) -> ChannelListQueryDTO { | ||
| if let existingDTO = channelListQuery(filterHash: query.filter.filterHash) { | ||
| if let existingDTO = channelListQuery(query) { | ||
| return existingDTO | ||
| } | ||
|
|
||
| let request = ChannelListQueryDTO.fetchRequest( | ||
| keyPath: #keyPath(ChannelListQueryDTO.filterHash), | ||
| equalTo: query.filter.filterHash | ||
| equalTo: query.queryHash | ||
| ) | ||
| let newDTO = NSEntityDescription.insertNewObject(into: self, for: request) | ||
| newDTO.filterHash = query.filter.filterHash | ||
| newDTO.filterHash = query.queryHash | ||
|
|
||
|
Comment on lines
+70
to
85
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Namespace grouped query ids before persisting them. These lookups now store both regular list queries and grouped list queries in the same unique π€ Prompt for AI Agents |
||
| let jsonData: Data | ||
| do { | ||
|
|
||
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.
Fix the Upcoming changelog entry before merge.
This block documents
unreadChannelCountsByGroup, but the feature in this PR isgroupedUnreadCount, so the release notes will point users to a non-existent symbol. It also omits the requiredStreamChatUIandStreamChatCommonUIsubsections under# Upcoming.As per coding guidelines,
Include separate subsections in CHANGELOG.md for StreamChat, StreamChatUI, and StreamChatCommonUI.π€ Prompt for AI Agents