Skip to content

Commit 4be2f1e

Browse files
authored
ChatUserName for Typing Indicators (#97)
1 parent aaa70de commit 4be2f1e

File tree

7 files changed

+107
-3
lines changed

7 files changed

+107
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
66
### ✅ Added
77
- Possibility to view channel info on channel options
88
- Date separators in the message list
9+
- ChatUserNamer to customize user name on typing indicator
910

1011
### 🐞 Fixed
1112
- Bug about link attachments not opening when the URL was missing the scheme

Sources/StreamChatSwiftUI/ChatChannel/Utils/ChatChannelExtensions.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,9 @@ extension ChatChannel {
4747
/// - currentUserId: the id of the current user.
4848
/// - Returns: the typing indicator string.
4949
public func typingIndicatorString(currentUserId: UserId?) -> String {
50+
let chatUserNamer = InjectedValues[\.utils].chatUserNamer
5051
let typingUsers = currentlyTypingUsersFiltered(currentUserId: currentUserId)
51-
if let user = typingUsers.first(where: { user in user.name != nil }), let name = user.name {
52+
if let user = typingUsers.first(where: { user in user.name != nil }), let name = chatUserNamer.name(forUser: user) {
5253
return L10n.MessageList.TypingIndicator.users(name, typingUsers.count - 1)
5354
} else {
5455
// If we somehow cannot fetch any user name, we simply show that `Someone is typing`

Sources/StreamChatSwiftUI/Utils.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public class Utils {
1515
public var imageProcessor: ImageProcessor
1616
public var imageMerger: ImageMerging
1717
public var channelNamer: ChatChannelNamer
18+
public var chatUserNamer: ChatUserNamer
1819
public var channelAvatarsMerger: ChannelAvatarsMerging
1920
public var messageTypeResolver: MessageTypeResolving
2021
public var messageActionsResolver: MessageActionsResolving
@@ -40,6 +41,7 @@ public class Utils {
4041
messageListConfig: MessageListConfig = MessageListConfig(),
4142
composerConfig: ComposerConfig = ComposerConfig(),
4243
channelNamer: @escaping ChatChannelNamer = DefaultChatChannelNamer(),
44+
chatUserNamer: ChatUserNamer = DefaultChatUserNamer(),
4345
shouldSyncChannelControllerOnAppear: @escaping (ChatChannelController) -> Bool = { _ in true }
4446
) {
4547
self.dateFormatter = dateFormatter
@@ -49,6 +51,7 @@ public class Utils {
4951
self.imageProcessor = imageProcessor
5052
self.imageMerger = imageMerger
5153
self.channelNamer = channelNamer
54+
self.chatUserNamer = chatUserNamer
5255
self.channelAvatarsMerger = channelAvatarsMerger
5356
self.messageTypeResolver = messageTypeResolver
5457
messageActionsResolver = messageActionResolver
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//
2+
// Copyright © 2022 Stream.io Inc. All rights reserved.
3+
//
4+
5+
import Foundation
6+
import StreamChat
7+
8+
public protocol ChatUserNamer {
9+
/// Creates the user name string representation from a ChatUser object.
10+
/// - Parameter user: The chat user from which the name will be generated from.
11+
/// - Returns: A string value that represents the name of the user.
12+
func name(forUser user: ChatUser) -> String?
13+
}
14+
15+
/// Default implementation of the `ChatUserNamer` protocol.
16+
public class DefaultChatUserNamer: ChatUserNamer {
17+
public init() {
18+
// Public init.
19+
}
20+
21+
public func name(forUser user: ChatUser) -> String? {
22+
user.name
23+
}
24+
}

StreamChatSwiftUI.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,8 @@
332332
84FF723F2782FB2E006E26C8 /* iMessagePocView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84FF723E2782FB2E006E26C8 /* iMessagePocView.swift */; };
333333
91B763A4283EB19900B458A9 /* MoreChannelActionsFullScreenWrappingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91B763A3283EB19800B458A9 /* MoreChannelActionsFullScreenWrappingView.swift */; };
334334
91B763A6283EB39600B458A9 /* MoreChannelActionsFullScreenWrappingView_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91B763A5283EB39600B458A9 /* MoreChannelActionsFullScreenWrappingView_Tests.swift */; };
335+
91B79FD7284E21E0005B6E4F /* ChatUserNamer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91B79FD6284E21E0005B6E4F /* ChatUserNamer.swift */; };
336+
91B79FD9284E7E9C005B6E4F /* ChatUserNamer_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91B79FD8284E7E9C005B6E4F /* ChatUserNamer_Tests.swift */; };
335337
91CC203A283C3E7F0049A146 /* URLExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91CC2039283C3E7F0049A146 /* URLExtensions.swift */; };
336338
91CC203C283C4C250049A146 /* URLUtils_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91CC203B283C4C250049A146 /* URLUtils_Tests.swift */; };
337339
C14A465B284665B100EF498E /* SDKIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = C14A465A284665B100EF498E /* SDKIdentifier.swift */; };
@@ -705,6 +707,8 @@
705707
84FF723E2782FB2E006E26C8 /* iMessagePocView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iMessagePocView.swift; sourceTree = "<group>"; };
706708
91B763A3283EB19800B458A9 /* MoreChannelActionsFullScreenWrappingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoreChannelActionsFullScreenWrappingView.swift; sourceTree = "<group>"; };
707709
91B763A5283EB39600B458A9 /* MoreChannelActionsFullScreenWrappingView_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoreChannelActionsFullScreenWrappingView_Tests.swift; sourceTree = "<group>"; };
710+
91B79FD6284E21E0005B6E4F /* ChatUserNamer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatUserNamer.swift; sourceTree = "<group>"; };
711+
91B79FD8284E7E9C005B6E4F /* ChatUserNamer_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatUserNamer_Tests.swift; sourceTree = "<group>"; };
708712
91CC2039283C3E7F0049A146 /* URLExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLExtensions.swift; sourceTree = "<group>"; };
709713
91CC203B283C4C250049A146 /* URLUtils_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLUtils_Tests.swift; sourceTree = "<group>"; };
710714
C14A465A284665B100EF498E /* SDKIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SDKIdentifier.swift; sourceTree = "<group>"; };
@@ -1094,6 +1098,7 @@
10941098
8465FD3F2746A95600AF091E /* UIImage+Extensions.swift */,
10951099
8465FD402746A95600AF091E /* DateUtils.swift */,
10961100
8465FD412746A95600AF091E /* ChatChannelNamer.swift */,
1101+
91B79FD6284E21E0005B6E4F /* ChatUserNamer.swift */,
10971102
8465FD422746A95600AF091E /* InputTextView.swift */,
10981103
8465FD432746A95600AF091E /* NSLayoutConstraint+Extensions.swift */,
10991104
8465FD442746A95600AF091E /* ChatMessage+Extensions.swift */,
@@ -1389,6 +1394,7 @@
13891394
84C94D5D275A3AA9007FE2B9 /* ImageCDN_Tests.swift */,
13901395
84C94D5F275A45D2007FE2B9 /* ViewFactory_Tests.swift */,
13911396
84C94D61275A5BB7007FE2B9 /* ChatChannelNamer_Tests.swift */,
1397+
91B79FD8284E7E9C005B6E4F /* ChatUserNamer_Tests.swift */,
13921398
);
13931399
path = Utils;
13941400
sourceTree = "<group>";
@@ -1731,6 +1737,7 @@
17311737
8423C33D277C94F30092DCF1 /* TwoStepMentionCommand.swift in Sources */,
17321738
8465FD802746A95700AF091E /* WebView.swift in Sources */,
17331739
8465FD942746A95700AF091E /* AddedFileAttachmentsView.swift in Sources */,
1740+
91B79FD7284E21E0005B6E4F /* ChatUserNamer.swift in Sources */,
17341741
84F29090276CC1280045472D /* ShareButtonView.swift in Sources */,
17351742
84AB7B282773D4FE00631A10 /* TypingSuggester.swift in Sources */,
17361743
91B763A4283EB19900B458A9 /* MoreChannelActionsFullScreenWrappingView.swift in Sources */,
@@ -1791,6 +1798,7 @@
17911798
84C94D0B27578BF2007FE2B9 /* QueueAwareDelegate.swift in Sources */,
17921799
846B15F42817E7630017F7A1 /* ChatChannelInfoViewModel_Tests.swift in Sources */,
17931800
84C94CE427578B92007FE2B9 /* ChatChannelController_Mock.swift in Sources */,
1801+
91B79FD9284E7E9C005B6E4F /* ChatUserNamer_Tests.swift in Sources */,
17941802
84C94D58275A1B89007FE2B9 /* MessageTypeResolver_Tests.swift in Sources */,
17951803
84C94D282757954C007FE2B9 /* VirtualTimer.swift in Sources */,
17961804
84C94D60275A45D2007FE2B9 /* ViewFactory_Tests.swift in Sources */,
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//
2+
// Copyright © 2022 Stream.io Inc. All rights reserved.
3+
//
4+
5+
import StreamChat
6+
@testable import StreamChatSwiftUI
7+
import XCTest
8+
9+
class ChatUserNamer_Tests: XCTestCase {
10+
func test_defaultChatUserNamer_whenUserHasName_showsStringName() {
11+
// Given
12+
let chatUser = ChatUser.mock(id: .unique, name: "Darth Vader")
13+
14+
// When
15+
let defaultChatUserNamer = DefaultChatUserNamer()
16+
let userNameString = defaultChatUserNamer.name(forUser: chatUser)
17+
18+
// Then
19+
guard let userNameString = userNameString else {
20+
XCTFail()
21+
return
22+
}
23+
24+
XCTAssertEqual(userNameString, "Darth Vader")
25+
}
26+
27+
func test_defaultChatUserNamer_whenUserHasNoName_showsNil() {
28+
// Given
29+
let chatUser = ChatUser.mock(id: .unique, name: nil)
30+
31+
// When
32+
let defaultChatUserNamer = DefaultChatUserNamer()
33+
let userNameString = defaultChatUserNamer.name(forUser: chatUser)
34+
35+
// Then
36+
XCTAssertNil(userNameString)
37+
}
38+
}

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

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ title: Typing Indicators
44

