Skip to content

Commit c2a6f8c

Browse files
added docs for message reactions
1 parent 3de5557 commit c2a6f8c

File tree

7 files changed

+259
-4
lines changed

7 files changed

+259
-4
lines changed

Sources/StreamChatSwiftUI/ChatChannel/ChatChannelView.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,7 @@ public struct ChatChannelView<Factory: ViewFactory>: View {
6363
.accentColor(colors.tintColor)
6464
.overlay(
6565
viewModel.reactionsShown ?
66-
ReactionsOverlayView(
67-
factory: factory,
66+
factory.makeReactionsOverlayView(
6867
currentSnapshot: viewModel.currentSnapshot!,
6968
messageDisplayInfo: messageDisplayInfo!,
7069
onBackgroundTap: {

Sources/StreamChatSwiftUI/ChatChannel/MessageList/MessageContainerView.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@ struct MessageContainerView<Factory: ViewFactory>: View {
4646
isFirst: showsAllInfo
4747
)
4848
.overlay(
49-
reactionsShown ? ReactionsContainer(message: message) : nil
49+
reactionsShown ?
50+
factory.makeMessageReactionView(message: message)
51+
: nil
5052
)
5153
.background(
5254
GeometryReader { proxy in

Sources/StreamChatSwiftUI/DefaultViewFactory.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,25 @@ extension ViewFactory {
398398

399399
return MessageActionsView(messageActions: messageActions)
400400
}
401+
402+
public func makeMessageReactionView(
403+
message: ChatMessage
404+
) -> some View {
405+
ReactionsContainer(message: message)
406+
}
407+
408+
public func makeReactionsOverlayView(
409+
currentSnapshot: UIImage,
410+
messageDisplayInfo: MessageDisplayInfo,
411+
onBackgroundTap: @escaping () -> Void
412+
) -> some View {
413+
ReactionsOverlayView(
414+
factory: self,
415+
currentSnapshot: currentSnapshot,
416+
messageDisplayInfo: messageDisplayInfo,
417+
onBackgroundTap: onBackgroundTap
418+
)
419+
}
401420
}
402421

403422
/// Default class conforming to `ViewFactory`, used throughout the SDK.

Sources/StreamChatSwiftUI/ViewFactory.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,4 +387,25 @@ public protocol ViewFactory: AnyObject {
387387
onDismiss: @escaping () -> Void,
388388
onError: @escaping (Error) -> Void
389389
) -> MessageActionsViewType
390+
391+
associatedtype MessageReactionViewType
392+
/// Creates the reactions view shown above the message.
393+
/// - Parameter message: the message for which reactions are shown.
394+
/// - Returns: view shown in the message reactions slot.
395+
func makeMessageReactionView(
396+
message: ChatMessage
397+
) -> MessageReactionViewType
398+
399+
associatedtype ReactionsOverlayViewType
400+
/// Creates the reactions overlay view.
401+
/// - Parameters:
402+
/// - currentSnapshot: current snapshot of the screen (in case blur effect is needed).
403+
/// - messageDisplayInfo: information about the displayed message.
404+
/// - onBackgroundTap: called when the background is tapped (to dismiss the view).
405+
/// - Returns: view displayed in the reactions overlay slot.
406+
func makeReactionsOverlayView(
407+
currentSnapshot: UIImage,
408+
messageDisplayInfo: MessageDisplayInfo,
409+
onBackgroundTap: @escaping () -> Void
410+
) -> ReactionsOverlayViewType
390411
}
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
---
2+
title: Message reactions
3+
---
4+
5+
## Reactions overview
6+
7+
The SwiftUI chat SDK provides a default view that's displayed as an overlay of the message. When you long press on a message, the message reactions overlay is shown. By default, it shows a blurred background and a possibility to react to a message or remove a reaction. Additionally, the reactions overlay has a "message actions" slot, which allows you to perform actions on the message. Both the displayed reactions on a message and the reactions overlay can be replaced by your own views.
8+
9+
## Customizing the message reactions view
10+
11+
The simplest way to customize the message reactions view is to replace its reaction icons. Those are available under the `availableReactions` property in the Images class, which is part of the Appearance class in the StreamChat object. The `availableReactions` property is a dictionary, which contains mappings between `MessageReactionType` and its corresponding `ChatMessageReactionAppearanceType`, which consists of small and large icon for a reaction. If you change these properties, make sure to inject the updated `Images` class in the StreamChat object.
12+
13+
```swift
14+
let customReactions = [.init(rawValue: "custom"): ChatMessageReactionAppearance(
15+
smallIcon: reactionCustomSmall,
16+
largeIcon: reactionCustomBig
17+
)]
18+
var images = Images()
19+
images.availableReactions = customReactions
20+
let appearance = Appearance(images: images)
21+
22+
streamChat = StreamChat(chatClient: chatClient, appearance: appearance)
23+
```
24+
25+
## Changing the message reactions view
26+
27+
Alternatively, you can completely swap the `ReactionsContainer` view with your own implementation. In order to do that, you need to implement the `makeMessageReactionView` method from the `ViewFactory`, which is called with the message as a parameter.
28+
29+
```swift
30+
public func makeMessageReactionView(
31+
message: ChatMessage
32+
) -> some View {
33+
CustomReactionsContainer(message: message)
34+
}
35+
```
36+
37+
## Customizing the reactions overlay view
38+
39+
The reactions overlay view (shown on long press of a message), also provides access to the message actions. If you want to replace / extend the default message actions, you can do so via the `suppotedMessageActions` method in the `ViewFactory`. As an inspiration, here's a glimpse on how the default message actions are configured.
40+
41+
```swift
42+
public func suppotedMessageActions(
43+
for message: ChatMessage,
44+
onDismiss: @escaping () -> Void,
45+
onError: @escaping (Error) -> Void
46+
) -> [MessageAction] {
47+
MessageAction.defaultActions(
48+
for: message,
49+
chatClient: chatClient,
50+
onDismiss: onDismiss,
51+
onError: onError
52+
)
53+
}
54+
55+
extension MessageAction {
56+
/// Returns the default message actions.
57+
///
58+
/// - Parameters:
59+
/// - message: the current message.
60+
/// - chatClient: the chat client.
61+
/// - onDimiss: called when the action is executed.
62+
/// - Returns: array of `MessageAction`.
63+
public static func defaultActions(
64+
for message: ChatMessage,
65+
chatClient: ChatClient,
66+
onDismiss: @escaping () -> Void,
67+
onError: @escaping (Error) -> Void
68+
) -> [MessageAction] {
69+
guard let channelId = message.cid else {
70+
return []
71+
}
72+
73+
var messageActions = [MessageAction]()
74+
75+
if message.isSentByCurrentUser {
76+
let deleteAction = deleteMessageAction(
77+
for: message,
78+
channelId: channelId,
79+
chatClient: chatClient,
80+
onDismiss: onDismiss,
81+
onError: onError
82+
)
83+
84+
messageActions.append(deleteAction)
85+
} else {
86+
let flagAction = flagMessageAction(
87+
for: message,
88+
channelId: channelId,
89+
chatClient: chatClient,
90+
onDismiss: onDismiss,
91+
onError: onError
92+
)
93+
94+
messageActions.append(flagAction)
95+
}
96+
97+
return messageActions
98+
}
99+
100+
private static func deleteMessageAction(
101+
for message: ChatMessage,
102+
channelId: ChannelId,
103+
chatClient: ChatClient,
104+
onDismiss: @escaping () -> Void,
105+
onError: @escaping (Error) -> Void
106+
) -> MessageAction {
107+
let messageController = chatClient.messageController(
108+
cid: channelId,
109+
messageId: message.id
110+
)
111+
112+
let deleteAction = {
113+
messageController.deleteMessage { error in
114+
if let error = error {
115+
onError(error)
116+
} else {
117+
onDismiss()
118+
}
119+
}
120+
}
121+
122+
let confirmationPopup = ConfirmationPopup(
123+
title: L10n.Message.Actions.Delete.confirmationTitle,
124+
message: L10n.Message.Actions.Delete.confirmationMessage,
125+
buttonTitle: L10n.Message.Actions.delete
126+
)
127+
128+
let deleteMessage = MessageAction(
129+
title: L10n.Message.Actions.delete,
130+
iconName: "trash",
131+
action: deleteAction,
132+
confirmationPopup: confirmationPopup,
133+
isDestructive: true
134+
)
135+
136+
return deleteMessage
137+
}
138+
139+
private static func flagMessageAction(
140+
for message: ChatMessage,
141+
channelId: ChannelId,
142+
chatClient: ChatClient,
143+
onDismiss: @escaping () -> Void,
144+
onError: @escaping (Error) -> Void
145+
) -> MessageAction {
146+
let messageController = chatClient.messageController(
147+
cid: channelId,
148+
messageId: message.id
149+
)
150+
151+
let flagAction = {
152+
messageController.flag { error in
153+
if let error = error {
154+
onError(error)
155+
} else {
156+
onDismiss()
157+
}
158+
}
159+
}
160+
161+
let confirmationPopup = ConfirmationPopup(
162+
title: L10n.Message.Actions.Flag.confirmationTitle,
163+
message: L10n.Message.Actions.Flag.confirmationMessage,
164+
buttonTitle: L10n.Message.Actions.flag
165+
)
166+
167+
let flageMessage = MessageAction(
168+
title: L10n.Message.Actions.flag,
169+
iconName: "flag",
170+
action: flagAction,
171+
confirmationPopup: confirmationPopup,
172+
isDestructive: false
173+
)
174+
175+
return flageMessage
176+
}
177+
}
178+
```
179+
180+
Alternatively, you can swap the whole `MessageActionsView` with your own implementation, by implementing the `makeMessageActionsView` method in the `ViewFactory`.
181+
182+
```swift
183+
public func makeMessageActionsView(
184+
for message: ChatMessage,
185+
onDismiss: @escaping () -> Void,
186+
onError: @escaping (Error) -> Void
187+
) -> some View {
188+
let messageActions = suppotedMessageActions(
189+
for: message,
190+
onDismiss: onDismiss,
191+
onError: onError
192+
)
193+
194+
return MessageActionsView(messageActions: messageActions)
195+
}
196+
```
197+
198+
Additionally, you can swap the whole `ReactionsOverlayView` with your own implementation. In order to do this, you need to implement the `makeReactionsOverlayView` method in the `ViewFactory`. The current snapshot of the message list is provided to you, in case you want to blur it or apply any other effects.
199+
200+
```swift
201+
public func makeReactionsOverlayView(
202+
currentSnapshot: UIImage,
203+
messageDisplayInfo: MessageDisplayInfo,
204+
onBackgroundTap: @escaping () -> Void
205+
) -> some View {
206+
ReactionsOverlayView(
207+
factory: self,
208+
currentSnapshot: currentSnapshot,
209+
messageDisplayInfo: messageDisplayInfo,
210+
onBackgroundTap: onBackgroundTap
211+
)
212+
}
213+
```

docusaurus/sidebars-ios.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@
6161
"swiftui/components/custom-avatar",
6262
"swiftui/components/channel-header",
6363
"swiftui/components/attachments",
64-
"swiftui/components/message-composer"
64+
"swiftui/components/message-composer",
65+
"swiftui/components/message-reactions"
6566
]
6667
}
6768
]

0 commit comments

Comments
 (0)