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)
Actions on received message | Actions 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;
isMyMessage, // boolean;
isThreadMessage, // boolean;
message, // MessageType<At, Ch, Co, Ev, Me, Re, Us>;
messageReactions, // boolean;
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.
Type |
---|
Text Style Props |
MessageTouchableHandlerPayload
MessageTouchableHandlerPayload
object is provided as parameter to callback handlers such as onLongPressMessage
, onPressMessage
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
(takesreactionType
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.
handleBlock
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>;