Skip to content

Commit 80af097

Browse files
typing timer manager: method renames and documentation
1 parent 0ab1c61 commit 80af097

File tree

4 files changed

+32
-34
lines changed

4 files changed

+32
-34
lines changed

Sources/AblyChat/DefaultTyping.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ internal final class DefaultTyping: Typing {
107107

108108
logger.log(message: "Received started typing message: \(message)", level: .debug)
109109

110-
if !typingTimerManager.isTypingTimerActive(for: messageClientID) {
110+
if !typingTimerManager.isCurrentlyTyping(clientID: messageClientID) {
111111
// (CHA-T13b1) If the event represents a new client typing, then the chat client shall add the typer to the typing set and a emit the updated set to any subscribers. It shall also begin a timeout that is the sum of the CHA-T10 heartbeat interval and the CHA-T10a graсe period.
112112
typingTimerManager.startTypingTimer(
113113
for: messageClientID
@@ -117,14 +117,14 @@ internal final class DefaultTyping: Typing {
117117
}
118118
// (CHA-T13b3) (2/2) If the (CHA-T13b1) timeout expires, the client shall remove the clientId from the typing set and emit a synthetic typing stop event for the given client.
119119
subscription.emit(
120-
TypingEvent(currentlyTyping: typingTimerManager.currentlyTypingClients(), change: .init(clientId: messageClientID, type: .stopped))
120+
TypingEvent(currentlyTyping: typingTimerManager.currentlyTypingClientIDs(), change: .init(clientId: messageClientID, type: .stopped))
121121
)
122122
}
123123

124124
// (CHA-T13) When a typing event (typing.start or typing.stop) is received from the realtime client, the Chat client shall emit appropriate events to the user.
125125
subscription.emit(
126126
TypingEvent(
127-
currentlyTyping: typingTimerManager.currentlyTypingClients(),
127+
currentlyTyping: typingTimerManager.currentlyTypingClientIDs(),
128128
change: .init(clientId: messageClientID, type: .started)
129129
)
130130
)
@@ -139,14 +139,14 @@ internal final class DefaultTyping: Typing {
139139
logger.log(message: "Received stopped typing message: \(message)", level: .debug)
140140

141141
// (CHA-T13b5) If the event represents that a client has stopped typing, but the clientId for that client is not present in the typing set, then the event is ignored.
142-
if typingTimerManager.isTypingTimerActive(for: messageClientID) {
142+
if typingTimerManager.isCurrentlyTyping(clientID: messageClientID) {
143143
// (CHA-T13b4) If the event represents a client that has stopped typing, then the chat client shall remove the clientId from the typing set and emit the updated set to any subscribers. It shall also cancel the (CHA-T13b1) timeout for the typing client.
144144
typingTimerManager.cancelTypingTimer(for: messageClientID)
145145

146146
// (CHA-T13) When a typing event (typing.start or typing.stop) is received from the realtime client, the Chat client shall emit appropriate events to the user.
147147
subscription.emit(
148148
TypingEvent(
149-
currentlyTyping: typingTimerManager.currentlyTypingClients(),
149+
currentlyTyping: typingTimerManager.currentlyTypingClientIDs(),
150150
change: .init(clientId: messageClientID, type: .stopped)
151151
)
152152
)
@@ -168,7 +168,7 @@ internal final class DefaultTyping: Typing {
168168
}
169169

170170
internal func get() async throws(ARTErrorInfo) -> Set<String> {
171-
typingTimerManager.currentlyTypingClients()
171+
typingTimerManager.currentlyTypingClientIDs()
172172
}
173173

174174
internal func keystroke() async throws(ARTErrorInfo) {
@@ -204,7 +204,7 @@ internal final class DefaultTyping: Typing {
204204
internal func stop() async throws(ARTErrorInfo) {
205205
do throws {
206206
logger.log(message: "Stopping typing indicator for client: \(clientID)", level: .debug)
207-
if typingTimerManager.isTypingTimerActive(for: clientID) {
207+
if typingTimerManager.isCurrentlyTyping(clientID: clientID) {
208208
try await keyboardOperationQueue.enqueue { [weak self] () throws(InternalError) in
209209
guard let self else {
210210
return

Sources/AblyChat/TypingTimerManager.swift

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import Foundation
22

33
@MainActor
4-
internal final class TypingTimerManager {
4+
internal final class TypingTimerManager: TypingTimerManagerProtocol {
55
private let heartbeatThrottle: TimeInterval
66
private let gracePeriod: TimeInterval
77
private let logger: InternalLogger
@@ -75,13 +75,13 @@ internal final class TypingTimerManager {
7575
whoIsTypingTimers[clientID] = nil
7676
}
7777

78-
// TODO: add documentation once we see how these are being used
79-
internal func isTypingTimerActive(for clientID: String) -> Bool {
80-
currentlyTypingClients().contains(clientID)
78+
/// Returns whether this client is present in the set of users who we consider to currently be typing. This is what should be used for the CHA-T13b1 "represents a new client typing" and CHA-T13b5 "is not present in the typing set" checks.
79+
internal func isCurrentlyTyping(clientID: String) -> Bool {
80+
currentlyTypingClientIDs().contains(clientID)
8181
}
8282

83-
// TODO: add documentation once we see how these are being used
84-
internal func currentlyTypingClients() -> Set<String> {
83+
/// Returns the set of client IDs that we consider to currently be typing (also referred to in the spec as the "typing set").
84+
internal func currentlyTypingClientIDs() -> Set<String> {
8585
Set(whoIsTypingTimers.keys)
8686
}
8787
}
@@ -97,17 +97,15 @@ internal protocol TypingTimerManagerProtocol {
9797

9898
/// Starts a CHA-T13b1 "is this person typing" timer, thus adding this clientID to the typing set.
9999
func startTypingTimer(for clientID: String, handler: (@MainActor () -> Void)?)
100-
/// Per CHA-T13b4, cancels the CHA-T13b1 "is this person typing", thus removing this clientID from the typing set.
100+
/// Per CHA-T13b4, cancels the CHA-T13b1 "is this person typing" timer, thus removing this clientID from the typing set.
101101
func cancelTypingTimer(for clientID: String)
102102

103-
// TODO: document
104-
func isTypingTimerActive(for clientID: String) -> Bool
105-
func currentlyTypingClients() -> Set<String>
103+
/// Returns whether this client is present in the set of users who we consider to currently be typing. This is what should be used for the CHA-T13b1 "represents a new client typing" and CHA-T13b5 "is not present in the typing set" checks.
104+
func isCurrentlyTyping(clientID: String) -> Bool
105+
/// Returns the set of client IDs that we consider to currently be typing (also referred to in the spec as the "typing set").
106+
func currentlyTypingClientIDs() -> Set<String>
106107
}
107108

108-
// Extend the actual TypingTimerManager to conform to our protocol
109-
extension TypingTimerManager: TypingTimerManagerProtocol {}
110-
111109
internal protocol ClockProvider {
112110
func now() -> Date
113111
}

Tests/AblyChatTests/Mocks/MockTypingTimerManager.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,11 @@
4545
print("MockTypingTimerManager: Cancelled timer for \(clientID), isSelf: \(isSelf)")
4646
}
4747

48-
func isTypingTimerActive(for clientID: String) -> Bool {
48+
func isCurrentlyTyping(clientID: String) {
4949
activeTimers.contains(clientID)
5050
}
5151

52-
func currentlyTypingClients() -> Set<String> {
52+
func currentlyTypingClientIDs() -> Set<String> {
5353
typingClients
5454
}
5555
}

Tests/AblyChatTests/TypingTimerManagerTests.swift

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,11 @@ final class TypingTimerManagerTests {
5656
let mockClock = MockClock()
5757
let timerManager = createTypingTimerManager(with: mockClock)
5858

59-
#expect(!timerManager.isTypingTimerActive(for: "client1"))
59+
#expect(!timerManager.isCurrentlyTyping(clientID: "client1"))
6060

6161
timerManager.startTypingTimer(for: "client1")
62-
#expect(timerManager.isTypingTimerActive(for: "client1"))
63-
#expect(timerManager.currentlyTypingClients() == ["client1"])
62+
#expect(timerManager.isCurrentlyTyping(clientID: "client1"))
63+
#expect(timerManager.currentlyTypingClientIDs() == ["client1"])
6464
}
6565

6666
// @specOneOf(2/2) CHA-T13b1 - Tests the currentlyTypingClients method returns correct set of typing clients.
@@ -74,11 +74,11 @@ final class TypingTimerManagerTests {
7474
timerManager.startTypingTimer(for: "client2")
7575
timerManager.startTypingTimer(for: "client3")
7676

77-
#expect(timerManager.currentlyTypingClients() == ["client1", "client2", "client3"])
77+
#expect(timerManager.currentlyTypingClientIDs() == ["client1", "client2", "client3"])
7878

7979
timerManager.cancelTypingTimer(for: "client2")
8080

81-
#expect(timerManager.currentlyTypingClients() == ["client1", "client3"])
81+
#expect(timerManager.currentlyTypingClientIDs() == ["client1", "client3"])
8282
}
8383

8484
// @spec CHA-T13b3 - Tests timeout expiration removing client from typing set and calling the onCancelled handler.
@@ -93,16 +93,16 @@ final class TypingTimerManagerTests {
9393
handlerCalled = true
9494
}
9595

96-
#expect(timerManager.isTypingTimerActive(for: "client1"))
96+
#expect(timerManager.isCurrentlyTyping(clientID: "client1"))
9797

9898
// Advance time to trigger timer expiration (heartbeatThrottle + gracePeriod)
9999
mockClock.advance(by: 1.6)
100100

101101
await Task.yield()
102102

103103
#expect(handlerCalled)
104-
#expect(!timerManager.isTypingTimerActive(for: "client1"))
105-
#expect(timerManager.currentlyTypingClients().isEmpty)
104+
#expect(!timerManager.isCurrentlyTyping(clientID: "client1"))
105+
#expect(timerManager.currentlyTypingClientIDs().isEmpty)
106106
}
107107

108108
// @spec CHA-T13b2 - Tests that each additional typing heartbeat resets the timeout
@@ -131,15 +131,15 @@ final class TypingTimerManagerTests {
131131
await Task.yield()
132132

133133
// Client should still be typing because we reset the timer
134-
#expect(timerManager.isTypingTimerActive(for: "client1"))
134+
#expect(timerManager.isCurrentlyTyping(clientID: "client1"))
135135
#expect(!handlerCalled)
136136

137137
// Now advance enough to expire the reset timer
138138
mockClock.advance(by: 1.0)
139139
await Task.yield()
140140

141141
#expect(handlerCalled)
142-
#expect(!timerManager.isTypingTimerActive(for: "client1"))
142+
#expect(!timerManager.isCurrentlyTyping(clientID: "client1"))
143143
}
144144

145145
// @spec CHA-T10a1 - Tests that grace period is correctly applied to prevent flickering
@@ -158,15 +158,15 @@ final class TypingTimerManagerTests {
158158
mockClock.advance(by: 1.0)
159159
await Task.yield()
160160

161-
#expect(timerManager.isTypingTimerActive(for: "client1"))
161+
#expect(timerManager.isCurrentlyTyping(clientID: "client1"))
162162
#expect(!handlerCalled)
163163

164164
// Advance by grace period - now should expire
165165
mockClock.advance(by: 0.5)
166166
await Task.yield()
167167

168168
#expect(handlerCalled)
169-
#expect(!timerManager.isTypingTimerActive(for: "client1"))
169+
#expect(!timerManager.isCurrentlyTyping(clientID: "client1"))
170170
}
171171
}
172172

0 commit comments

Comments
 (0)