Custom Message Actions

Message actions pop up in message overlay, when you long-press a message. We have provided a granular control over these actions. By default we render the following actions (as shown in screenshots)

Message Actions

My Message Actions

Actions on received messageActions on my message

Every message action that you see in UI, is represented by MessageAction object for that action. And MessageAction object provides all the necessary inputs (title, icon, action handler, actionType) for rendering the action button.

type MessageAction = {
  action: () => void;
  actionType: enum('blockUser', 'copyMessage', 'deleteMessage', 'editMessage', 'flagMessage', 'muteUser', 'pinMessage', 'selectReaction', 'reply', 'retry', 'quotedReply', 'threadReply', 'unpinMessage')
  title: string;
  icon?: React.ReactElement;
  titleStyle?: StyleProp<TextStyle>;
};

You can customize each one of the default actions using props on the Channel component as shown further on this page. The channel component makes these props available in the MessagesContext context.

The channel component accepts a prop called messageActions. You can use this prop as a callback function to render message actions selectively.

The arguments to this function is an object with all the default message actions as MessageAction objects. The function should return an array of MessageAction objects to render in a MessageActionList within the message overlay, that is shown when a user long presses a message in a MessageList.

You can also customize each one of the default actions using the messageActions prop passed to the OverlayProvider as shown in the example below. The OverlayProvider component makes these props available in the MessageOverlayContext context.

messageActions={({
  blockUser, // MessageAction | null;
  copyMessage, // MessageAction | null;
  deleteMessage, // MessageAction | null;
  dismissOverlay, // () => void;
  editMessage, // MessageAction | null;
  error, // boolean;
  flagMessage, // MessageAction | null;
  isMessageActionsVisible, // boolean;
  isMyMessage, // boolean;
  isThreadMessage, // boolean;
  message, // MessageType<At, Ch, Co, Ev, Me, Re, Us>;
  reply, // MessageAction | null;
  retry, // MessageAction | null;
  threadReply, // MessageAction | null;
  ownCapabilities, // object
}) => {
  return [] // Array<MessageAction>
}}

Message Action Object type

MessageAction

When you long press a message, it opens up a message overlay and renders all the actions available on message. MessageAction is an object consisting of all the parameters required to render a single action button, in message overlay.

Example

{
  action: () => { /** Some action here */ },
  icon: <PinIcon />,
  title: 'Pin Message',
  titleStyle: { color: 'red' }
}

Keys and Values

action

Callback when user presses the action button.

Type
Function

actionType

Type of the action performed.

Type
enum(‘blockUser’, ‘copyMessage’, ‘deleteMessage’, ‘editMessage’, ‘flagMessage’, ‘muteUser’, ‘pinMessage’, ‘selectReaction’, ‘reply’, ‘retry’, ‘quotedReply’, ‘threadReply’, ‘unpinMessage’)

icon

Element to render as icon for action button.

Type
React Element

title

Title for action button.

Type
String

titleStyle

Styles for underlying Text component of action title.

MessageTouchableHandlerPayload

MessageTouchableHandlerPayload object is provided as parameter to callback handlers such as onLongPressMessageonPressMessage for user interaction with message.

Example

{
  actionHandlers: {
    deleteMessage: [function],
    editMessage: [function],
    quotedReply: [function],
    resendMessage: [function],
    showMessageOverlay: [function],
    toggleBanUser: [function],
    toggleMuteUser: [function],
    toggleReaction: [function],
  },
  additionalInfo: [object],
  message: [message object],
}

Keys and values

additionalInfo

Additional message touchable handler info.

Type
Object

actionHandlers

Set of action handler functions for various message actions. You can use these functions to perform any action when give interaction occurs.

  • deleteMessage
  • editMessage
  • quotedReply
  • resendMessage
  • showMessageOverlay
  • toggleBanUser
  • toggleMuteUser
  • toggleReaction (takes reactionType as parameter)
<Channel onLongPressMessage={({ defaultHandlers }) => defaultHandlers.quotedReply()}
Type
Object

message

Message object, on which interaction occurred.

<Channel onLongPressMessage={({ message }) => console.log(message.id, message.text)}
Type
Object

How to conditionally render message actions

The following example demonstrates how to:

  • Only show the “Copy Message” and “Edit Message” actions from the default message actions.
  • Show “Edit Message” messages from the current user.

Additionally, the following example demonstrates how you can add custom styles for a message action title.

<Channel
  messageActions={({ copyMessage, editMessage, isMyMessage }) =>
    isMyMessage
      ? [
          copyMessage,
          editMessage,
          {
            ...deleteMessage,
            textStyle: {
              color: "red",
              fontWeight: "bold",
            },
          },
        ]
      : [copyMessage]
  }
>
  {/** MessageList and MessageInput component here */}
</Channel>

How to add a custom message action

  • Add a new custom action - “Mute User”
  • Show “Mute User” action only for messages from other user.
import {
  messageActions as defaultMessageActions,
  Mute as MuteIcon,
} from "stream-chat-react-native";

