diff --git a/src/components/MultipleAvatars.js b/src/components/MultipleAvatars.js index ceb1c5371c7e..a53515d35c0f 100644 --- a/src/components/MultipleAvatars.js +++ b/src/components/MultipleAvatars.js @@ -71,7 +71,7 @@ const defaultProps = { maxAvatarsInRow: CONST.AVATAR_ROW_SIZE.DEFAULT, }; -function getContainerStyles(size) { +function getContainerStyles(size, isInReportAction) { let containerStyles; switch (size) { @@ -85,14 +85,14 @@ function getContainerStyles(size) { containerStyles = [styles.emptyAvatarMedium, styles.emptyAvatarMargin]; break; default: - containerStyles = [styles.emptyAvatar, styles.emptyAvatarMargin]; + containerStyles = [styles.emptyAvatar, isInReportAction ? styles.emptyAvatarMarginChat : styles.emptyAvatarMargin]; } return containerStyles; } function MultipleAvatars(props) { const [avatarRows, setAvatarRows] = useState([props.icons]); - let avatarContainerStyles = getContainerStyles(props.size); + let avatarContainerStyles = getContainerStyles(props.size, props.isInReportAction); const singleAvatarStyle = props.size === CONST.AVATAR_SIZE.SMALL ? styles.singleAvatarSmall : styles.singleAvatar; const secondAvatarStyles = [props.size === CONST.AVATAR_SIZE.SMALL ? styles.secondAvatarSmall : styles.secondAvatar, ...props.secondAvatarStyle]; const tooltipTexts = props.shouldShowTooltip ? _.pluck(props.icons, 'name') : ['']; diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 05e57c592b9f..f38f52af1fe5 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -103,6 +103,9 @@ const propTypes = { emojiReactions: EmojiReactionsPropTypes, personalDetailsList: PropTypes.objectOf(personalDetailsPropType), + /** IOU report for this action, if any */ + iouReport: reportPropTypes, + /** Flag to show, hide the thread divider line */ shouldHideThreadDividerLine: PropTypes.bool, }; @@ -114,6 +117,7 @@ const defaultProps = { personalDetailsList: {}, shouldShowSubscriptAvatar: false, hasOutstandingIOU: false, + iouReport: undefined, shouldHideThreadDividerLine: false, }; @@ -438,6 +442,8 @@ function ReportActionItem(props) { wrapperStyles={[styles.chatItem, isWhisper ? styles.pt1 : {}]} shouldShowSubscriptAvatar={props.shouldShowSubscriptAvatar} report={props.report} + iouReport={props.iouReport} + isHovered={hovered} hasBeenFlagged={!_.contains([CONST.MODERATION.MODERATOR_DECISION_APPROVED, CONST.MODERATION.MODERATOR_DECISION_PENDING], moderationDecision)} > {content} @@ -593,6 +599,9 @@ export default compose( preferredSkinTone: { key: ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE, }, + iouReport: { + key: ({action}) => `${ONYXKEYS.COLLECTION.REPORT}${ReportActionsUtils.getIOUReportIDFromReportActionPreview(action)}`, + }, emojiReactions: { key: ({action}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${action.reportActionID}`, }, diff --git a/src/pages/home/report/ReportActionItemSingle.js b/src/pages/home/report/ReportActionItemSingle.js index 703a7d432027..8633f8f8a224 100644 --- a/src/pages/home/report/ReportActionItemSingle.js +++ b/src/pages/home/report/ReportActionItemSingle.js @@ -23,6 +23,9 @@ import reportPropTypes from '../../reportPropTypes'; import * as UserUtils from '../../../libs/UserUtils'; import PressableWithoutFeedback from '../../../components/Pressable/PressableWithoutFeedback'; import UserDetailsTooltip from '../../../components/UserDetailsTooltip'; +import MultipleAvatars from '../../../components/MultipleAvatars'; +import * as StyleUtils from '../../../styles/StyleUtils'; +import themeColors from '../../../styles/themes/default'; const propTypes = { /** All the data of the action */ @@ -41,6 +44,9 @@ const propTypes = { /** Report for this action */ report: reportPropTypes, + /** IOU Report for this action, if any */ + iouReport: reportPropTypes, + /** Show header for action */ showHeader: PropTypes.bool, @@ -50,6 +56,9 @@ const propTypes = { /** If the message has been flagged for moderation */ hasBeenFlagged: PropTypes.bool, + /** If the action is being hovered */ + isHovered: PropTypes.bool, + ...withLocalizePropTypes, }; @@ -60,6 +69,8 @@ const defaultProps = { shouldShowSubscriptAvatar: false, hasBeenFlagged: false, report: undefined, + iouReport: undefined, + isHovered: false, }; const showUserDetails = (accountID) => { @@ -91,7 +102,25 @@ function ReportActionItemSingle(props) { displayName = actorHint; avatarSource = UserUtils.getAvatar(delegateDetails.avatar, props.action.delegateAccountID); } - const icon = {source: avatarSource, type: isWorkspaceActor ? CONST.ICON_TYPE_WORKSPACE : CONST.ICON_TYPE_AVATAR, name: displayName, id: actorAccountID}; + + // If this is a report preview, display names and avatars of both people involved + let secondaryAvatar = {}; + const displayAllActors = props.action.actionName === CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW && props.iouReport; + const primaryDisplayName = displayName; + if (displayAllActors) { + const secondaryUserDetails = props.personalDetailsList[props.iouReport.ownerAccountID] || {}; + const secondaryDisplayName = lodashGet(secondaryUserDetails, 'displayName', ''); + displayName = `${primaryDisplayName} & ${secondaryDisplayName}`; + secondaryAvatar = { + source: UserUtils.getAvatar(secondaryUserDetails.avatar, props.iouReport.ownerAccountID), + type: CONST.ICON_TYPE_AVATAR, + name: secondaryDisplayName, + id: props.iouReport.ownerAccountID, + }; + } else if (!isWorkspaceActor) { + secondaryAvatar = ReportUtils.getIcons(props.report, {})[props.report.isOwnPolicyExpenseChat ? 0 : 1]; + } + const icon = {source: avatarSource, type: isWorkspaceActor ? CONST.ICON_TYPE_WORKSPACE : CONST.ICON_TYPE_AVATAR, name: primaryDisplayName, id: actorAccountID}; // Since the display name for a report action message is delivered with the report history as an array of fragments // we'll need to take the displayName from personal details and have it be in the same format for now. Eventually, @@ -118,6 +147,49 @@ function ReportActionItemSingle(props) { [props.action, isWorkspaceActor, actorAccountID], ); + const getAvatar = () => { + if (displayAllActors) { + return ( + + ); + } + if (props.shouldShowSubscriptAvatar) { + return ( + + ); + } + return ( + + + + + + ); + }; + return ( - - {props.shouldShowSubscriptAvatar ? ( - - ) : ( - - - - - - )} - + {getAvatar()} {props.showHeader ? ( diff --git a/src/styles/styles.js b/src/styles/styles.js index f7517cd3acb7..ac12ff9819da 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -2013,6 +2013,10 @@ const styles = { marginRight: variables.avatarChatSpacing, }, + emptyAvatarMarginChat: { + marginRight: variables.avatarChatSpacing - 12, + }, + emptyAvatarMarginSmall: { marginRight: variables.avatarChatSpacing - 4, },