Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Display both names for IOU Report Previews #23043

Merged
merged 19 commits into from
Aug 8, 2023
6 changes: 3 additions & 3 deletions src/components/MultipleAvatars.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ const defaultProps = {
maxAvatarsInRow: CONST.AVATAR_ROW_SIZE.DEFAULT,
};

function getContainerStyles(size) {
function getContainerStyles(size, isInReportAction) {
let containerStyles;

switch (size) {
Expand All @@ -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') : [''];
Expand Down
9 changes: 9 additions & 0 deletions src/pages/home/report/ReportActionItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand All @@ -114,6 +117,7 @@ const defaultProps = {
personalDetailsList: {},
shouldShowSubscriptAvatar: false,
hasOutstandingIOU: false,
iouReport: undefined,
shouldHideThreadDividerLine: false,
};

Expand Down Expand Up @@ -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}
Expand Down Expand Up @@ -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}`,
},
Expand Down
101 changes: 74 additions & 27 deletions src/pages/home/report/ReportActionItemSingle.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand All @@ -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,

Expand All @@ -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,
};

Expand All @@ -60,6 +69,8 @@ const defaultProps = {
shouldShowSubscriptAvatar: false,
hasBeenFlagged: false,
report: undefined,
iouReport: undefined,
isHovered: false,
};

const showUserDetails = (accountID) => {
Expand Down Expand Up @@ -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] || {};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not correct. action.actorAccountID and iouReport.ownerAccountID can be same.

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,
Expand All @@ -118,6 +147,49 @@ function ReportActionItemSingle(props) {
[props.action, isWorkspaceActor, actorAccountID],
);

const getAvatar = () => {
if (displayAllActors) {
return (
<MultipleAvatars
icons={[icon, secondaryAvatar]}
isInReportAction
shouldShowTooltip
secondAvatarStyle={[
StyleUtils.getBackgroundAndBorderStyle(themeColors.appBG),
props.isHovered ? StyleUtils.getBackgroundAndBorderStyle(themeColors.highlightBG) : undefined,
]}
/>
);
}
if (props.shouldShowSubscriptAvatar) {
return (
<SubscriptAvatar
mainAvatar={icon}
secondaryAvatar={secondaryAvatar}
mainTooltip={actorHint}
secondaryTooltip={ReportUtils.getPolicyName(props.report)}
noMargin
/>
);
}
return (
<UserDetailsTooltip
accountID={actorAccountID}
delegateAccountID={props.action.delegateAccountID}
icon={icon}
>
<View>
<Avatar
containerStyles={[styles.actionAvatar]}
source={icon.source}
type={icon.type}
name={icon.name}
/>
</View>
</UserDetailsTooltip>
);
};

return (
<View style={props.wrapperStyles}>
<PressableWithoutFeedback
Expand All @@ -129,32 +201,7 @@ function ReportActionItemSingle(props) {
accessibilityLabel={actorHint}
accessibilityRole={CONST.ACCESSIBILITY_ROLE.BUTTON}
>
<OfflineWithFeedback pendingAction={lodashGet(pendingFields, 'avatar', null)}>
{props.shouldShowSubscriptAvatar ? (
<SubscriptAvatar
mainAvatar={icon}
secondaryAvatar={isWorkspaceActor ? {} : ReportUtils.getIcons(props.report, {})[props.report.isOwnPolicyExpenseChat ? 0 : 1]}
mainTooltip={actorHint}
secondaryTooltip={ReportUtils.getPolicyName(props.report)}
noMargin
/>
) : (
<UserDetailsTooltip
accountID={actorAccountID}
delegateAccountID={props.action.delegateAccountID}
icon={icon}
>
<View>
<Avatar
containerStyles={[styles.actionAvatar]}
source={icon.source}
type={icon.type}
name={icon.name}
/>
</View>
</UserDetailsTooltip>
)}
</OfflineWithFeedback>
<OfflineWithFeedback pendingAction={lodashGet(pendingFields, 'avatar', null)}>{getAvatar()}</OfflineWithFeedback>
</PressableWithoutFeedback>
<View style={[styles.chatItemRight]}>
{props.showHeader ? (
Expand Down
4 changes: 4 additions & 0 deletions src/styles/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -2013,6 +2013,10 @@ const styles = {
marginRight: variables.avatarChatSpacing,
},

emptyAvatarMarginChat: {
marginRight: variables.avatarChatSpacing - 12,
},

emptyAvatarMarginSmall: {
marginRight: variables.avatarChatSpacing - 4,
},
Expand Down