stream-chat-react
Version:
React components to create chat conversations or livestream style chat
121 lines (120 loc) • 7.96 kB
JavaScript
import React, { useCallback, useMemo } from 'react';
import { useActionHandler, useDeleteHandler, useEditHandler, useFlagHandler, useMarkUnreadHandler, useMentionsHandler, useMuteHandler, useOpenThreadHandler, usePinHandler, useReactionHandler, useReactionsFetcher, useRetryHandler, useUserHandler, useUserRole, } from './hooks';
import { areMessagePropsEqual, getMessageActions, MESSAGE_ACTIONS } from './utils';
import { MessageProvider, useChannelActionContext, useChannelStateContext, useChatContext, useComponentContext, } from '../../context';
import { MessageSimple as DefaultMessage } from './MessageSimple';
const MessageWithContext = (props) => {
const { canPin, groupedByUser, Message: propMessage, message, messageActions = Object.keys(MESSAGE_ACTIONS), onUserClick: propOnUserClick, onUserHover: propOnUserHover, userRoles, } = props;
const { client, isMessageAIGenerated } = useChatContext('Message');
const { channelConfig, read } = useChannelStateContext('Message');
const { Message: contextMessage } = useComponentContext('Message');
const actionsEnabled = message.type === 'regular' && message.status === 'received';
const MessageUIComponent = propMessage ?? contextMessage ?? DefaultMessage;
const { clearEdit, editing, setEdit } = useEditHandler();
const { onUserClick, onUserHover } = useUserHandler(message, {
onUserClickHandler: propOnUserClick,
onUserHoverHandler: propOnUserHover,
});
const { canDelete, canEdit, canFlag, canMarkUnread, canMute, canQuote, canReact, canReply, isMyMessage, } = userRoles;
const messageIsUnread = useMemo(() => !!(!isMyMessage &&
client.user?.id &&
read &&
(!read[client.user.id] ||
(message?.created_at &&
new Date(message.created_at).getTime() >
read[client.user.id].last_read.getTime()))), [client, isMyMessage, message.created_at, read]);
const messageActionsHandler = useCallback(() => getMessageActions(messageActions, {
canDelete,
canEdit,
canFlag,
canMarkUnread,
canMute,
canPin,
canQuote,
canReact,
canReply,
}, channelConfig), [
messageActions,
canDelete,
canEdit,
canFlag,
canMarkUnread,
canMute,
canPin,
canQuote,
canReact,
canReply,
channelConfig,
]);
const { canPin: canPinPropToNotPass, // eslint-disable-line @typescript-eslint/no-unused-vars
messageActions: messageActionsPropToNotPass, // eslint-disable-line @typescript-eslint/no-unused-vars
onlySenderCanEdit: onlySenderCanEditPropToNotPass, // eslint-disable-line @typescript-eslint/no-unused-vars
onUserClick: onUserClickPropToNotPass, // eslint-disable-line @typescript-eslint/no-unused-vars
onUserHover: onUserHoverPropToNotPass, // eslint-disable-line @typescript-eslint/no-unused-vars
userRoles: userRolesPropToNotPass, // eslint-disable-line @typescript-eslint/no-unused-vars
...rest } = props;
const messageContextValue = {
...rest,
actionsEnabled,
clearEditingState: clearEdit,
editing,
getMessageActions: messageActionsHandler,
handleEdit: setEdit,
isMessageAIGenerated,
isMyMessage: () => isMyMessage,
messageIsUnread,
onUserClick,
onUserHover,
setEditingState: setEdit,
};
return (React.createElement(MessageProvider, { value: messageContextValue },
React.createElement(MessageUIComponent, { groupedByUser: groupedByUser })));
};
const MemoizedMessage = React.memo(MessageWithContext, areMessagePropsEqual);
/**
* The Message component is a context provider which implements all the logic required for rendering
* an individual message. The actual UI of the message is delegated via the Message prop on Channel.
*/
export const Message = (props) => {
const { closeReactionSelectorOnClick, disableQuotedMessages, getDeleteMessageErrorNotification, getFetchReactionsErrorNotification, getFlagMessageErrorNotification, getFlagMessageSuccessNotification, getMarkMessageUnreadErrorNotification, getMarkMessageUnreadSuccessNotification, getMuteUserErrorNotification, getMuteUserSuccessNotification, getPinMessageErrorNotification, message, onlySenderCanEdit = false, onMentionsClick: propOnMentionsClick, onMentionsHover: propOnMentionsHover, openThread: propOpenThread, pinPermissions, reactionDetailsSort, retrySendMessage: propRetrySendMessage, sortReactionDetails, sortReactions, } = props;
const { addNotification } = useChannelActionContext('Message');
const { highlightedMessageId, mutes } = useChannelStateContext('Message');
const handleAction = useActionHandler(message);
const handleOpenThread = useOpenThreadHandler(message, propOpenThread);
const handleReaction = useReactionHandler(message);
const handleRetry = useRetryHandler(propRetrySendMessage);
const userRoles = useUserRole(message, onlySenderCanEdit, disableQuotedMessages);
const handleFetchReactions = useReactionsFetcher(message, {
getErrorNotification: getFetchReactionsErrorNotification,
notify: addNotification,
});
const handleDelete = useDeleteHandler(message, {
getErrorNotification: getDeleteMessageErrorNotification,
notify: addNotification,
});
const handleFlag = useFlagHandler(message, {
getErrorNotification: getFlagMessageErrorNotification,
getSuccessNotification: getFlagMessageSuccessNotification,
notify: addNotification,
});
const handleMarkUnread = useMarkUnreadHandler(message, {
getErrorNotification: getMarkMessageUnreadErrorNotification,
getSuccessNotification: getMarkMessageUnreadSuccessNotification,
notify: addNotification,
});
const handleMute = useMuteHandler(message, {
getErrorNotification: getMuteUserErrorNotification,
getSuccessNotification: getMuteUserSuccessNotification,
notify: addNotification,
});
const { onMentionsClick, onMentionsHover } = useMentionsHandler(message, {
onMentionsClick: propOnMentionsClick,
onMentionsHover: propOnMentionsHover,
});
const { canPin, handlePin } = usePinHandler(message, pinPermissions, {
getErrorNotification: getPinMessageErrorNotification,
notify: addNotification,
});
const highlighted = highlightedMessageId === message.id;
return (React.createElement(MemoizedMessage, { additionalMessageInputProps: props.additionalMessageInputProps, autoscrollToBottom: props.autoscrollToBottom, canPin: canPin, closeReactionSelectorOnClick: closeReactionSelectorOnClick, customMessageActions: props.customMessageActions, disableQuotedMessages: props.disableQuotedMessages, endOfGroup: props.endOfGroup, firstOfGroup: props.firstOfGroup, formatDate: props.formatDate, groupedByUser: props.groupedByUser, groupStyles: props.groupStyles, handleAction: handleAction, handleDelete: handleDelete, handleFetchReactions: handleFetchReactions, handleFlag: handleFlag, handleMarkUnread: handleMarkUnread, handleMute: handleMute, handleOpenThread: handleOpenThread, handlePin: handlePin, handleReaction: handleReaction, handleRetry: handleRetry, highlighted: highlighted, initialMessage: props.initialMessage, lastReceivedId: props.lastReceivedId, message: message, Message: props.Message, messageActions: props.messageActions, messageListRect: props.messageListRect, mutes: mutes, onMentionsClickMessage: onMentionsClick, onMentionsHoverMessage: onMentionsHover, onUserClick: props.onUserClick, onUserHover: props.onUserHover, pinPermissions: props.pinPermissions, reactionDetailsSort: reactionDetailsSort, readBy: props.readBy, renderText: props.renderText, sortReactionDetails: sortReactionDetails, sortReactions: sortReactions, threadList: props.threadList, unsafeHTML: props.unsafeHTML, userRoles: userRoles }));
};