Skip to content

Commit b706e12

Browse files
Merge branch 'feature/typing-indicators' of https://github.com/GetStream/stream-chat-swiftui into feature/muting-users
2 parents a9bd8f6 + 717448c commit b706e12

File tree

8 files changed

+48
-19
lines changed

8 files changed

+48
-19
lines changed

Sources/StreamChatSwiftUI/ChatChannel/ChannelHeader/ChatChannelHeaderViewModifier.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,11 @@ public struct DefaultChatChannelHeader: ToolbarContent {
3333
VStack(spacing: 2) {
3434
Text(channelNamer(channel, currentUserId) ?? "")
3535
.font(fonts.bodyBold)
36-
if !channel.currentlyTypingUsers.isEmpty
36+
if !channel.currentlyTypingUsersFiltered(currentUserId: currentUserId).isEmpty
3737
&& utils.typingIndicatorPlacement == .navigationBar {
3838
HStack {
3939
TypingIndicatorView()
40-
SubtitleText(text: channel.typingIndicatorString)
40+
SubtitleText(text: channel.typingIndicatorString(currentUserId: currentUserId))
4141
}
4242
} else {
4343
Text(channel.onlineInfoText(currentUserId: currentUserId))

Sources/StreamChatSwiftUI/ChatChannel/ChatChannelViewModel.swift

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ open class ChatChannelViewModel: ObservableObject, MessagesDataSource {
8888

8989
@Published public var reactionsShown = false {
9090
didSet {
91+
// When reactions are shown, the navigation bar is hidden.
92+
// Check the header type and trigger an update.
9193
checkHeaderType()
9294
}
9395
}
@@ -271,16 +273,27 @@ open class ChatChannelViewModel: ObservableObject, MessagesDataSource {
271273

272274
private func checkHeaderType() {
273275
let type: ChannelHeaderType
276+
let typingUsers = channel.currentlyTypingUsersFiltered(
277+
currentUserId: chatClient.currentUserId
278+
)
279+
274280
if !reactionsShown && isMessageThread {
275281
type = .messageThread
276-
} else if !channel.currentlyTypingUsers.isEmpty {
282+
} else if !typingUsers.isEmpty {
277283
type = .typingIndicator
278284
} else {
279285
type = .regular
280286
}
281287

282288
if type != channelHeaderType {
283289
channelHeaderType = type
290+
} else if type == .typingIndicator {
291+
// Toolbar is not updated when new user starts typing.
292+
// Therefore, we shortly update the state to regular to trigger an update.
293+
channelHeaderType = .regular
294+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
295+
self?.channelHeaderType = .typingIndicator
296+
}
284297
}
285298
}
286299
}

Sources/StreamChatSwiftUI/ChatChannel/MessageList/MessageListView.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import SwiftUI
77

88
struct MessageListView<Factory: ViewFactory>: View, KeyboardReadable {
99
@Injected(\.utils) private var utils
10+
@Injected(\.chatClient) private var chatClient
1011

1112
var factory: Factory
1213
var channel: ChatChannel
@@ -145,10 +146,10 @@ struct MessageListView<Factory: ViewFactory>: View, KeyboardReadable {
145146
DateIndicatorView(date: date)
146147
}
147148

148-
if !channel.currentlyTypingUsers.isEmpty
149+
if !channel.currentlyTypingUsersFiltered(currentUserId: chatClient.currentUserId).isEmpty
149150
&& utils.typingIndicatorPlacement == .bottomOverlay {
150151
TypingIndicatorBottomView(
151-
typingIndicatorString: channel.typingIndicatorString
152+
typingIndicatorString: channel.typingIndicatorString(currentUserId: chatClient.currentUserId)
152153
)
153154
}
154155
}

Sources/StreamChatSwiftUI/ChatChannel/Utils/ChatChannelExtensions.swift

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import Foundation
66
import StreamChat
77

88
extension ChatChannel {
9+
910
func onlineInfoText(currentUserId: String) -> String {
1011
if isDirectMessageChannel {
1112
guard let member = lastActiveMembers
@@ -27,8 +28,14 @@ extension ChatChannel {
2728
return L10n.Message.Title.group(memberCount, watcherCount)
2829
}
2930

30-
var typingIndicatorString: String {
31-
let typingUsers = Array(currentlyTypingUsers)
31+
func currentlyTypingUsersFiltered(currentUserId: UserId?) -> [ChatUser] {
32+
currentlyTypingUsers.filter { user in
33+
user.id != currentUserId
34+
}
35+
}
36+
37+
func typingIndicatorString(currentUserId: UserId?) -> String {
38+
let typingUsers = currentlyTypingUsersFiltered(currentUserId: currentUserId)
3239
if let user = typingUsers.first(where: { user in user.name != nil }), let name = user.name {
3340
return L10n.MessageList.TypingIndicator.users(name, typingUsers.count - 1)
3441
} else {

Sources/StreamChatSwiftUI/ChatChannelList/ChatChannelListItem.swift

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ public struct ChatChannelListItem: View {
1212
@Injected(\.colors) private var colors
1313
@Injected(\.utils) private var utils
1414
@Injected(\.images) private var images
15+
@Injected(\.chatClient) private var chatClient
1516

1617
var channel: ChatChannel
1718
var channelName: String
@@ -47,7 +48,9 @@ public struct ChatChannelListItem: View {
4748
}
4849
} else {
4950
HStack(spacing: 4) {
50-
if !channel.currentlyTypingUsers.isEmpty {
51+
if !channel.currentlyTypingUsersFiltered(
52+
currentUserId: chatClient.currentUserId
53+
).isEmpty {
5154
TypingIndicatorView()
5255
}
5356
SubtitleText(text: subtitleText)
@@ -81,8 +84,10 @@ public struct ChatChannelListItem: View {
8184
private var subtitleText: String {
8285
if channel.isMuted {
8386
return L10n.Channel.Item.muted
84-
} else if !channel.currentlyTypingUsers.isEmpty {
85-
return channel.typingIndicatorString
87+
} else if !channel.currentlyTypingUsersFiltered(
88+
currentUserId: chatClient.currentUserId
89+
).isEmpty {
90+
return channel.typingIndicatorString(currentUserId: chatClient.currentUserId)
8691
} else if let latestMessage = channel.latestMessages.first {
8792
return "\(latestMessage.author.name ?? latestMessage.author.id): \(latestMessage.textContent ?? latestMessage.text)"
8893
} else {

Sources/StreamChatSwiftUI/Resources/en.lproj/Localizable.stringsdict

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<key>zero</key>
1616
<string> is typing</string>
1717
<key>one</key>
18-
<string> and %2$d more is typing</string>
18+
<string> and %2$d more are typing</string>
1919
<key>other</key>
2020
<string> and %2$d more are typing</string>
2121
</dict>

StreamChatSwiftUITests/Tests/ChatChannel/ChatChannelExtensions_Tests.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class ChatChannelExtensions_Tests: XCTestCase {
2626
let channel = ChatChannel.mockDMChannel()
2727

2828
// When
29-
let typingIndicatorString = channel.typingIndicatorString
29+
let typingIndicatorString = channel.typingIndicatorString(currentUserId: nil)
3030

3131
// Then
3232
XCTAssert(typingIndicatorString == "Someone is typing")
@@ -42,7 +42,7 @@ class ChatChannelExtensions_Tests: XCTestCase {
4242
)
4343

4444
// When
45-
let typingIndicatorString = channel.typingIndicatorString
45+
let typingIndicatorString = channel.typingIndicatorString(currentUserId: nil)
4646

4747
// Then
4848
XCTAssert(typingIndicatorString == "Martin is typing")
@@ -61,12 +61,12 @@ class ChatChannelExtensions_Tests: XCTestCase {
6161
)
6262

6363
// When
64-
let typingIndicatorString = channel.typingIndicatorString
64+
let typingIndicatorString = channel.typingIndicatorString(currentUserId: nil)
6565

6666
// Then
6767
XCTAssert(
68-
typingIndicatorString == "Stefan and 1 more is typing"
69-
|| typingIndicatorString == "Martin and 1 more is typing"
68+
typingIndicatorString == "Stefan and 1 more are typing"
69+
|| typingIndicatorString == "Martin and 1 more are typing"
7070
) // Any of the names can appear first.
7171
}
7272
}

docusaurus/docs/iOS/swiftui/components/typing-indicators.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@ title: Typing Indicators
44

55
## Typing Indicators Overview
66

7-
The SwiftUI SDK has support for typing indicators, shown when other participants in a conversation are typing. There are two places where the typing indicators are shown. The first one is when the user is in the channels list - when someone in a channel is typing, the indicator is shown in the corresponding channel list item. The second place, where a typing indicator is shown, is in the chat channel view. Depending on the configuration provided, the typing indicator can be shown either in the navigation bar, or above the composer, as an overlay over the message list.
7+
The SwiftUI SDK has support for typing indicators which are shown when other participants in a conversation are typing.
88

9-
The configuration for placing the typing indicator can be found in the `TypingIndicatorPlacement` enum, which is part of the `Utils` class. By default, the placement of the typing indicator is above the composer, which is represented by the enum value `bottomOverlay`.
9+
There are two places where the typing indicators are shown. The first one is when the user is in the channels list - when someone in a channel is typing, the indicator appears in the corresponding channel list item.
10+
The second place, where a typing indicator is shown, is in the chat channel view. Depending on the configuration provided, the typing indicator can be shown either in the navigation bar or above the composer, as an overlay over the message list.
1011

11-
Here's an example how to change the configuration, so that the typing indicator is shown in the navigation bar.
12+
The configuration for placing the typing indicator can be found in the `TypingIndicatorPlacement` enum, which is part of the `Utils` class. By default, the placement of the typing indicator is above the composer, which is represented by the enum value `.bottomOverlay`.
13+
14+
Here's an example of how to change the configuration, so that the typing indicator is shown in the navigation bar (represented by the `TypingIndicatorPlacement` enum value `.navigationBar`).
1215

1316
```swift
1417
let utils = Utils(typingIndicatorPlacement: .navigationBar)

0 commit comments

Comments
 (0)