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

Cloudpresser/user details tooltip #20276

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
614b283
feat(userTooltip): init
cloudpresser Jun 6, 2023
f856ce5
feat(reportActionItem): use userTooltip
cloudpresser Jun 6, 2023
e3f2fc1
fix(userTooltip): proper boolean conditional
cloudpresser Jun 6, 2023
8c6a4b7
fix(userDetailsTooltip): rename index
cloudpresser Jun 6, 2023
198aa5b
fix(userDetailsTooltip): update imports
cloudpresser Jun 6, 2023
34f12e9
style(userDetailsTooltip): use function instead of const
cloudpresser Jun 6, 2023
e6eb717
style(userDetailsTooltip): cast string to boolean
cloudpresser Jun 6, 2023
f7bde40
style(userDetailsTooltip): don't destructure props
cloudpresser Jun 6, 2023
9840b18
fix(userDetailsTooltip): useCallback
cloudpresser Jun 6, 2023
5722575
style(userDetailsTooltip): new line
cloudpresser Jun 6, 2023
4fa3e52
fix(userDetailsTooltip): propTypes
cloudpresser Jun 6, 2023
4535ee0
feat(userDetailsTooltip): correct styles
cloudpresser Jun 6, 2023
48f5af8
feat(userDetailsTooltip): use login instead of handle
cloudpresser Jun 7, 2023
48557bb
feat(userDetailsTooltip): use on LHN users
cloudpresser Jun 7, 2023
1d9a457
chore: lint
cloudpresser Jun 7, 2023
1e94377
Merge remote-tracking branch 'upstream/main' into cloudpresser/userDe…
cloudpresser Jun 8, 2023
aaa593b
feat(userDetailsTooltip): use it in mentions
cloudpresser Jun 8, 2023
709a6b3
feat(userDetailsTooltip): use it in detailsPage
cloudpresser Jun 8, 2023
efd0580
Revert "feat(userDetailsTooltip): use it in mentions"
cloudpresser Jun 9, 2023
6d2736c
refactor(userDetailsTooltip): use accountID
cloudpresser Jun 10, 2023
77cb99c
feat(userDetailsTooltip): use it on report welcome
cloudpresser Jun 10, 2023
d6fadfc
fix(userDetailsTooltip): render issue
cloudpresser Jun 10, 2023
24aa674
fix(sideBarLinks): pass accountID to avatar
cloudpresser Jun 10, 2023
7941eca
fix(sidebarLinks): revert tooltip changes
cloudpresser Jun 10, 2023
b0b1a05
Update src/components/DisplayNames/displayNamesPropTypes.js
cloudpresser Jun 10, 2023
da0dcd3
Update src/components/UserDetailsTooltip/userDetailsTooltipPropTypes.js
cloudpresser Jun 10, 2023
0f3c1f2
Update src/components/DisplayNames/displayNamesPropTypes.js
cloudpresser Jun 10, 2023
ff1a4ec
Update src/components/UserDetailsTooltip/userDetailsTooltipPropTypes.js
cloudpresser Jun 10, 2023
9ac3b90
fix(reportActionItem): fragment props
cloudpresser Jun 10, 2023
a7c5077
fix(reportActionItemMessage): pass accountID
cloudpresser Jun 10, 2023
0fed1bd
chore: eslint --fix
cloudpresser Jun 10, 2023
b656d43
chore: remove unused prop
cloudpresser Jun 10, 2023
cf3a4ad
fix: fallback avatar not avatarSource
cloudpresser Jun 11, 2023
8f85240
fix(userDetailsTooltip): avatar propType
cloudpresser Jun 11, 2023
1c14280
feat(userDetailsTooltip): remove sms domain
cloudpresser Jun 11, 2023
3608e48
chore: lint
cloudpresser Jun 11, 2023
a285d7e
fix(userDetailsTooltip): condition to hide
cloudpresser Jun 11, 2023
8eaf9bf
chore: use lodashGet
cloudpresser Jun 11, 2023
65a1103
chore: use lodashget
cloudpresser Jun 11, 2023
07fd999
test: update expectations
cloudpresser Jun 11, 2023
68c1ed0
chore: rename UserDetailsTooltip.js to index.js
cloudpresser Jun 11, 2023
e1dd0bf
chore: prettier
cloudpresser Jun 11, 2023
1ba85f2
feat(userDetailsTooltip): early return
cloudpresser Jun 11, 2023
8bc46ce
fix: add concierge email on name for tooltip
cloudpresser Jun 11, 2023
3922f09
chore: prettier
cloudpresser Jun 11, 2023
ccc7229
fix: styles
cloudpresser Jun 11, 2023
245a284
refactor: create getAccoutIdForLogin function
cloudpresser Jun 11, 2023
fca8b1e
test: update expectations
cloudpresser Jun 11, 2023
a97bf9b
feat(getDisplayNamesWithTooltips): use accountID directly
cloudpresser Jun 11, 2023
89e626a
chore: prettier
cloudpresser Jun 12, 2023
c275f20
fix: displayNamesPropTypes accountID
cloudpresser Jun 12, 2023
50ef668
fix: accept numbers or strings
cloudpresser Jun 12, 2023
56bb67b
Update src/components/MultipleAvatars.js
cloudpresser Jun 12, 2023
a8455fb
feat: hide email when displayName is equal
cloudpresser Jun 12, 2023
b844737
fix: do not return text inside view
cloudpresser Jun 12, 2023
3b52187
Update src/components/UserDetailsTooltip/index.js
cloudpresser Jun 12, 2023
cb8c6ca
test: update displayNamesWithTooltips expectation
cloudpresser Jun 12, 2023
562244c
feat(displayNamesWithTooltips): avatar and login
cloudpresser Jun 12, 2023
a0ea50d
feat(displayNames): fallback tooltips
cloudpresser Jun 12, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Member

