import React from 'react'; import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native'; import { LocalMessage } from 'stream-chat'; import { renderText, RenderTextParams } from './utils/renderText'; import { useComponentsContext } from '../../../contexts/componentsContext/ComponentsContext'; import { MessageContextValue, useMessageContext, } from '../../../contexts/messageContext/MessageContext'; import { MessagesContextValue, useMessagesContext, } from '../../../contexts/messagesContext/MessagesContext'; import { useTheme } from '../../../contexts/themeContext/ThemeContext'; import type { MarkdownStyle, Theme } from '../../../contexts/themeContext/utils/theme'; import { useTranslatedMessage } from '../../../hooks/useTranslatedMessage'; import { primitives } from '../../../theme'; const styles = StyleSheet.create({ textContainer: { maxWidth: 256, paddingHorizontal: primitives.spacingSm }, }); export type MessageTextProps = MessageTextContainerProps & { renderText: (params: RenderTextParams) => React.ReactNode | null; theme: { theme: Theme }; }; export type MessageTextContainerPropsWithContext = Pick< MessageContextValue, 'message' | 'onLongPress' | 'onlyEmojis' | 'onPress' | 'preventPress' | 'isMyMessage' > & Pick & { markdownStyles?: MarkdownStyle; messageOverlay?: boolean; styles?: Partial<{ textContainer: StyleProp; }>; }; const MessageTextContainerWithContext = (props: MessageTextContainerPropsWithContext) => { const theme = useTheme(); const { isMyMessage, markdownRules, markdownStyles: markdownStylesProp = {}, message, messageOverlay, messageTextNumberOfLines, onLongPress, onlyEmojis, onPress, preventPress, styles: stylesProp = {}, } = props; const { MessageText } = useComponentsContext(); const { theme: { messageItemView: { content: { markdown, textContainer: { onlyEmojiMarkdown, ...textContainer }, }, }, semantics, }, } = theme; const translatedMessage = useTranslatedMessage(message); if (!message.text) { return null; } const markdownStyles = { ...markdown, ...markdownStylesProp }; return ( {MessageText ? ( ) : ( renderText({ isMyMessage, semantics, markdownRules, markdownStyles: { ...markdownStyles, ...(onlyEmojis ? onlyEmojiMarkdown : {}), }, message: translatedMessage as LocalMessage, messageOverlay, messageTextNumberOfLines, onLongPress, onlyEmojis, onPress, preventPress, }) )} ); }; const areEqual = ( prevProps: MessageTextContainerPropsWithContext, nextProps: MessageTextContainerPropsWithContext, ) => { const { markdownStyles: prevMarkdownStyles, message: prevMessage, myMessageTheme: prevMyMessageTheme, onlyEmojis: prevOnlyEmojis, } = prevProps; const { markdownStyles: nextMarkdownStyles, message: nextMessage, myMessageTheme: nextMyMessageTheme, onlyEmojis: nextOnlyEmojis, } = nextProps; const messageStatusEqual = prevMessage.status === nextMessage.status; if (!messageStatusEqual) { return false; } const messageTextEqual = prevMessage.text === nextMessage.text && prevMessage.i18n === nextMessage.i18n; if (!messageTextEqual) { return false; } const onlyEmojisEqual = prevOnlyEmojis === nextOnlyEmojis; if (!onlyEmojisEqual) { return false; } const mentionedUsersEqual = prevMessage.mentioned_users?.length === nextMessage.mentioned_users?.length && (nextMessage.mentioned_users?.length === 0 || (prevMessage.mentioned_users?.length && nextMessage.mentioned_users?.length && prevMessage.mentioned_users[0].name === nextMessage.mentioned_users[0].name)); if (!mentionedUsersEqual) { return false; } // stringify could be an expensive operation, so lets rule out the obvious // possibilities first such as different object reference or empty objects etc. // Also keeping markdown equality check at the last to make sure other less // expensive equality checks get executed first and markdown check will be skipped if returned // false from previous checks. const markdownStylesEqual = prevMarkdownStyles === nextMarkdownStyles || (Object.keys(prevMarkdownStyles || {}).length === 0 && Object.keys(nextMarkdownStyles || {}).length === 0) || JSON.stringify(prevMarkdownStyles) === JSON.stringify(nextMarkdownStyles); if (!markdownStylesEqual) { return false; } const messageThemeEqual = JSON.stringify(prevMyMessageTheme) === JSON.stringify(nextMyMessageTheme); if (!messageThemeEqual) { return false; } return true; }; const MemoizedMessageTextContainer = React.memo( MessageTextContainerWithContext, areEqual, ) as typeof MessageTextContainerWithContext; export type MessageTextContainerProps = Partial; export const MessageTextContainer = (props: MessageTextContainerProps) => { const { message, onLongPress, onlyEmojis, onPress, preventPress, isMyMessage } = useMessageContext(); const { markdownRules, messageTextNumberOfLines, myMessageTheme } = useMessagesContext(); return ( ); }; MessageTextContainer.displayName = 'MessageTextContainer{messageItemView{content}}';