Skip to content

Commit

Permalink
Merge pull request #20276 from cloudpresser/cloudpresser/userDetailsT…
Browse files Browse the repository at this point in the history
…ooltip

Cloudpresser/user details tooltip
  • Loading branch information
puneetlath authored Jun 12, 2023
2 parents 1b06172 + a0ea50d commit aa70abf
Show file tree
Hide file tree
Showing 13 changed files with 200 additions and 46 deletions.
10 changes: 8 additions & 2 deletions src/components/DisplayNames/displayNamesPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -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]),
}),
),

Expand Down
14 changes: 10 additions & 4 deletions src/components/DisplayNames/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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) => (
<Fragment key={index}>
<Tooltip
<UserDetailsTooltip
key={index}
text={tooltip}
accountID={accountID}
fallbackUserDetails={{
avatar,
login,
displayName,
}}
shiftHorizontal={() => this.getTooltipShiftX(index)}
>
{/* // We need to get the refs to all the names which will be used to correct
Expand All @@ -101,7 +107,7 @@ class DisplayNames extends PureComponent {
>
{displayName}
</Text>
</Tooltip>
</UserDetailsTooltip>
{index < this.props.displayNamesWithTooltips.length - 1 && <Text style={this.props.textStyles}>,&nbsp;</Text>}
</Fragment>
))}
Expand Down
28 changes: 19 additions & 9 deletions src/components/MultipleAvatars.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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 */
Expand Down Expand Up @@ -74,7 +77,14 @@ const MultipleAvatars = (props) => {

if (props.icons.length === 1 && !props.shouldStackHorizontally) {
return (
<Tooltip text={tooltipTexts[0]}>
<UserDetailsTooltip
accountID={ReportUtils.getAccountIDForLogin(props.icons[0].name)}
fallbackUserDetails={{
displayName: ReportUtils.getDisplayNameForParticipant(props.icons[0].name),
login: lodashGet(props.icons[0], 'name', tooltipTexts[0]),
avatar: lodashGet(props.icons[0], 'source', ''),
}}
>
<View style={avatarContainerStyles}>
<Avatar
source={props.icons[0].source}
Expand All @@ -84,7 +94,7 @@ const MultipleAvatars = (props) => {
type={props.icons[0].type}
/>
</View>
</Tooltip>
</UserDetailsTooltip>
);
}

Expand Down Expand Up @@ -112,9 +122,9 @@ const MultipleAvatars = (props) => {
{props.shouldStackHorizontally ? (
<>
{_.map([...props.icons].splice(0, 4), (icon, index) => (
<Tooltip
<UserDetailsTooltip
key={`stackedAvatars-${index}`}
text={tooltipTexts[index]}
accountID={ReportUtils.getAccountIDForLogin(icon.name)}
>
<View
style={[
Expand All @@ -138,7 +148,7 @@ const MultipleAvatars = (props) => {
type={icon.type}
/>
</View>
</Tooltip>
</UserDetailsTooltip>
))}
{props.icons.length > 4 && (
<Tooltip
Expand Down Expand Up @@ -173,7 +183,7 @@ const MultipleAvatars = (props) => {
</>
) : (
<View style={singleAvatarStyles}>
<Tooltip text={tooltipTexts[0]}>
<UserDetailsTooltip accountID={ReportUtils.getAccountIDForLogin(props.icons[0].name)}>
{/* View is necessary for tooltip to show for multiple avatars in LHN */}
<View>
<Avatar
Expand All @@ -185,10 +195,10 @@ const MultipleAvatars = (props) => {
type={props.icons[0].type}
/>
</View>
</Tooltip>
</UserDetailsTooltip>
<View style={secondAvatarStyles}>
{props.icons.length === 2 ? (
<Tooltip text={tooltipTexts[1]}>
<UserDetailsTooltip accountID={ReportUtils.getAccountIDForLogin(props.icons[1].name)}>
<View>
<Avatar
source={props.icons[1].source || props.fallbackIcon}
Expand All @@ -199,7 +209,7 @@ const MultipleAvatars = (props) => {
type={props.icons[1].type}
/>
</View>
</Tooltip>
</UserDetailsTooltip>
) : (
<Tooltip text={tooltipTexts.slice(1).join(', ')}>
<View style={[singleAvatarStyles, styles.alignItemsCenter, styles.justifyContentCenter]}>
Expand Down
8 changes: 4 additions & 4 deletions src/components/ReportWelcomeText.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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';

Expand Down Expand Up @@ -93,16 +93,16 @@ const ReportWelcomeText = (props) => {
{isDefault && (
<Text>
<Text>{props.translate('reportActionsView.beginningOfChatHistory')}</Text>
{_.map(displayNamesWithTooltips, ({displayName, pronouns, tooltip}, index) => (
{_.map(displayNamesWithTooltips, ({displayName, pronouns, accountID}, index) => (
<Text key={`${displayName}${pronouns}${index}`}>
<Tooltip text={tooltip}>
<UserDetailsTooltip accountID={accountID}>
<Text
style={[styles.textStrong]}
onPress={() => Navigation.navigate(ROUTES.getProfileRoute(participantAccountIDs[index]))}
>
{displayName}
</Text>
</Tooltip>
</UserDetailsTooltip>
{!_.isEmpty(pronouns) && <Text>{` (${pronouns})`}</Text>}
{index === displayNamesWithTooltips.length - 1 && <Text>.</Text>}
{index === displayNamesWithTooltips.length - 2 && <Text>{` ${props.translate('common.and')} `}</Text>}
Expand Down
52 changes: 52 additions & 0 deletions src/components/UserDetailsTooltip/index.js
Original file line number Diff line number Diff line change
@@ -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(
() => (
<View style={[styles.alignItemsCenter, styles.ph2, styles.pv2]}>
<View style={styles.emptyAvatar}>
<Avatar
containerStyles={[styles.actionAvatar]}
source={userDetails.avatar}
/>
</View>

<Text style={[styles.mt2, styles.textMicroBold, styles.textReactionSenders, styles.textAlignCenter]}>
{String(userDetails.displayName).trim() ? userDetails.displayName : ''}
</Text>

<Text style={[styles.textMicro, styles.fontColorReactionLabel]}>
{String(userDetails.login).trim() && !_.isEqual(userDetails.login, userDetails.displayName) ? Str.removeSMSDomain(userDetails.login) : ''}
</Text>
</View>
),
[userDetails.avatar, userDetails.displayName, userDetails.login],
);

if (!userDetails.displayName && !userDetails.login) {
return props.children;
}

return <Tooltip renderTooltipContent={renderTooltipContent}>{props.children}</Tooltip>;
}

UserDetailsTooltip.propTypes = propTypes;
UserDetailsTooltip.defaultProps = defaultProps;
UserDetailsTooltip.displayName = 'UserDetailsTooltip';

export default withOnyx({
personalDetailsList: {
key: ONYXKEYS.PERSONAL_DETAILS_LIST,
},
})(UserDetailsTooltip);
28 changes: 28 additions & 0 deletions src/components/UserDetailsTooltip/userDetailsTooltipPropTypes.js
Original file line number Diff line number Diff line change
@@ -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};
20 changes: 18 additions & 2 deletions src/libs/ReportUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -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.
*
Expand Down Expand Up @@ -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)) {
Expand All @@ -883,7 +895,9 @@ function getDisplayNamesWithTooltips(participants, isMultipleParticipantReport)

return {
displayName,
tooltip,
avatar,
login: participant.login,
accountID,
pronouns,
};
});
Expand Down Expand Up @@ -2153,7 +2167,9 @@ function getParentReport(report) {
}

export {
getAccountIDForLogin,
getReportParticipantsTitle,
getPersonalDetailsForLogin,
isReportMessageAttachment,
findLastAccessedReport,
canEditReportAction,
Expand Down
6 changes: 3 additions & 3 deletions src/pages/DetailsPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -168,9 +168,9 @@ class DetailsPage extends React.PureComponent {
{this.props.translate(isSMSLogin ? 'common.phoneNumber' : 'common.email')}
</Text>
<CommunicationsLink value={phoneOrEmail}>
<Tooltip text={phoneOrEmail}>
<UserDetailsTooltip accountID={details.accountID}>
<Text numberOfLines={1}>{isSMSLogin ? this.props.formatPhoneNumber(phoneNumber) : details.login}</Text>
</Tooltip>
</UserDetailsTooltip>
</CommunicationsLink>
</View>
) : null}
Expand Down
6 changes: 3 additions & 3 deletions src/pages/ProfilePage.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -179,9 +179,9 @@ function ProfilePage(props) {
{props.translate(isSMSLogin ? 'common.phoneNumber' : 'common.email')}
</Text>
<CommunicationsLink value={phoneOrEmail}>
<Tooltip text={phoneOrEmail}>
<UserDetailsTooltip accountID={details.accountID}>
<Text numberOfLines={1}>{isSMSLogin ? props.formatPhoneNumber(phoneNumber) : login}</Text>
</Tooltip>
</UserDetailsTooltip>
</CommunicationsLink>
</View>
) : null}
Expand Down
Loading

0 comments on commit aa70abf

Please sign in to comment.