55
## Typing Indicators Overview
66

7-
The SwiftUI SDK has support for typing indicators which are shown when other participants in a conversation are typing.
7+
The SwiftUI SDK has support for typing indicators which are shown when other participants in a conversation are typing.
8+
9+
### Typing Indicator Placement
810

911
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.
1012
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.
@@ -20,4 +22,31 @@ let utils = Utils(messageListConfig: messageListConfig)
2022
let streamChat = StreamChat(chatClient: chatClient, utils: utils)
2123
```
2224

23-
This setup is done when the `StreamChat` object is being created, usually at the start of the app (e.g. in the `AppDelegate`).
25+
This setup is done when the `StreamChat` object is being created, usually at the start of the app (e.g. in the `AppDelegate`).
26+
27+
### Typing Indicator User Name String
28+
29+
The user name string that is displayed on the typing indicator message can also be personalized. By default the displayed string in the typing indicator message is obtained from the `name` property in the `ChatUser` model, however this can be altered by creating a custom implementation of `ChatUserNamer`.
30+
31+
Here's an example of how to achieve this customization, so that the message prepends a role to the name of the user.
32+
33+
```swift
34+
class CustomChatUserNamer: ChatUserNamer {
35+
func name(forUser user: ChatUser) -> String? {
36+
guard let name = user.name else {
37+
return nil
38+
}
39+
40+
return "admin: \(name)"
41+
}
42+
}
43+
```
44+
45+
This setup is done when the `StreamChat` object is being created, usually at the start of the app (e.g. in the `AppDelegate`).
46+
47+
```swift
48+
let customChatUserNamer = CustomChatUserNamer()
49+
let utils = Utils(chatUserNamer: customChatUserNamer)
50+
51+
let streamChat = StreamChat(chatClient: chatClient, utils: utils)
52+
```

0 commit comments

Comments
 (0)