diff --git a/src/components/DisplayNames/displayNamesPropTypes.js b/src/components/DisplayNames/displayNamesPropTypes.js
index 30492b90bfc6..c923b943174f 100644
--- a/src/components/DisplayNames/displayNamesPropTypes.js
+++ b/src/components/DisplayNames/displayNamesPropTypes.js
@@ -10,8 +10,14 @@ const propTypes = {
/** The name to display in bold */
displayName: PropTypes.string,
- /** The tooltip to show when the associated name is hovered */
- tooltip: PropTypes.string,
+ /** The Account ID for the tooltip */
+ accountID: PropTypes.string,
+
+ /** The login for the tooltip fallback */
+ login: PropTypes.string,
+
+ /** The avatar for the tooltip fallback */
+ avatar: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
}),
),
diff --git a/src/components/DisplayNames/index.js b/src/components/DisplayNames/index.js
index 9eb7d0bc501f..fd1bcaf8a6e0 100644
--- a/src/components/DisplayNames/index.js
+++ b/src/components/DisplayNames/index.js
@@ -5,6 +5,7 @@ import {propTypes, defaultProps} from './displayNamesPropTypes';
import styles from '../../styles/styles';
import Tooltip from '../Tooltip';
import Text from '../Text';
+import UserDetailsTooltip from '../UserDetailsTooltip';
class DisplayNames extends PureComponent {
constructor(props) {
@@ -86,11 +87,16 @@ class DisplayNames extends PureComponent {
>
{this.props.shouldUseFullTitle
? this.props.fullTitle
- : _.map(this.props.displayNamesWithTooltips, ({displayName, tooltip}, index) => (
+ : _.map(this.props.displayNamesWithTooltips, ({displayName, accountID, avatar, login}, index) => (
- this.getTooltipShiftX(index)}
>
{/* // We need to get the refs to all the names which will be used to correct
@@ -101,7 +107,7 @@ class DisplayNames extends PureComponent {
>
{displayName}
-
+
{index < this.props.displayNamesWithTooltips.length - 1 && , }
))}
diff --git a/src/components/MultipleAvatars.js b/src/components/MultipleAvatars.js
index fa6330d4703f..c65e6ff8ed9b 100644
--- a/src/components/MultipleAvatars.js
+++ b/src/components/MultipleAvatars.js
@@ -2,6 +2,7 @@ import React, {memo} from 'react';
import PropTypes from 'prop-types';
import {View} from 'react-native';
import _ from 'underscore';
+import lodashGet from 'lodash/get';
import styles from '../styles/styles';
import Avatar from './Avatar';
import Tooltip from './Tooltip';
@@ -11,6 +12,8 @@ import * as StyleUtils from '../styles/StyleUtils';
import CONST from '../CONST';
import variables from '../styles/variables';
import avatarPropTypes from './avatarPropTypes';
+import UserDetailsTooltip from './UserDetailsTooltip';
+import * as ReportUtils from '../libs/ReportUtils';
const propTypes = {
/** Array of avatar URLs or icons */
@@ -74,7 +77,14 @@ const MultipleAvatars = (props) => {
if (props.icons.length === 1 && !props.shouldStackHorizontally) {
return (
-
+
{
type={props.icons[0].type}
/>
-
+
);
}
@@ -112,9 +122,9 @@ const MultipleAvatars = (props) => {
{props.shouldStackHorizontally ? (
<>
{_.map([...props.icons].splice(0, 4), (icon, index) => (
-
{
type={icon.type}
/>
-
+
))}
{props.icons.length > 4 && (
{
>
) : (
-
+
{/* View is necessary for tooltip to show for multiple avatars in LHN */}
{
type={props.icons[0].type}
/>
-
+
{props.icons.length === 2 ? (
-
+
{
type={props.icons[1].type}
/>
-
+
) : (
diff --git a/src/components/ReportWelcomeText.js b/src/components/ReportWelcomeText.js
index f2a79dd50fe3..52016d285a9b 100644
--- a/src/components/ReportWelcomeText.js
+++ b/src/components/ReportWelcomeText.js
@@ -4,6 +4,7 @@ import lodashGet from 'lodash/get';
import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';
+import UserDetailsTooltip from './UserDetailsTooltip';
import styles from '../styles/styles';
import Text from './Text';
import withLocalize, {withLocalizePropTypes} from './withLocalize';
@@ -13,7 +14,6 @@ import * as OptionsListUtils from '../libs/OptionsListUtils';
import ONYXKEYS from '../ONYXKEYS';
import Navigation from '../libs/Navigation/Navigation';
import ROUTES from '../ROUTES';
-import Tooltip from './Tooltip';
import reportPropTypes from '../pages/reportPropTypes';
import CONST from '../CONST';
@@ -93,16 +93,16 @@ const ReportWelcomeText = (props) => {
{isDefault && (
{props.translate('reportActionsView.beginningOfChatHistory')}
- {_.map(displayNamesWithTooltips, ({displayName, pronouns, tooltip}, index) => (
+ {_.map(displayNamesWithTooltips, ({displayName, pronouns, accountID}, index) => (
-
+
Navigation.navigate(ROUTES.getProfileRoute(participantAccountIDs[index]))}
>
{displayName}
-
+
{!_.isEmpty(pronouns) && {` (${pronouns})`}}
{index === displayNamesWithTooltips.length - 1 && .}
{index === displayNamesWithTooltips.length - 2 && {` ${props.translate('common.and')} `}}
diff --git a/src/components/UserDetailsTooltip/index.js b/src/components/UserDetailsTooltip/index.js
new file mode 100644
index 000000000000..493b40a0a5e1
--- /dev/null
+++ b/src/components/UserDetailsTooltip/index.js
@@ -0,0 +1,52 @@
+import React, {useCallback} from 'react';
+import {View, Text} from 'react-native';
+import {withOnyx} from 'react-native-onyx';
+import Str from 'expensify-common/lib/str';
+import lodashGet from 'lodash/get';
+import _ from 'underscore';
+import Avatar from '../Avatar';
+import Tooltip from '../Tooltip';
+import {propTypes, defaultProps} from './userDetailsTooltipPropTypes';
+import styles from '../../styles/styles';
+import ONYXKEYS from '../../ONYXKEYS';
+
+function UserDetailsTooltip(props) {
+ const userDetails = lodashGet(props.personalDetailsList, props.accountID, props.fallbackUserDetails);
+ const renderTooltipContent = useCallback(
+ () => (
+
+
+
+
+
+
+ {String(userDetails.displayName).trim() ? userDetails.displayName : ''}
+
+
+
+ {String(userDetails.login).trim() && !_.isEqual(userDetails.login, userDetails.displayName) ? Str.removeSMSDomain(userDetails.login) : ''}
+
+
+ ),
+ [userDetails.avatar, userDetails.displayName, userDetails.login],
+ );
+
+ if (!userDetails.displayName && !userDetails.login) {
+ return props.children;
+ }
+
+ return {props.children};
+}
+
+UserDetailsTooltip.propTypes = propTypes;
+UserDetailsTooltip.defaultProps = defaultProps;
+UserDetailsTooltip.displayName = 'UserDetailsTooltip';
+
+export default withOnyx({
+ personalDetailsList: {
+ key: ONYXKEYS.PERSONAL_DETAILS_LIST,
+ },
+})(UserDetailsTooltip);
diff --git a/src/components/UserDetailsTooltip/userDetailsTooltipPropTypes.js b/src/components/UserDetailsTooltip/userDetailsTooltipPropTypes.js
new file mode 100644
index 000000000000..4d34c73ef1ba
--- /dev/null
+++ b/src/components/UserDetailsTooltip/userDetailsTooltipPropTypes.js
@@ -0,0 +1,28 @@
+import PropTypes from 'prop-types';
+import personalDetailsPropType from '../../pages/personalDetailsPropType';
+
+const propTypes = {
+ /** User's Account ID */
+ accountID: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
+ /** Fallback User Details object used if no accountID */
+ fallbackUserDetails: PropTypes.shape({
+ /** Avatar URL */
+ avatar: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
+ /** Display Name */
+ displayName: PropTypes.string,
+ /** Login */
+ login: PropTypes.string,
+ }),
+ /** Component that displays the tooltip */
+ children: PropTypes.node.isRequired,
+ /** List of personalDetails (keyed by accountID) */
+ personalDetailsList: PropTypes.objectOf(personalDetailsPropType),
+};
+
+const defaultProps = {
+ accountID: '',
+ fallbackUserDetails: {displayName: '', login: '', avatar: ''},
+ personalDetailsList: {},
+};
+
+export {propTypes, defaultProps};
diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js
index fa8b93741aa3..4fa3d0ed2afc 100644
--- a/src/libs/ReportUtils.js
+++ b/src/libs/ReportUtils.js
@@ -749,6 +749,7 @@ function getIcons(report, personalDetails, defaultIcon = null, isPayer = false)
}
if (isConciergeChatReport(report)) {
result.source = CONST.CONCIERGE_ICON_URL;
+ result.name = CONST.EMAIL.CONCIERGE;
return [result];
}
if (isArchivedRoom(report)) {
@@ -845,6 +846,16 @@ function getPersonalDetailsForLogin(login) {
);
}
+/**
+ * Gets the accountID for a login by looking in the ONYXKEYS.PERSONAL_DETAILS Onyx key (stored in the local variable, allPersonalDetails). If it doesn't exist in Onyx,
+ * then an empty string is returned.
+ * @param {String} login
+ * @returns {String}
+ */
+function getAccountIDForLogin(login) {
+ return lodashGet(allPersonalDetails, [login, 'accountID'], '');
+}
+
/**
* Get the displayName for a single report participant.
*
@@ -873,7 +884,8 @@ function getDisplayNameForParticipant(login, shouldUseShortForm = false) {
function getDisplayNamesWithTooltips(participants, isMultipleParticipantReport) {
return _.map(participants, (participant) => {
const displayName = getDisplayNameForParticipant(participant.login, isMultipleParticipantReport);
- const tooltip = participant.login ? Str.removeSMSDomain(participant.login) : '';
+ const avatar = UserUtils.getDefaultAvatar(participant.login);
+ const accountID = participant.accountID;
let pronouns = participant.pronouns;
if (pronouns && pronouns.startsWith(CONST.PRONOUNS.PREFIX)) {
@@ -883,7 +895,9 @@ function getDisplayNamesWithTooltips(participants, isMultipleParticipantReport)
return {
displayName,
- tooltip,
+ avatar,
+ login: participant.login,
+ accountID,
pronouns,
};
});
@@ -2153,7 +2167,9 @@ function getParentReport(report) {
}
export {
+ getAccountIDForLogin,
getReportParticipantsTitle,
+ getPersonalDetailsForLogin,
isReportMessageAttachment,
findLastAccessedReport,
canEditReportAction,
diff --git a/src/pages/DetailsPage.js b/src/pages/DetailsPage.js
index 909b52da89b3..1cb2c0ef76f0 100755
--- a/src/pages/DetailsPage.js
+++ b/src/pages/DetailsPage.js
@@ -16,7 +16,7 @@ import personalDetailsPropType from './personalDetailsPropType';
import withLocalize, {withLocalizePropTypes} from '../components/withLocalize';
import compose from '../libs/compose';
import CommunicationsLink from '../components/CommunicationsLink';
-import Tooltip from '../components/Tooltip';
+import UserDetailsTooltip from '../components/UserDetailsTooltip';
import CONST from '../CONST';
import * as ReportUtils from '../libs/ReportUtils';
import * as Expensicons from '../components/Icon/Expensicons';
@@ -168,9 +168,9 @@ class DetailsPage extends React.PureComponent {
{this.props.translate(isSMSLogin ? 'common.phoneNumber' : 'common.email')}
-
+
{isSMSLogin ? this.props.formatPhoneNumber(phoneNumber) : details.login}
-
+
) : null}
diff --git a/src/pages/ProfilePage.js b/src/pages/ProfilePage.js
index 932f489be5f4..31d6aeef422e 100755
--- a/src/pages/ProfilePage.js
+++ b/src/pages/ProfilePage.js
@@ -17,7 +17,7 @@ import personalDetailsPropType from './personalDetailsPropType';
import withLocalize, {withLocalizePropTypes} from '../components/withLocalize';
import compose from '../libs/compose';
import CommunicationsLink from '../components/CommunicationsLink';
-import Tooltip from '../components/Tooltip';
+import UserDetailsTooltip from '../components/UserDetailsTooltip';
import CONST from '../CONST';
import * as ReportUtils from '../libs/ReportUtils';
import * as Expensicons from '../components/Icon/Expensicons';
@@ -179,9 +179,9 @@ function ProfilePage(props) {
{props.translate(isSMSLogin ? 'common.phoneNumber' : 'common.email')}
-
+
{isSMSLogin ? props.formatPhoneNumber(phoneNumber) : login}
-
+
) : null}
diff --git a/src/pages/home/report/ReportActionItemFragment.js b/src/pages/home/report/ReportActionItemFragment.js
index e606b0c65f69..07b00590306e 100644
--- a/src/pages/home/report/ReportActionItemFragment.js
+++ b/src/pages/home/report/ReportActionItemFragment.js
@@ -8,7 +8,6 @@ import variables from '../../../styles/variables';
import themeColors from '../../../styles/themes/default';
import RenderHTML from '../../../components/RenderHTML';
import Text from '../../../components/Text';
-import Tooltip from '../../../components/Tooltip';
import * as EmojiUtils from '../../../libs/EmojiUtils';
import withWindowDimensions, {windowDimensionsPropTypes} from '../../../components/withWindowDimensions';
import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize';
@@ -19,14 +18,15 @@ import {withNetwork} from '../../../components/OnyxProvider';
import CONST from '../../../CONST';
import applyStrikethrough from '../../../components/HTMLEngineProvider/applyStrikethrough';
import editedLabelStyles from '../../../styles/editedLabelStyles';
+import UserDetailsTooltip from '../../../components/UserDetailsTooltip';
const propTypes = {
+ /** Users accountID */
+ accountID: PropTypes.string.isRequired,
+
/** The message fragment needing to be displayed */
fragment: reportActionFragmentPropTypes.isRequired,
- /** Text to be shown for tooltip When Fragment is report Actor */
- tooltipText: PropTypes.string,
-
/** Is this fragment an attachment? */
isAttachment: PropTypes.bool,
@@ -73,7 +73,6 @@ const defaultProps = {
},
loading: false,
isSingleLine: false,
- tooltipText: '',
source: '',
style: [],
};
@@ -146,14 +145,14 @@ const ReportActionItemFragment = (props) => {
}
case 'TEXT':
return (
-
+
{props.fragment.text}
-
+
);
case 'LINK':
return LINK;
diff --git a/src/pages/home/report/ReportActionItemMessage.js b/src/pages/home/report/ReportActionItemMessage.js
index aa8639ea071c..fef1bb0377c9 100644
--- a/src/pages/home/report/ReportActionItemMessage.js
+++ b/src/pages/home/report/ReportActionItemMessage.js
@@ -40,6 +40,7 @@ const ReportActionItemMessage = (props) => (
attachmentInfo={props.action.attachmentInfo}
pendingAction={props.action.pendingAction}
source={lodashGet(props.action, 'originalMessage.source')}
+ accountID={String(props.action.actorAccountID)}
loading={props.action.isLoading}
style={props.style}
/>
diff --git a/src/pages/home/report/ReportActionItemSingle.js b/src/pages/home/report/ReportActionItemSingle.js
index eb5f2fa07434..84a89bf7475f 100644
--- a/src/pages/home/report/ReportActionItemSingle.js
+++ b/src/pages/home/report/ReportActionItemSingle.js
@@ -14,7 +14,6 @@ import withLocalize, {withLocalizePropTypes} from '../../../components/withLocal
import Navigation from '../../../libs/Navigation/Navigation';
import ROUTES from '../../../ROUTES';
import {withPersonalDetails} from '../../../components/OnyxProvider';
-import Tooltip from '../../../components/Tooltip';
import ControlSelection from '../../../libs/ControlSelection';
import * as ReportUtils from '../../../libs/ReportUtils';
import OfflineWithFeedback from '../../../components/OfflineWithFeedback';
@@ -22,6 +21,7 @@ import CONST from '../../../CONST';
import SubscriptAvatar from '../../../components/SubscriptAvatar';
import reportPropTypes from '../../reportPropTypes';
import * as UserUtils from '../../../libs/UserUtils';
+import UserDetailsTooltip from '../../../components/UserDetailsTooltip';
const propTypes = {
/** All the data of the action */
@@ -100,14 +100,14 @@ const ReportActionItemSingle = (props) => {
noMargin
/>
) : (
-
+
-
+
)}
@@ -123,8 +123,8 @@ const ReportActionItemSingle = (props) => {
{_.map(personArray, (fragment, index) => (
{
expect(ReportUtils.getDisplayNamesWithTooltips(participantsPersonalDetails, false)).toStrictEqual([
{
displayName: 'Ragnar Lothbrok',
- tooltip: 'ragnar@vikings.net',
+ login: 'ragnar@vikings.net',
+ avatar: {
+ testUri: '../../../assets/images/avatars/user/default-avatar_16.svg',
+ },
+ accountID: 1,
pronouns: undefined,
},
{
displayName: 'floki@vikings.net',
- tooltip: 'floki@vikings.net',
+ avatar: {
+ testUri: '../../../assets/images/avatars/user/default-avatar_24.svg',
+ },
+ login: 'floki@vikings.net',
+ accountID: 2,
pronouns: undefined,
},
{
displayName: 'Lagertha Lothbrok',
- tooltip: 'lagertha@vikings.net',
+ avatar: {
+ testUri: '../../../assets/images/avatars/user/default-avatar_11.svg',
+ },
+ login: 'lagertha@vikings.net',
+ accountID: 3,
pronouns: 'She/her',
},
{
displayName: '(833) 240-3627',
- tooltip: '+18332403627',
+ avatar: {
+ testUri: '../../../assets/images/avatars/user/default-avatar_15.svg',
+ },
+ login: '+18332403627@expensify.sms',
+ accountID: 4,
pronouns: undefined,
},
]);
@@ -80,22 +100,38 @@ describe('ReportUtils', () => {
expect(ReportUtils.getDisplayNamesWithTooltips(participantsPersonalDetails, true)).toStrictEqual([
{
displayName: 'Ragnar',
- tooltip: 'ragnar@vikings.net',
+ login: 'ragnar@vikings.net',
+ avatar: {
+ testUri: '../../../assets/images/avatars/user/default-avatar_16.svg',
+ },
+ accountID: 1,
pronouns: undefined,
},
{
displayName: 'floki@vikings.net',
- tooltip: 'floki@vikings.net',
+ avatar: {
+ testUri: '../../../assets/images/avatars/user/default-avatar_24.svg',
+ },
+ login: 'floki@vikings.net',
+ accountID: 2,
pronouns: undefined,
},
{
displayName: 'Lagertha',
- tooltip: 'lagertha@vikings.net',
+ avatar: {
+ testUri: '../../../assets/images/avatars/user/default-avatar_11.svg',
+ },
+ login: 'lagertha@vikings.net',
+ accountID: 3,
pronouns: 'She/her',
},
{
displayName: '(833) 240-3627',
- tooltip: '+18332403627',
+ avatar: {
+ testUri: '../../../assets/images/avatars/user/default-avatar_15.svg',
+ },
+ login: '+18332403627@expensify.sms',
+ accountID: 4,
pronouns: undefined,
},
]);