diff --git a/web/chat/composed-message.react.js b/web/chat/composed-message.react.js index 10c27b700..2267db44c 100644 --- a/web/chat/composed-message.react.js +++ b/web/chat/composed-message.react.js @@ -1,256 +1,226 @@ // @flow import classNames from 'classnames'; import * as React from 'react'; import { CheckCircle as CheckCircleIcon, Circle as CircleIcon, XCircle as XCircleIcon, } from 'react-feather'; import { useStringForUser } from 'lib/hooks/ens-cache.js'; import { type ChatMessageInfoItem } from 'lib/selectors/chat-selectors.js'; import { getMessageLabel } from 'lib/shared/edit-messages-utils.js'; import { assertComposableMessageType } from 'lib/types/message-types.js'; import type { ThreadInfo } from 'lib/types/minimally-encoded-thread-permissions-types.js'; import { getComposedMessageID } from './chat-constants.js'; import css from './chat-message-list.css'; import FailedSend from './failed-send.react.js'; import InlineEngagement from './inline-engagement.react.js'; import UserAvatar from '../avatars/user-avatar.react.js'; import CommIcon from '../comm-icon.react.js'; import { usePushUserProfileModal } from '../modals/user-profile/user-profile-utils.js'; import { useMessageTooltip } from '../tooltips/tooltip-action-utils.js'; import { tooltipPositions } from '../tooltips/tooltip-utils.js'; export type ComposedMessageID = string; const availableTooltipPositionsForViewerMessage = [ tooltipPositions.LEFT, tooltipPositions.LEFT_BOTTOM, tooltipPositions.LEFT_TOP, tooltipPositions.RIGHT, tooltipPositions.RIGHT_BOTTOM, tooltipPositions.RIGHT_TOP, tooltipPositions.BOTTOM, tooltipPositions.TOP, ]; const availableTooltipPositionsForNonViewerMessage = [ tooltipPositions.RIGHT, tooltipPositions.RIGHT_BOTTOM, tooltipPositions.RIGHT_TOP, tooltipPositions.LEFT, tooltipPositions.LEFT_BOTTOM, tooltipPositions.LEFT_TOP, tooltipPositions.BOTTOM, tooltipPositions.TOP, ]; -type BaseProps = { +type Props = { +item: ChatMessageInfoItem, +threadInfo: ThreadInfo, +shouldDisplayPinIndicator: boolean, +sendFailed: boolean, +children: React.Node, +fixedWidth?: boolean, - +borderRadius: number, -}; -type DefaultProps = { +borderRadius: number }; -type BaseConfig = React.Config; -type Props = { - ...BaseProps, - +onMouseLeave: ?() => mixed, - +onMouseEnter: (event: SyntheticEvent) => mixed, - +stringForUser: ?string, - +onClickUser: () => mixed, + +borderRadius?: number, }; -class ComposedMessage extends React.PureComponent { - static defaultProps: DefaultProps = { borderRadius: 8 }; +const ComposedMessage: React.ComponentType = React.memo( + function ComposedMessage(props) { + const { item, threadInfo } = props; + const { creator } = item.messageInfo; + const { isViewer } = creator; + const availablePositions = isViewer + ? availableTooltipPositionsForViewerMessage + : availableTooltipPositionsForNonViewerMessage; + + const { onMouseLeave, onMouseEnter } = useMessageTooltip({ + item, + threadInfo, + availablePositions, + }); + + const shouldShowUsername = !isViewer && item.startsCluster; + + const pushUserProfileModal = usePushUserProfileModal(creator.id); - render(): React.Node { - assertComposableMessageType(this.props.item.messageInfo.type); - const { borderRadius, item, threadInfo, shouldDisplayPinIndicator } = - this.props; + assertComposableMessageType(item.messageInfo.type); + const { shouldDisplayPinIndicator } = props; + const borderRadius = props.borderRadius ?? 8; const { hasBeenEdited, isPinned } = item; - const { id, creator } = item.messageInfo; + const { id } = item.messageInfo; const threadColor = threadInfo.color; - const { isViewer } = creator; const contentClassName = classNames({ [css.content]: true, [css.viewerContent]: isViewer, [css.nonViewerContent]: !isViewer, }); const messageBoxContainerClassName = classNames({ [css.messageBoxContainer]: true, - [css.fixedWidthMessageBoxContainer]: this.props.fixedWidth, + [css.fixedWidthMessageBoxContainer]: props.fixedWidth, }); const messageBoxClassName = classNames({ [css.messageBox]: true, - [css.fixedWidthMessageBox]: this.props.fixedWidth, + [css.fixedWidthMessageBox]: props.fixedWidth, }); const messageBoxStyle = { borderTopRightRadius: isViewer && !item.startsCluster ? 0 : borderRadius, borderBottomRightRadius: isViewer && !item.endsCluster ? 0 : borderRadius, borderTopLeftRadius: !isViewer && !item.startsCluster ? 0 : borderRadius, borderBottomLeftRadius: !isViewer && !item.endsCluster ? 0 : borderRadius, }; let authorName = null; - const { stringForUser } = this.props; + const stringForUser = useStringForUser(shouldShowUsername ? creator : null); if (stringForUser) { authorName = ( - + {stringForUser} ); } let deliveryIcon = null; let failedSendInfo = null; if (isViewer) { let deliveryIconSpan; let deliveryIconColor = threadColor; const notDeliveredP2PMessages = item?.localMessageInfo?.outboundP2PMessageIDs ?? []; if ( id !== null && id !== undefined && notDeliveredP2PMessages.length === 0 ) { deliveryIconSpan = ; - } else if (this.props.sendFailed) { + } else if (props.sendFailed) { deliveryIconSpan = ; deliveryIconColor = 'FF0000'; failedSendInfo = ; } else { deliveryIconSpan = ; } deliveryIcon = (
{deliveryIconSpan}
); } let inlineEngagement = null; const label = getMessageLabel(hasBeenEdited, threadInfo.id); if ( item.threadCreatedFromMessage || Object.keys(item.reactions).length > 0 || label ) { const positioning = isViewer ? 'right' : 'left'; inlineEngagement = (
); } let avatar; if (!isViewer && item.endsCluster) { avatar = ( -
+
); } else if (!isViewer) { avatar =
; } const pinIconPositioning = isViewer ? 'left' : 'right'; const pinIconName = pinIconPositioning === 'left' ? 'pin-mirror' : 'pin'; const pinIconContainerClassName = classNames({ [css.pinIconContainer]: true, [css.pinIconLeft]: pinIconPositioning === 'left', [css.pinIconRight]: pinIconPositioning === 'right', }); let pinIcon; if (isPinned && shouldDisplayPinIndicator) { pinIcon = (
); } return ( {authorName}
{avatar}
{pinIcon}
- {this.props.children} + {props.children}
{deliveryIcon}
{failedSendInfo} {inlineEngagement}
); - } -} - -type ConnectedConfig = React.Config< - BaseProps, - typeof ComposedMessage.defaultProps, ->; -const ConnectedComposedMessage: React.ComponentType = - React.memo(function ConnectedComposedMessage(props) { - const { item, threadInfo } = props; - const { creator } = props.item.messageInfo; - const { isViewer } = creator; - const availablePositions = isViewer - ? availableTooltipPositionsForViewerMessage - : availableTooltipPositionsForNonViewerMessage; - - const { onMouseLeave, onMouseEnter } = useMessageTooltip({ - item, - threadInfo, - availablePositions, - }); - - const shouldShowUsername = !isViewer && item.startsCluster; - const stringForUser = useStringForUser(shouldShowUsername ? creator : null); - - const pushUserProfileModal = usePushUserProfileModal(creator.id); - - return ( - - ); - }); + }, +); -export default ConnectedComposedMessage; +export default ComposedMessage;