Skip to content

Commit 92aa8e9

Browse files
authored
Fix ViewFactory.makeMessageAvatarView() not used in some views (#1068)
* Use `ViewFactory` avatar view in remaining common views * Add default view factory to avoid breaking changes * Add ChatThreadListItem too * Update CHANGELOG.md * Fix ThreadListItem Tests * Fix MessageRepliesView Tests * Fix E2E Tests
1 parent 6d0cda2 commit 92aa8e9

File tree

15 files changed

+231
-64
lines changed

15 files changed

+231
-64
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
88
### 🐞 Fixed
99
- Use `muteChannel` capability for showing mute channel button in the `ChatChannelInfoView` [#1018](https://github.com/GetStream/stream-chat-swiftui/pull/1018)
1010
- Fix `PollOptionAllVotesViewModel` not loading more votes [#1067](https://github.com/GetStream/stream-chat-swiftui/pull/1067)
11+
- Fix `ViewFactory.makeMessageAvatarView()` not used in some views [#1068](https://github.com/GetStream/stream-chat-swiftui/pull/1068)
12+
- `MessageRepliesView`
13+
- `ReactionsUsersView`
14+
- `MentionUsersView`
15+
- `ParticipantInfoView`
16+
- `ChatThreadListItem`
1117

1218
# [4.94.0](https://github.com/GetStream/stream-chat-swiftui/releases/tag/4.94.0)
1319
_December 02, 2025_

Sources/StreamChatSwiftUI/ChatChannel/ChannelInfo/ChatChannelInfoView.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ public struct ChatChannelInfoView<Factory: ViewFactory>: View, KeyboardReadable
132132

133133
if let selectedParticipant = viewModel.selectedParticipant {
134134
ParticipantInfoView(
135+
factory: factory,
135136
participant: selectedParticipant,
136137
actions: viewModel.participantActions(for: selectedParticipant)
137138
) {

Sources/StreamChatSwiftUI/ChatChannel/ChannelInfo/ParticipantInfoView.swift

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,28 @@
44

55
import SwiftUI
66

7-
struct ParticipantInfoView: View {
7+
struct ParticipantInfoView<Factory: ViewFactory>: View {
88
@Injected(\.fonts) var fonts
99
@Injected(\.colors) var colors
1010

11+
var factory: Factory
1112
let participant: ParticipantInfo
1213
var actions: [ParticipantAction]
1314

1415
var onDismiss: () -> Void
1516

17+
init(
18+
factory: Factory = DefaultViewFactory.shared,
19+
participant: ParticipantInfo,
20+
actions: [ParticipantAction],
21+
onDismiss: @escaping () -> Void
22+
) {
23+
self.factory = factory
24+
self.participant = participant
25+
self.actions = actions
26+
self.onDismiss = onDismiss
27+
}
28+
1629
@State private var alertShown = false
1730
@State private var alertAction: ParticipantAction? {
1831
didSet {
@@ -31,13 +44,15 @@ struct ParticipantInfoView: View {
3144
.font(fonts.footnote)
3245
.foregroundColor(Color(colors.textLowEmphasis))
3346

34-
MessageAvatarView(
35-
avatarURL: participant.chatUser.imageURL,
36-
size: CGSize(width: 64, height: 64),
37-
showOnlineIndicator: participant.chatUser.isOnline
47+
let displayInfo = UserDisplayInfo(
48+
id: participant.chatUser.id,
49+
name: participant.chatUser.name ?? participant.chatUser.id,
50+
imageURL: participant.chatUser.imageURL,
51+
size: CGSize(width: 64, height: 64)
3852
)
39-
.padding()
40-
53+
factory.makeMessageAvatarView(for: displayInfo)
54+
.padding()
55+
4156
VStack {
4257
ForEach(actions) { action in
4358
Divider()

Sources/StreamChatSwiftUI/ChatChannel/Composer/Suggestions/CommandsContainerView.swift

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,26 @@ import StreamChat
66
import SwiftUI
77

88
/// Default implementation of the commands container.
9-
struct CommandsContainerView: View {
9+
struct CommandsContainerView<Factory: ViewFactory>: View {
10+
var factory: Factory
1011
var suggestions: [String: Any]
1112
var handleCommand: ([String: Any]) -> Void
1213

14+
init(
15+
factory: Factory = DefaultViewFactory.shared,
16+
suggestions: [String: Any],
17+
handleCommand: @escaping ([String: Any]) -> Void
18+
) {
19+
self.factory = factory
20+
self.suggestions = suggestions
21+
self.handleCommand = handleCommand
22+
}
23+
1324
var body: some View {
1425
ZStack {
1526
if let suggestedUsers = suggestions["mentions"] as? [ChatUser] {
1627
MentionUsersView(
28+
factory: factory,
1729
users: suggestedUsers,
1830
userSelected: { user in
1931
handleCommand(["chatUser": user])

Sources/StreamChatSwiftUI/ChatChannel/Composer/Suggestions/Mentions/MentionUsersView.swift

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,31 @@ import StreamChat
66
import SwiftUI
77

88
/// View for the mentioned users.
9-
public struct MentionUsersView: View {
9+
public struct MentionUsersView<Factory: ViewFactory>: View {
1010
@Injected(\.colors) private var colors
1111

12+
var factory: Factory
1213
private let itemHeight: CGFloat = 60
1314

1415
var users: [ChatUser]
1516
var userSelected: (ChatUser) -> Void
1617

18+
public init(
19+
factory: Factory = DefaultViewFactory.shared,
20+
users: [ChatUser],
21+
userSelected: @escaping (ChatUser) -> Void
22+
) {
23+
self.factory = factory
24+
self.users = users
25+
self.userSelected = userSelected
26+
}
27+
1728
public var body: some View {
1829
ScrollView {
1930
LazyVStack {
2031
ForEach(users) { user in
2132
MentionUserView(
33+
factory: factory,
2234
user: user,
2335
userSelected: userSelected
2436
)
@@ -43,20 +55,33 @@ public struct MentionUsersView: View {
4355
}
4456

4557
/// View for one user that can be mentioned.
46-
public struct MentionUserView: View {
58+
public struct MentionUserView<Factory: ViewFactory>: View {
4759
@Injected(\.fonts) private var fonts
4860
@Injected(\.colors) private var colors
4961
@Injected(\.utils) private var utils
5062

63+
var factory: Factory
5164
var user: ChatUser
5265
var userSelected: (ChatUser) -> Void
5366

67+
public init(
68+
factory: Factory = DefaultViewFactory.shared,
69+
user: ChatUser,
70+
userSelected: @escaping (ChatUser) -> Void
71+
) {
72+
self.factory = factory
73+
self.user = user
74+
self.userSelected = userSelected
75+
}
76+
5477
public var body: some View {
5578
HStack {
56-
MessageAvatarView(
57-
avatarURL: user.imageURL,
58-
showOnlineIndicator: true
79+
let displayInfo = UserDisplayInfo(
80+
id: user.id,
81+
name: user.name ?? user.id,
82+
imageURL: user.imageURL
5983
)
84+
factory.makeMessageAvatarView(for: displayInfo)
6085
Text(user.name ?? user.id)
6186
.lineLimit(1)
6287
.font(fonts.bodyBold)

Sources/StreamChatSwiftUI/ChatChannel/MessageList/MessageRepliesView.swift

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,18 +60,12 @@ public struct MessageRepliesView<Factory: ViewFactory>: View {
6060
} label: {
6161
HStack {
6262
if !isRightAligned {
63-
MessageAvatarView(
64-
avatarURL: message.threadParticipants.first?.imageURL,
65-
size: .init(width: 16, height: 16)
66-
)
63+
messageAvatarView
6764
}
6865
Text(title)
6966
.font(fonts.footnoteBold)
7067
if isRightAligned {
71-
MessageAvatarView(
72-
avatarURL: message.threadParticipants.first?.imageURL,
73-
size: .init(width: 16, height: 16)
74-
)
68+
messageAvatarView
7569
}
7670
}
7771
.padding(.horizontal, 16)
@@ -122,6 +116,28 @@ public struct MessageRepliesView<Factory: ViewFactory>: View {
122116
return L10n.Message.Threads.replies
123117
}
124118
}
119+
120+
private var messageAvatarView: some View {
121+
// This is just a fallback for backwards compatibility
122+
// In practice thread participants will never be empty.
123+
// So, the factory method will always run.
124+
Group {
125+
if let participant = message.threadParticipants.first {
126+
let displayInfo = UserDisplayInfo(
127+
id: participant.id,
128+
name: participant.name ?? participant.id,
129+
imageURL: participant.imageURL,
130+
size: .init(width: 16, height: 16)
131+
)
132+
factory.makeMessageAvatarView(for: displayInfo)
133+
} else {
134+
MessageAvatarView(
135+
avatarURL: message.threadParticipants.first?.imageURL,
136+
size: .init(width: 16, height: 16)
137+
)
138+
}
139+
}
140+
}
125141
}
126142

127143
/// Lazy view that uses the message controller to fetch the parent message before creating message replies view.

Sources/StreamChatSwiftUI/ChatChannel/Reactions/ReactionUserView.swift

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,24 @@ import StreamChat
66
import SwiftUI
77

88
/// View displaying single user reaction.
9-
struct ReactionUserView: View {
9+
struct ReactionUserView<Factory: ViewFactory>: View {
1010
@Injected(\.chatClient) private var chatClient
1111
@Injected(\.fonts) private var fonts
1212

13+
var factory: Factory
1314
var reaction: ChatMessageReaction
1415
var imageSize: CGFloat
1516

17+
init(
18+
factory: Factory = DefaultViewFactory.shared,
19+
reaction: ChatMessageReaction,
20+
imageSize: CGFloat
21+
) {
22+
self.factory = factory
23+
self.reaction = reaction
24+
self.imageSize = imageSize
25+
}
26+
1627
private var isCurrentUser: Bool {
1728
chatClient.currentUserId == reaction.author.id
1829
}
@@ -27,18 +38,20 @@ struct ReactionUserView: View {
2738

2839
var body: some View {
2940
VStack {
30-
MessageAvatarView(
31-
avatarURL: reaction.author.imageURL,
32-
size: CGSize(width: imageSize, height: imageSize),
33-
showOnlineIndicator: false
34-
)
35-
.overlay(
36-
VStack {
37-
Spacer()
38-
SingleReactionView(reaction: reaction)
39-
.frame(height: imageSize / 2)
40-
}
41+
let displayInfo = UserDisplayInfo(
42+
id: reaction.author.id,
43+
name: reaction.author.name ?? reaction.author.id,
44+
imageURL: reaction.author.imageURL,
45+
size: CGSize(width: imageSize, height: imageSize)
4146
)
47+
factory.makeMessageAvatarView(for: displayInfo)
48+
.overlay(
49+
VStack {
50+
Spacer()
51+
SingleReactionView(reaction: reaction)
52+
.frame(height: imageSize / 2)
53+
}
54+
)
4255

4356
Text(authorName)
4457
.multilineTextAlignment(.center)

Sources/StreamChatSwiftUI/ChatChannel/Reactions/ReactionsUsersView.swift

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,30 +6,46 @@ import StreamChat
66
import SwiftUI
77

88
/// View displaying users who have reacted to a message.
9-
struct ReactionsUsersView: View {
9+
struct ReactionsUsersView<Factory: ViewFactory>: View {
1010
@StateObject private var viewModel: ReactionsUsersViewModel
1111

1212
@Injected(\.fonts) private var fonts
1313
@Injected(\.colors) private var colors
1414

15+
var factory: Factory
1516
var maxHeight: CGFloat
1617

17-
private static let columnCount = 4
18-
private static let itemSize: CGFloat = 64
18+
let columnCount = 4
19+
let itemSize: CGFloat = 64
1920

20-
private let columns = Array(
21-
repeating: GridItem(.adaptive(minimum: itemSize), alignment: .top),
22-
count: columnCount
23-
)
24-
25-
init(message: ChatMessage, maxHeight: CGFloat) {
21+
private let columns: [GridItem]
22+
23+
init(
24+
factory: Factory = DefaultViewFactory.shared,
25+
message: ChatMessage,
26+
maxHeight: CGFloat
27+
) {
28+
self.factory = factory
2629
self.maxHeight = maxHeight
2730
_viewModel = StateObject(wrappedValue: ReactionsUsersViewModel(message: message))
31+
self.columns = Array(
32+
repeating: GridItem(.adaptive(minimum: itemSize), alignment: .top),
33+
count: columnCount
34+
)
2835
}
2936

30-
init(viewModel: ReactionsUsersViewModel, maxHeight: CGFloat) {
37+
init(
38+
factory: Factory = DefaultViewFactory.shared,
39+
viewModel: ReactionsUsersViewModel,
40+
maxHeight: CGFloat
41+
) {
42+
self.factory = factory
3143
self.maxHeight = maxHeight
3244
_viewModel = StateObject(wrappedValue: viewModel)
45+
self.columns = Array(
46+
repeating: GridItem(.adaptive(minimum: itemSize), alignment: .top),
47+
count: columnCount
48+
)
3349
}
3450

3551
var body: some View {
@@ -45,13 +61,14 @@ struct ReactionsUsersView: View {
4561
.fontWeight(.bold)
4662
.padding()
4763

48-
if viewModel.reactions.count > Self.columnCount {
64+
if viewModel.reactions.count > columnCount {
4965
ScrollView {
5066
LazyVGrid(columns: columns, alignment: .center, spacing: 8) {
5167
ForEach(viewModel.reactions) { reaction in
5268
ReactionUserView(
69+
factory: factory,
5370
reaction: reaction,
54-
imageSize: Self.itemSize
71+
imageSize: itemSize
5572
)
5673
}
5774
}
@@ -61,8 +78,9 @@ struct ReactionsUsersView: View {
6178
HStack(alignment: .top, spacing: 0) {
6279
ForEach(viewModel.reactions) { reaction in
6380
ReactionUserView(
81+
factory: factory,
6482
reaction: reaction,
65-
imageSize: Self.itemSize
83+
imageSize: itemSize
6684
)
6785
}
6886
}

0 commit comments

Comments
 (0)