<Channel
  channel={channel}
  messageActions={(param) => {
    const { isMyMessage, ownCapabilities, dismissOverlay } = param;
    const actions = defaultMessageActions({ ...param });

    if (!isMyMessage) {
      const isMuted = (client.mutedUsers || []).some(
        (mute) =>
          mute.user.id === client.userID && mute.target.id === message.user?.id,
      );

      actions.push({
        action: async () => {
          dismissOverlay();
          if (message.user?.id) {
            if (isMuted) {
              await client.unmuteUser(message.user.id);
            } else {
              await client.muteUser(message.user.id);
            }
          }
        },
        actionType: "muteUser",
        icon: <MuteIcon />,
        title: isMuted ? t("Unmute User") : t("Mute User"),
      });
    }

    return actions;
  }}
>
  {/** MessageList and MessageInput component here */}
</Channel>;

How to customize message action UI

OverlayProvider component accepts props called - MessageActionList and MessageActionListItem. They both serve a different purpose.

  • MessageActionList - Allows full customizability of the message action list and allows users to add/define their own message action along with the style they prefer for the application.
  • MessageActionListItem - Allows customizability of an item in a message action list.

Customize a Message Action List

An example for the usage of MessageActionList component is as follows. You can obviously have your own logic in the component.

import {
  MessageActionListItem,
  OverlayProvider,
  useOverlayContext,
} from "stream-chat-react-native";

const CustomMessageActionList = () => {
  const { setOverlay } = useOverlayContext();
  const messageActions = [
    {
      action: function () {
        Alert.alert("Edit Message action called.");
        setOverlay("none");
      },
      actionType: "editMessage",
      title: "Edit messagee",
    },
    {
      action: function () {
        Alert.alert("Delete message action");
        setOverlay("none");
      },
      actionType: "deleteMessage",
      title: "Delete Message",
    },
  ];
  return (
    <View style={{ backgroundColor: "white" }}>
      {messageActions.map(({ actionType, ...rest }) => (
        <MessageActionListItem
          actionType={actionType}
          key={actionType}
          {...rest}
        />
      ))}
    </View>
  );
};

<OverlayProvider MessageActionList={CustomMessageActionList}>
  {/* Underlying Channel, MessageList and Message components */}
</OverlayProvider>;

Notice that the MessageActionList is a simple prop which just accepts your own component for the message action overlay. The content, styles and the action logic are all defined by the user itself.

Customize Message Action list item

Suppose you want to customise pinMessage and muteUser actions, you just need to add a condition for that particular action type and return a component which you want to render for that actionType.

The types of actions which are available by default are as follows:

  • blockUser
  • copyMessage
  • deleteMessage
  • editMessage
  • flagMessage
  • muteUser
  • pinMessage
  • selectReaction
  • reply
  • retry
  • quotedReply
  • threadReply
  • unpinMessage

If you don’t have any condition you need to return the MessageActionListItem as a default component and pass the action props to it so that it will be rendered.

You can use these props to provide your own component.

An example for the MessageActionListItem component customization is as following:

import {
  MessageActionListItem,
  OverlayProvider,
  useMessageActionAnimation,
} from "stream-chat-react-native";

const CustomMessageActionListItem = ({ action, actionType, ...rest }) => {
  const { onTap } = useMessageActionAnimation({ action: action });
  if (actionType === "pinMessage") {
    return (
      <TapGestureHandler onHandlerStateChange={onTap}>
        <Animated.View>
          <Text>{actionType}</Text>
        </Animated.View>
      </TapGestureHandler>
    );
  } else if (actionType === "muteUser") {
    return (
      <TapGestureHandler onHandlerStateChange={onTap}>
        <Animated.View>
          <Text>{actionType}</Text>
        </Animated.View>
      </TapGestureHandler>
    );
  } else {
    return (
      <MessageActionListItem
        action={action}
        actionType={actionType}
        {...rest}
      />
    );
  }
};

<OverlayProvider MessageActionListItem={CustomMessageActionListItem}>
  {/* Underlying Channel, MessageList and Message components */}
</OverlayProvider>;

Note: The useMessageActionAnimation hook takes in the action for the message action and this would give some utilities like ontap, animatedStyle and the opacity value which would be helpful with the Animated View which can be used in support of the package react-native-reanimated.

Please continue reading further to see different use cases.

How to intercept a message action

If you are looking to add some analytics tracking to message action, you can do so in following handler prop functions. These functions will be called right before the underlying default handlers.

Please note that these intercepts will neither change the standard functions nor block them.

  • handleBan
  • handleBlock(deprecated)
  • handleCopy
  • handleDelete
  • handleEdit
  • handleFlag
  • handleMute
  • handleReaction
  • handleReply
  • handleRetry
  • handleThreadReply

Following example demonstrates how to add analytics tracking to “Copy Message” action.

<Channel handleCopy={() => trackCopyAction()} />

How to disable a message action

To disable a particular action you can return null for a particular action type in the MessageActionListItem prop. An example to the situation would be as follows:

import {
  MessageActionListItem,
  OverlayProvider,
  useMessageActionAnimation,
} from "stream-chat-react-native";

const CustomMessageActionListItem = ({ action, actionType, ...rest }) => {
  if (actionType === "pinMessage") {
    return null;
  } else {
    return (
      <MessageActionListItem
        action={action}
        actionType={actionType}
        {...rest}
      />
    );
  }
};

<OverlayProvider MessageActionListItem={CustomMessageActionListItem}>
  {/* Underlying Channel, MessageList and Message components */}
</OverlayProvider>;
© Getstream.io, Inc. All Rights Reserved.