Skip to content

Commit 8d9766d

Browse files
Enabled swapping of the message container view
1 parent 8b1667c commit 8d9766d

File tree

14 files changed

+227
-45
lines changed

14 files changed

+227
-45
lines changed

Sources/StreamChatSwiftUI/ChatChannel/ChannelHeader/ChatChannelHeaderViewModifier.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public struct DefaultChatChannelHeader: ToolbarContent {
2727

2828
private var shouldShowTypingIndicator: Bool {
2929
!channel.currentlyTypingUsersFiltered(currentUserId: currentUserId).isEmpty
30-
&& utils.typingIndicatorPlacement == .navigationBar
30+
&& utils.messageListConfig.typingIndicatorPlacement == .navigationBar
3131
&& channel.config.typingEventsEnabled
3232
}
3333

Sources/StreamChatSwiftUI/ChatChannel/ChatChannelView.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,6 @@ public struct ChatChannelView<Factory: ViewFactory>: View {
4040
showScrollToLatestButton: $viewModel.showScrollToLatestButton,
4141
quotedMessage: $viewModel.quotedMessage,
4242
currentDateString: viewModel.currentDateString,
43-
isGroup: !viewModel.channel.isDirectMessageChannel,
44-
unreadCount: viewModel.channel.unreadCount.messages,
4543
listId: viewModel.listId,
4644
isMessageThread: viewModel.isMessageThread,
4745
onMessageAppear: viewModel.handleMessageAppear(index:),

Sources/StreamChatSwiftUI/ChatChannel/MessageList/MessageContainerView.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ struct MessageContainerView<Factory: ViewFactory>: View {
1717
var factory: Factory
1818
let channel: ChatChannel
1919
let message: ChatMessage
20-
let isInGroup: Bool
2120
var width: CGFloat?
2221
var showsAllInfo: Bool
2322
var isInThread: Bool
@@ -35,7 +34,7 @@ struct MessageContainerView<Factory: ViewFactory>: View {
3534
var body: some View {
3635
HStack(alignment: .bottom) {
3736
if message.type == .system {
38-
SystemMessageView(message: message.text)
37+
factory.makeSystemMessageView(message: message)
3938
} else {
4039
if message.isSentByCurrentUser {
4140
MessageSpacer(spacerWidth: spacerWidth)
@@ -146,7 +145,7 @@ struct MessageContainerView<Factory: ViewFactory>: View {
146145
)
147146
MessageDateView(message: message)
148147
}
149-
} else if !message.isSentByCurrentUser && isInGroup {
148+
} else if !message.isSentByCurrentUser && !channel.isDirectMessageChannel {
150149
MessageAuthorAndDateView(message: message)
151150
} else {
152151
MessageDateView(message: message)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//
2+
// Copyright © 2022 Stream.io Inc. All rights reserved.
3+
//
4+
5+
import StreamChat
6+
7+
/// Configuration for the message list.
8+
public struct MessageListConfig {
9+
public init(
10+
messageListType: MessageListType = .messaging,
11+
typingIndicatorPlacement: TypingIndicatorPlacement = .bottomOverlay,
12+
groupMessages: Bool = true
13+
) {
14+
self.messageListType = messageListType
15+
self.typingIndicatorPlacement = typingIndicatorPlacement
16+
self.groupMessages = groupMessages
17+
}
18+
19+
let messageListType: MessageListType
20+
let typingIndicatorPlacement: TypingIndicatorPlacement
21+
let groupMessages: Bool
22+
}
23+
24+
/// Type of message list. Currently only `messaging` is supported.
25+
public enum MessageListType {
26+
case messaging
27+
case team
28+
case livestream
29+
case commerce
30+
}

Sources/StreamChatSwiftUI/ChatChannel/MessageList/MessageListView.swift

Lines changed: 35 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ struct MessageListView<Factory: ViewFactory>: View, KeyboardReadable {
1717
@Binding var showScrollToLatestButton: Bool
1818
@Binding var quotedMessage: ChatMessage?
1919
var currentDateString: String?
20-
var isGroup: Bool
21-
var unreadCount: Int
2220
var listId: String
2321
var isMessageThread: Bool
2422

@@ -35,9 +33,13 @@ struct MessageListView<Factory: ViewFactory>: View, KeyboardReadable {
3533
utils.dateFormatter
3634
}
3735

36+
private var messageListConfig: MessageListConfig {
37+
utils.messageListConfig
38+
}
39+
3840
private var shouldShowTypingIndicator: Bool {
3941
!channel.currentlyTypingUsersFiltered(currentUserId: chatClient.currentUserId).isEmpty
40-
&& utils.typingIndicatorPlacement == .bottomOverlay
42+
&& messageListConfig.typingIndicatorPlacement == .bottomOverlay
4143
&& channel.config.typingEventsEnabled
4244
}
4345

@@ -59,42 +61,17 @@ struct MessageListView<Factory: ViewFactory>: View, KeyboardReadable {
5961

6062
LazyVStack(spacing: 0) {
6163
ForEach(messages, id: \.messageId) { message in
62-
MessageContainerView(
63-
factory: factory,
64+
factory.makeMessageContainerView(
6465
channel: channel,
6566
message: message,
66-
isInGroup: isGroup,
6767
width: width,
6868
showsAllInfo: showsAllData(for: message),
6969
isInThread: isMessageThread,
7070
scrolledId: $scrolledId,
7171
quotedMessage: $quotedMessage,
72-
onLongPress: { messageDisplayInfo in
73-
if keyboardShown {
74-
resignFirstResponder()
75-
let updatedFrame = CGRect(
76-
x: messageDisplayInfo.frame.origin.x,
77-
y: messageDisplayInfo.frame.origin.y,
78-
width: messageDisplayInfo.frame.width,
79-
height: messageDisplayInfo.frame.height
80-
)
81-
82-
let updatedDisplayInfo = MessageDisplayInfo(
83-
message: messageDisplayInfo.message,
84-
frame: updatedFrame,
85-
contentWidth: messageDisplayInfo.contentWidth,
86-
isFirst: messageDisplayInfo.isFirst
87-
)
88-
89-
onLongPress(updatedDisplayInfo)
90-
} else {
91-
onLongPress(messageDisplayInfo)
92-
}
93-
}
72+
onLongPress: handleLongPress(messageDisplayInfo:),
73+
isLast: message == messages.last
9474
)
95-
.padding(.horizontal, 8)
96-
.padding(.bottom, showsAllData(for: message) ? 8 : 2)
97-
.padding(.top, message == messages.last ? 8 : 0)
9875
.flippedUpsideDown()
9976
.onAppear {
10077
let index = messages.firstIndex { msg in
@@ -143,7 +120,7 @@ struct MessageListView<Factory: ViewFactory>: View, KeyboardReadable {
143120

144121
if showScrollToLatestButton {
145122
ScrollToBottomButton(
146-
unreadCount: unreadCount,
123+
unreadCount: channel.unreadCount.messages,
147124
onScrollToBottom: onScrollToBottom
148125
)
149126
}
@@ -175,12 +152,38 @@ struct MessageListView<Factory: ViewFactory>: View, KeyboardReadable {
175152
}
176153

177154
private func showsAllData(for message: ChatMessage) -> Bool {
155+
if !messageListConfig.groupMessages {
156+
return true
157+
}
178158
let dateString = dateFormatter.string(from: message.createdAt)
179159
let prefix = message.author.id
180160
let key = "\(prefix)-\(dateString)"
181161
let inMessagingGroup = messagesGroupingInfo[key]?.contains(message.id) ?? false
182162
return inMessagingGroup
183163
}
164+
165+
private func handleLongPress(messageDisplayInfo: MessageDisplayInfo) {
166+
if keyboardShown {
167+
resignFirstResponder()
168+
let updatedFrame = CGRect(
169+
x: messageDisplayInfo.frame.origin.x,
170+
y: messageDisplayInfo.frame.origin.y,
171+
width: messageDisplayInfo.frame.width,
172+
height: messageDisplayInfo.frame.height
173+
)
174+
175+
let updatedDisplayInfo = MessageDisplayInfo(
176+
message: messageDisplayInfo.message,
177+
frame: updatedFrame,
178+
contentWidth: messageDisplayInfo.contentWidth,
179+
isFirst: messageDisplayInfo.isFirst
180+
)
181+
182+
onLongPress(updatedDisplayInfo)
183+
} else {
184+
onLongPress(messageDisplayInfo)
185+
}
186+
}
184187
}
185188

186189
public struct ScrollToBottomButton: View {

Sources/StreamChatSwiftUI/DefaultViewFactory.swift

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,33 @@ extension ViewFactory {
164164
DefaultMessageThreadHeaderModifier()
165165
}
166166

167+
public func makeMessageContainerView(
168+
channel: ChatChannel,
169+
message: ChatMessage,
170+
width: CGFloat?,
171+
showsAllInfo: Bool,
172+
isInThread: Bool,
173+
scrolledId: Binding<String?>,
174+
quotedMessage: Binding<ChatMessage?>,
175+
onLongPress: @escaping (MessageDisplayInfo) -> Void,
176+
isLast: Bool
177+
) -> some View {
178+
MessageContainerView(
179+
factory: self,
180+
channel: channel,
181+
message: message,
182+
width: width,
183+
showsAllInfo: showsAllInfo,
184+
isInThread: isInThread,
185+
scrolledId: scrolledId,
186+
quotedMessage: quotedMessage,
187+
onLongPress: onLongPress
188+
)
189+
.padding(.horizontal, 8)
190+
.padding(.bottom, showsAllInfo ? 8 : 2)
191+
.padding(.top, isLast ? 8 : 0)
192+
}
193+
167194
public func makeMessageTextView(
168195
for message: ChatMessage,
169196
isFirst: Bool,
@@ -258,6 +285,12 @@ extension ViewFactory {
258285
)
259286
}
260287

288+
public func makeSystemMessageView(
289+
message: ChatMessage
290+
) -> some View {
291+
SystemMessageView(message: message.text)
292+
}
293+
261294
public func makeCustomAttachmentViewType(
262295
for message: ChatMessage,
263296
isFirst: Bool,

Sources/StreamChatSwiftUI/Utils.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public class Utils {
1818
public var messageTypeResolver: MessageTypeResolving
1919
public var messageActionsResolver: MessageActionsResolving
2020
public var commandsConfig: CommandsConfig
21-
public var typingIndicatorPlacement: TypingIndicatorPlacement
21+
public var messageListConfig: MessageListConfig
2222

2323
public init(
2424
dateFormatter: DateFormatter = .makeDefault(),
@@ -31,7 +31,7 @@ public class Utils {
3131
messageTypeResolver: MessageTypeResolving = MessageTypeResolver(),
3232
messageActionResolver: MessageActionsResolving = MessageActionsResolver(),
3333
commandsConfig: CommandsConfig = DefaultCommandsConfig(),
34-
typingIndicatorPlacement: TypingIndicatorPlacement = .bottomOverlay,
34+
messageListConfig: MessageListConfig = MessageListConfig(),
3535
channelNamer: @escaping ChatChannelNamer = DefaultChatChannelNamer()
3636
) {
3737
self.dateFormatter = dateFormatter
@@ -45,6 +45,6 @@ public class Utils {
4545
self.messageTypeResolver = messageTypeResolver
4646
messageActionsResolver = messageActionResolver
4747
self.commandsConfig = commandsConfig
48-
self.typingIndicatorPlacement = typingIndicatorPlacement
48+
self.messageListConfig = messageListConfig
4949
}
5050
}

Sources/StreamChatSwiftUI/ViewFactory.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,31 @@ public protocol ViewFactory: AnyObject {
139139
/// Creates the message thread header view modifier.
140140
func makeMessageThreadHeaderViewModifier() -> ThreadHeaderViewModifier
141141

142+
associatedtype MessageContainerViewType: View
143+
/// Creates the message container view.
144+
/// - Parameters:
145+
/// - channel: the chat channel where the message was sent.
146+
/// - message: the chat message.
147+
/// - width: the available width for the message.
148+
/// - showsAllInfo: whether all info is shown for the message (i.e. whether is part of a group or leading message).
149+
/// - isInThread: whether the message is part of a message thread.
150+
/// - scrolledId: binding of the currently scrolled id. Use it to force scrolling to the particular message.
151+
/// - quotedMessage: binding of an optional quoted message.
152+
/// - onLongPress: called when the message is long pressed.
153+
/// - isLast: whether it is the last message (e.g. to apply extra padding).
154+
/// - Returns: view shown in the message container slot.
155+
func makeMessageContainerView(
156+
channel: ChatChannel,
157+
message: ChatMessage,
158+
width: CGFloat?,
159+
showsAllInfo: Bool,
160+
isInThread: Bool,
161+
scrolledId: Binding<String?>,
162+
quotedMessage: Binding<ChatMessage?>,
163+
onLongPress: @escaping (MessageDisplayInfo) -> Void,
164+
isLast: Bool
165+
) -> MessageContainerViewType
166+
142167
associatedtype MessageTextViewType: View
143168
/// Creates the message text view.
144169
/// - Parameters:
@@ -242,6 +267,12 @@ public protocol ViewFactory: AnyObject {
242267
availableWidth: CGFloat
243268
) -> DeletedMessageViewType
244269

270+
associatedtype SystemMessageViewType: View
271+
/// Creates the view for displaying system messages.
272+
/// - Parameter message: the system message.
273+
/// - Returns: view displayed when a system message appears.
274+
func makeSystemMessageView(message: ChatMessage) -> SystemMessageViewType
275+
245276
associatedtype CustomAttachmentViewType: View
246277
/// Creates custom attachment view.
247278
/// If support for more than one custom view is needed, just do if-else check inside the view.

StreamChatSwiftUI.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@
175175
84B288D1274CEDD000DD090B /* GroupNameView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84B288D0274CEDD000DD090B /* GroupNameView.swift */; };
176176
84B288D3274D23AF00DD090B /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84B288D2274D23AF00DD090B /* LoginView.swift */; };
177177
84B288D5274D286500DD090B /* LoginViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84B288D4274D286500DD090B /* LoginViewModel.swift */; };
178+
84B55F6A2798154C00B99B01 /* MessageListConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84B55F692798154C00B99B01 /* MessageListConfig.swift */; };
178179
84C2042327917B6A0024D616 /* MessageListView_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84C2042227917B6A0024D616 /* MessageListView_Tests.swift */; };
179180
84C94C8027567D3F007FE2B9 /* ChatChannelListViewModel_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84C94C7F27567D3F007FE2B9 /* ChatChannelListViewModel_Tests.swift */; };
180181
84C94CCC27578B92007FE2B9 /* ChatClientUpdater_Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84C94CAA27578B92007FE2B9 /* ChatClientUpdater_Mock.swift */; };
@@ -482,6 +483,7 @@
482483
84B288D0274CEDD000DD090B /* GroupNameView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupNameView.swift; sourceTree = "<group>"; };
483484
84B288D2274D23AF00DD090B /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = "<group>"; };
484485
84B288D4274D286500DD090B /* LoginViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginViewModel.swift; sourceTree = "<group>"; };
486+
84B55F692798154C00B99B01 /* MessageListConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageListConfig.swift; sourceTree = "<group>"; };
485487
84C2042227917B6A0024D616 /* MessageListView_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageListView_Tests.swift; sourceTree = "<group>"; };
486488
84C94C7E27567D3F007FE2B9 /* StreamChatSwiftUITests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "StreamChatSwiftUITests-Bridging-Header.h"; sourceTree = "<group>"; };
487489
84C94C7F27567D3F007FE2B9 /* ChatChannelListViewModel_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatChannelListViewModel_Tests.swift; sourceTree = "<group>"; };
@@ -830,6 +832,7 @@
830832
8465FD0A2746A95600AF091E /* SystemMessageView.swift */,
831833
842383E327678A4D00888CFC /* QuotedMessageView.swift */,
832834
841B2EF3278DB9E500ED619E /* MessageListHelperViews.swift */,
835+
84B55F692798154C00B99B01 /* MessageListConfig.swift */,
833836
);
834837
path = MessageList;
835838
sourceTree = "<group>";
@@ -1444,6 +1447,7 @@
14441447
8465FDC72746A95700AF091E /* ChatChannelListViewModel.swift in Sources */,
14451448
8465FDD02746A95700AF091E /* DefaultViewFactory.swift in Sources */,
14461449
8465FD822746A95700AF091E /* LinkTextView.swift in Sources */,
1450+
84B55F6A2798154C00B99B01 /* MessageListConfig.swift in Sources */,
14471451
841B64CC2775C6300016FF3B /* CommandsConfig.swift in Sources */,
14481452
8465FD6D2746A95700AF091E /* ViewModelsFactory.swift in Sources */,
14491453
8465FDBA2746A95700AF091E /* BundleExtensions.swift in Sources */,

StreamChatSwiftUITests/Tests/ChatChannel/MessageListView_Tests.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,6 @@ class MessageListView_Tests: XCTestCase {
6464
showScrollToLatestButton: .constant(false),
6565
quotedMessage: .constant(nil),
6666
currentDateString: nil,
67-
isGroup: false,
68-
unreadCount: 0,
6967
listId: "listId",
7068
isMessageThread: false,
7169
onMessageAppear: { _ in },

0 commit comments

Comments
 (0)