Skip to content

Commit 9856ee4

Browse files
Add throttling to mark as read
1 parent 63e1369 commit 9856ee4

File tree

4 files changed

+65
-1
lines changed

4 files changed

+65
-1
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
66
### 🐞 Fixed
77
- Fixed visibility for deleted messages indicator for current user
88

9+
### ✅ Added
10+
- Add throttling to mark as read
11+
912
# [4.38.0](https://github.com/GetStream/stream-chat-swiftui/releases/tag/4.38.0)
1013
_September 29, 2023_
1114

Sources/StreamChatSwiftUI/ChatChannel/ChatChannelViewModel.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ open class ChatChannelViewModel: ObservableObject, MessagesDataSource {
3838
private var disableDateIndicator = false
3939
private var channelName = ""
4040
private var onlineIndicatorShown = false
41+
private let throttler = Throttler(interval: 3, broadcastLatestEvent: true)
4142

4243
public var channelController: ChatChannelController
4344
public var messageController: ChatMessageController?
@@ -343,7 +344,9 @@ open class ChatChannelViewModel: ObservableObject, MessagesDataSource {
343344
private func maybeSendReadEvent(for message: ChatMessage) {
344345
if message.id != lastMessageRead {
345346
lastMessageRead = message.id
346-
channelController.markRead()
347+
throttler.throttle { [weak self] in
348+
self?.channelController.markRead()
349+
}
347350
}
348351
}
349352

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
//
2+
// Copyright © 2023 Stream.io Inc. All rights reserved.
3+
//
4+
5+
import Foundation
6+
7+
/// A throttler implementation. The action provided will only be executed if the last action executed has passed an amount of time.
8+
/// Based on the implementation from Apple: https://developer.apple.com/documentation/combine/anypublisher/throttle(for:scheduler:latest:)
9+
class Throttler {
10+
private var workItem: DispatchWorkItem?
11+
private let queue = DispatchQueue(label: "com.stream.throttler", qos: .background)
12+
private var previousRun: Date = Date.distantPast
13+
let interval: TimeInterval
14+
let broadcastLatestEvent: Bool
15+
16+
/// - Parameters:
17+
/// - interval: The interval that an action can be executed.
18+
/// - broadcastLatestEvent: A Boolean value that indicates whether we should be using the first or last event of the ones that are being throttled.
19+
/// This last action will have a delay of the provided interval until it is executed.
20+
init(interval: TimeInterval, broadcastLatestEvent: Bool = true) {
21+
self.interval = interval
22+
self.broadcastLatestEvent = broadcastLatestEvent
23+
}
24+
25+
/// Throttle an action. It will cancel the previous action if exists, and it will execute the action immediately
26+
/// if the last action executed was past the interval provided. If not, it will only be executed after a delay.
27+
/// - Parameter action: The closure to be performed.
28+
func throttle(_ action: @escaping () -> Void) {
29+
workItem?.cancel()
30+
31+
let workItem = DispatchWorkItem { [weak self] in
32+
guard let workItem = self?.workItem, !workItem.isCancelled else { return }
33+
action()
34+
self?.previousRun = Date()
35+
self?.workItem = nil
36+
}
37+
38+
self.workItem = workItem
39+
40+
let timeSinceLastRun = Date().timeIntervalSince(previousRun)
41+
let delay = timeSinceLastRun > interval ? 0 : interval
42+
// If the delay is 0, we always execute the action immediately.
43+
// If the delay is bigger than 0, we only execute it if `latest` was enabled.
44+
if delay == 0 || delay > 0 && broadcastLatestEvent {
45+
queue.asyncAfter(deadline: .now() + delay, execute: workItem)
46+
}
47+
}
48+
49+
/// Cancel any active action.
50+
func cancel() {
51+
workItem?.cancel()
52+
workItem = nil
53+
}
54+
}

StreamChatSwiftUI.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@
215215
84782785284A4DB500D2EE11 /* ChatClient_Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84C94CCB27578B92007FE2B9 /* ChatClient_Mock.swift */; };
216216
847CEFEE27C38ABE00606257 /* MessageCachingUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 847CEFED27C38ABE00606257 /* MessageCachingUtils.swift */; };
217217
847F7949282A91AD0009F74C /* ChatChannelView_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 847F7948282A91AD0009F74C /* ChatChannelView_Tests.swift */; };
218+
8482094E2ACFFCD900EF3261 /* Throttler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8482094D2ACFFCD900EF3261 /* Throttler.swift */; };
218219
848399EA275FB3E9003075E4 /* SnapshotTesting in Frameworks */ = {isa = PBXBuildFile; productRef = 848399E9275FB3E9003075E4 /* SnapshotTesting */; };
219220
848399EC275FB41B003075E4 /* ChatChannelListView_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 848399EB275FB41B003075E4 /* ChatChannelListView_Tests.swift */; };
220221
848399F227601231003075E4 /* ReactionsOverlayView_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 848399F127601231003075E4 /* ReactionsOverlayView_Tests.swift */; };
@@ -628,6 +629,7 @@
628629
847BA08827E0BAEE00ED20C7 /* WebSocketEngineMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebSocketEngineMock.swift; sourceTree = "<group>"; };
629630
847CEFED27C38ABE00606257 /* MessageCachingUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageCachingUtils.swift; sourceTree = "<group>"; };
630631
847F7948282A91AD0009F74C /* ChatChannelView_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatChannelView_Tests.swift; sourceTree = "<group>"; };
632+
8482094D2ACFFCD900EF3261 /* Throttler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Throttler.swift; sourceTree = "<group>"; };
631633
848399EB275FB41B003075E4 /* ChatChannelListView_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatChannelListView_Tests.swift; sourceTree = "<group>"; };
632634
848399F127601231003075E4 /* ReactionsOverlayView_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionsOverlayView_Tests.swift; sourceTree = "<group>"; };
633635
8492974727ABD97F00A8EEB0 /* DemoAppSwiftUI.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DemoAppSwiftUI.entitlements; sourceTree = "<group>"; };
@@ -1201,6 +1203,7 @@
12011203
8465FD2B2746A95600AF091E /* ChatChannelHelpers.swift */,
12021204
8465FD2C2746A95600AF091E /* ChatChannelExtensions.swift */,
12031205
91CC2039283C3E7F0049A146 /* URLExtensions.swift */,
1206+
8482094D2ACFFCD900EF3261 /* Throttler.swift */,
12041207
);
12051208
path = Utils;
12061209
sourceTree = "<group>";
@@ -1984,6 +1987,7 @@
19841987
8465FDA72746A95700AF091E /* KeyboardHandling.swift in Sources */,
19851988
8465FDD72746A95800AF091E /* Appearance.swift in Sources */,
19861989
8465FD8A2746A95700AF091E /* DiscardAttachmentButton.swift in Sources */,
1990+
8482094E2ACFFCD900EF3261 /* Throttler.swift in Sources */,
19871991
8465FDCB2746A95700AF091E /* ChatChannelListView.swift in Sources */,
19881992
841B2EF4278DB9E500ED619E /* MessageListHelperViews.swift in Sources */,
19891993
84C0C9A328CF18F700CD0136 /* SnapshotCreator.swift in Sources */,

0 commit comments

Comments
 (0)