# 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](https://getstream.io/chat/docs/sdk/ios/v4/guides/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:

```swift
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:

```swift
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](https://getstream.io/chat/docs/sdk/ios/v4/swiftui/view-customizations/) and [Message Reactions](https://getstream.io/chat/docs/sdk/ios/v4/swiftui/message-components/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:

```swift
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](https://getstream.io/chat/docs/sdk/ios/v4/swiftui/localization/) for more details.

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/ios/v4/swiftui/message-components/message-moderation/](https://getstream.io/chat/docs/sdk/ios/v4/swiftui/message-components/message-moderation/).