Message Moderation
Stream Chat can moderate messages before they are delivered (bounce) and lets users report messages to moderators (flag). This page shows how to customize the SwiftUI components for moderated messages. For how moderated messages are represented in the SDK, see the Moderation guide.
Bounced Messages
A bounced message is displayed with an error indicator next to it. When the user long presses it, the SDK shows an alert with the Send Anyway, Edit Message and Delete Message actions. This is controlled by the bouncedMessagesAlertActionsEnabled flag of MessageListConfig, which is true by default.
The alert is added by the BouncedMessageActionsModifier. To provide your own UI, implement makeBouncedMessageActionsModifier in your custom ViewFactory. The ChatChannelViewModel exposes the bounced message and the actions that can be performed on it:
struct CustomBouncedMessageActionsModifier: ViewModifier {
@ObservedObject var viewModel: ChatChannelViewModel
func body(content: Content) -> some View {
content
.alert(isPresented: $viewModel.bouncedActionsViewShown) {
Alert(
title: Text("Message not sent"),
message: Text("This message goes against our community guidelines."),
primaryButton: .default(Text("Edit Message")) {
if let message = viewModel.bouncedMessage {
viewModel.editMessage(message)
}
},
secondaryButton: .destructive(Text("Delete Message")) {
if let message = viewModel.bouncedMessage {
viewModel.deleteMessage(message)
}
}
)
}
}
}
class CustomViewFactory: ViewFactory {
@Injected(\.chatClient) public var chatClient
func makeBouncedMessageActionsModifier(viewModel: ChatChannelViewModel) -> some ViewModifier {
CustomBouncedMessageActionsModifier(viewModel: viewModel)
}
}If you set bouncedMessagesAlertActionsEnabled to false, long pressing a bounced message opens the regular message actions overlay instead. In this case, the actions are provided by supportedMessageActions, as described in the next section, and you can check message.isBounced to return different actions for bounced messages.
Flag Action
The message actions are provided by the supportedMessageActions method of the ViewFactory. The default flag action has the MessageActionId.flag identifier, so you can remove it or replace it with your own action:
class CustomViewFactory: ViewFactory {
@Injected(\.chatClient) public var chatClient
func supportedMessageActions(
for message: ChatMessage,
channel: ChatChannel,
onFinish: @escaping (MessageActionInfo) -> Void,
onError: @escaping (Error) -> Void
) -> [MessageAction] {
var actions = MessageAction.defaultActions(
factory: self,
for: message,
channel: channel,
chatClient: chatClient,
onFinish: onFinish,
onError: onError
)
guard let index = actions.firstIndex(where: { $0.id == MessageActionId.flag }) else {
return actions
}
let messageController = chatClient.messageController(
cid: channel.cid,
messageId: message.id
)
actions[index] = MessageAction(
id: MessageActionId.flag,
title: "Report",
iconName: "exclamationmark.bubble",
action: {
messageController.flag(reason: "inappropriate") { error in
if let error {
onError(error)
} else {
onFinish(MessageActionInfo(message: message, identifier: "flag"))
}
}
},
confirmationPopup: ConfirmationPopup(
title: "Report message?",
message: "Our moderators will review this message.",
buttonTitle: "Report"
),
isDestructive: true
)
return actions
}
}To hide the flag action, filter it out instead: .filter { $0.id != MessageActionId.flag }.
For more details, see View Customizations and Message Reactions.
Changing the Texts
If you only need to change the texts, override the following keys in your app's Localizable.strings. By default, the SDK reads strings only from its own bundle, so you also need to set localizationProvider. The following example reads from your app's bundle first and falls back to the SDK strings for keys you didn't override:
let sdkLocalizationProvider = Appearance.localizationProvider
Appearance.localizationProvider = { key, table in
let value = Bundle.main.localizedString(forKey: key, value: key, table: table)
return value == key ? sdkLocalizationProvider(key, table) : value
}The keys used by the moderation components are:
| Key | Default value |
|---|---|
message.moderation.alert.title |
Are you sure? |
message.moderation.alert.message |
Consider how your comment might... |
message.moderation.alert.resend |
Send Anyway |
message.moderation.alert.edit |
Edit Message |
message.moderation.alert.delete |
Delete Message |
message.bounce.title |
Message was bounced |
message.actions.flag |
Flag Message |
message.actions.flag.confirmation-title |
Flag Message |
message.actions.flag.confirmation-message |
Do you want to send a copy of... |
See the localization guide for more details.