# 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/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 `Styles`. 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)
                        }
                    }
                )
            }
    }
}

final class CustomStyles: Styles {
    var composerPlacement: ComposerPlacement = .docked

    func makeBouncedMessageActionsModifier(viewModel: ChatChannelViewModel) -> some ViewModifier {
        CustomBouncedMessageActionsModifier(viewModel: viewModel)
    }

    func makeComposerInputViewModifier(options: ComposerInputModifierOptions) -> some ViewModifier {
        RegularInputViewModifier()
    }

    func makeComposerButtonViewModifier(options: ComposerButtonModifierOptions) -> some ViewModifier {
        RegularButtonViewModifier()
    }

    func makeSuggestionsContainerModifier(options: SuggestionsContainerModifierOptions) -> some ViewModifier {
        SuggestionsRegularContainerModifier()
    }
}

class CustomViewFactory: ViewFactory {
    @Injected(\.chatClient) public var chatClient
    public var styles = CustomStyles()
}
```

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 `options.message.isBounced` to return different actions for bounced messages.

## Flag Action

The message actions are provided by the `supportedMessageActions` closure of `MessageListConfig`. The default flag action has the `MessageActionId.flag` identifier, so you can remove it or replace it with your own action:

```swift
let utils = Utils(
    messageListConfig: MessageListConfig(
        supportedMessageActions: { options in
            var actions = MessageAction.defaultActions(for: options)
            guard let index = actions.firstIndex(where: { $0.id == MessageActionId.flag }) else {
                return actions
            }

            let chatClient = InjectedValues[\.chatClient]
            let messageController = chatClient.messageController(
                cid: options.channel.cid,
                messageId: options.message.id
            )
            actions[index] = MessageAction(
                id: MessageActionId.flag,
                title: "Report",
                iconName: "exclamationmark.bubble",
                action: {
                    messageController.flag(reason: "inappropriate") { error in
                        if let error {
                            options.onError(error)
                        } else {
                            options.onFinish(MessageActionInfo(message: options.message, identifier: "flag"))
                        }
                    }
                },
                confirmationPopup: ConfirmationPopup(
                    title: "Report message?",
                    message: "Our moderators will review this message.",
                    buttonTitle: "Report"
                ),
                isDestructive: true
            )
            return actions
        }
    )
)
let streamChat = StreamChat(chatClient: chatClient, utils: utils)
```

To hide the flag action, filter it out instead: `MessageAction.defaultActions(for: options).filter { $0.id != MessageActionId.flag }`.

For more details, see [View Customizations](https://getstream.io/chat/docs/sdk/ios/swiftui/view-customizations/) and [Message Reactions](https://getstream.io/chat/docs/sdk/ios/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`:

| 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/swiftui/localization/) for more details.

---

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