Choose a reason for hiding this comment

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

We didn't properly migrate the shouldShowTooltip prop to UserDetailsTooltip which was being applied indirectly to the old Tooltip via tooltipTexts at line 72.

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>
Comment on lines +29 to +31
Copy link
Collaborator

@Santhosh-Sellavel Santhosh-Sellavel Jul 14, 2023

Choose a reason for hiding this comment

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

The user with mobile number login case was handled incorrectly resulting in the regression showing the phone number twice with different formatting. Handled it by improving the checks!

Copy link
Collaborator

Choose a reason for hiding this comment

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

We added the login handling here, but we didn't account for overflowing content on the tooltip with a very long email. This caused a regression here.

</View>
),
[userDetails.avatar, userDetails.displayName, userDetails.login],
);

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

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

Choose a reason for hiding this comment

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

Tooltip supports renderTooltipContentKey prop for resize when renderTooltipContent dynamically changes. As it was missed to pass this key, caused minor regression below:

Copy link
Contributor

Choose a reason for hiding this comment

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

Tooltip also supports shiftHorizontal to adjust horizontal position of tooltip. As it was missed to pass this key, caused another minor regression below:

}

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

export default withOnyx({
personalDetailsList: {
key: ONYXKEYS.PERSONAL_DETAILS_LIST,
},
})(UserDetailsTooltip);
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) {
Copy link
Contributor

Choose a reason for hiding this comment

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

@cloudpresser @puneetlath
This method no longer works after accountID migration because we have accountID as they and not login anymore. This is causing the following issues:

Copy link
Contributor

Choose a reason for hiding this comment

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

Issue reported here: #20934

Copy link
Contributor

Choose a reason for hiding this comment

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

@esh-g If the the method no longer works after accountID migration , then the migration PR caused this regression , it broke this function which was working fine before the migration.

So it's not a regression from this PR.

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) : '';
cloudpresser marked this conversation as resolved.
Show resolved Hide resolved
const avatar = UserUtils.getDefaultAvatar(participant.login);
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this caused a regression where we are unable to load a user's uploaded/custom avatar in some cases as identified on #20683

I believe I've got a fix so will push it up and request reviews from ya'll.

Copy link
Contributor

Choose a reason for hiding this comment

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

PR is here #20727

Copy link
Contributor

Choose a reason for hiding this comment

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

@bondydaa The root cause of that issue is that we are not passing the accountID. The avatar you are referring to should only be displayed in case we have a user without an accountID (I believe these cases are mentions or when you search for a new account).

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,
cloudpresser marked this conversation as resolved.
Show resolved Hide resolved
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
Loading