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

[TS migration] Migrate 'ReportWelcomeText.js' component to TypeScript #33251

Merged
merged 8 commits into from
Jan 2, 2024
Original file line number Diff line number Diff line change
@@ -1,114 +1,86 @@
import lodashGet from 'lodash/get';
import PropTypes from 'prop-types';
import React from 'react';
import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';
import {OnyxEntry, withOnyx} from 'react-native-onyx';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import Navigation from '@libs/Navigation/Navigation';
import * as OptionsListUtils from '@libs/OptionsListUtils';
import * as PolicyUtils from '@libs/PolicyUtils';
import * as ReportUtils from '@libs/ReportUtils';
import reportPropTypes from '@pages/reportPropTypes';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type {PersonalDetailsList, Policy, Report} from '@src/types/onyx';
import Text from './Text';
import UserDetailsTooltip from './UserDetailsTooltip';
import withLocalize, {withLocalizePropTypes} from './withLocalize';

const personalDetailsPropTypes = PropTypes.shape({
/** The login of the person (either email or phone number) */
login: PropTypes.string,

/** The URL of the person's avatar (there should already be a default avatar if
the person doesn't have their own avatar uploaded yet, except for anon users) */
avatar: PropTypes.string,

/** This is either the user's full name, or their login if full name is an empty string */
displayName: PropTypes.string,
});

const propTypes = {
/** The report currently being looked at */
report: reportPropTypes,

/** The policy object for the current route */
policy: PropTypes.shape({
/** The name of the policy */
name: PropTypes.string,

/** The URL for the policy avatar */
avatar: PropTypes.string,
}),

/* Onyx Props */

type ReportWelcomeTextOnyxProps = {
/** All of the personal details for everyone */
personalDetails: PropTypes.objectOf(personalDetailsPropTypes),

...withLocalizePropTypes,
personalDetails: OnyxEntry<PersonalDetailsList>;
};

const defaultProps = {
report: {},
policy: {},
personalDetails: {},
type ReportWelcomeTextProps = ReportWelcomeTextOnyxProps & {
/** The report currently being looked at */
report: OnyxEntry<Report>;

/** The policy for the current route */
policy: OnyxEntry<Policy>;
};

function ReportWelcomeText(props) {
function ReportWelcomeText({report, policy, personalDetails}: ReportWelcomeTextProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();
const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(props.report);
const isChatRoom = ReportUtils.isChatRoom(props.report);
const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(report);
const isChatRoom = ReportUtils.isChatRoom(report);
const isDefault = !(isChatRoom || isPolicyExpenseChat);
const participantAccountIDs = lodashGet(props.report, 'participantAccountIDs', []);
const participantAccountIDs = report?.participantAccountIDs ?? [];
const isMultipleParticipant = participantAccountIDs.length > 1;
const displayNamesWithTooltips = ReportUtils.getDisplayNamesWithTooltips(
OptionsListUtils.getPersonalDetailsForAccountIDs(participantAccountIDs, props.personalDetails),
// @ts-expect-error TODO: Remove this once OptionsListUtils (https://github.com/Expensify/App/issues/24921) is migrated to TypeScript.
OptionsListUtils.getPersonalDetailsForAccountIDs(participantAccountIDs, personalDetails),
isMultipleParticipant,
);
const isUserPolicyAdmin = PolicyUtils.isPolicyAdmin(props.policy);
const roomWelcomeMessage = ReportUtils.getRoomWelcomeMessage(props.report, isUserPolicyAdmin);
const moneyRequestOptions = ReportUtils.getMoneyRequestOptions(props.report, participantAccountIDs);
const isUserPolicyAdmin = PolicyUtils.isPolicyAdmin(policy);
const roomWelcomeMessage = ReportUtils.getRoomWelcomeMessage(report, isUserPolicyAdmin);
const moneyRequestOptions = ReportUtils.getMoneyRequestOptions(report, participantAccountIDs);

return (
<>
<View>
<Text style={[styles.textHero]}>
{isChatRoom ? props.translate('reportActionsView.welcomeToRoom', {roomName: ReportUtils.getReportName(props.report)}) : props.translate('reportActionsView.sayHello')}
{isChatRoom ? translate('reportActionsView.welcomeToRoom', {roomName: ReportUtils.getReportName(report)}) : translate('reportActionsView.sayHello')}
</Text>
</View>
<Text style={[styles.mt3, styles.mw100]}>
{isPolicyExpenseChat && (
<>
<Text>{props.translate('reportActionsView.beginningOfChatHistoryPolicyExpenseChatPartOne')}</Text>
<Text style={[styles.textStrong]}>{ReportUtils.getDisplayNameForParticipant(props.report.ownerAccountID)}</Text>
<Text>{props.translate('reportActionsView.beginningOfChatHistoryPolicyExpenseChatPartTwo')}</Text>
<Text style={[styles.textStrong]}>{ReportUtils.getPolicyName(props.report)}</Text>
<Text>{props.translate('reportActionsView.beginningOfChatHistoryPolicyExpenseChatPartThree')}</Text>
<Text>{translate('reportActionsView.beginningOfChatHistoryPolicyExpenseChatPartOne')}</Text>
<Text style={[styles.textStrong]}>{ReportUtils.getDisplayNameForParticipant(report?.ownerAccountID)}</Text>
<Text>{translate('reportActionsView.beginningOfChatHistoryPolicyExpenseChatPartTwo')}</Text>
<Text style={[styles.textStrong]}>{ReportUtils.getPolicyName(report)}</Text>
<Text>{translate('reportActionsView.beginningOfChatHistoryPolicyExpenseChatPartThree')}</Text>
</>
)}
{isChatRoom && (
<>
<Text>{roomWelcomeMessage.phrase1}</Text>
{roomWelcomeMessage.showReportName && (
{roomWelcomeMessage.showReportName && !!report?.reportID && (
<Text
Copy link
Contributor Author

Choose a reason for hiding this comment

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

ROUTES.REPORT_WITH_ID_DETAILS.getRoute requires string, not undefined type, so !!report?.reportID is added here for the conditional rendering

Copy link
Contributor

Choose a reason for hiding this comment

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

@nikosamofa I think is better to do this condition inside onPress callback instead of here, e.g.:

onPress={() => {
    if (!report?.reportID) {
        return;
    }

    Navigation.navigate(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(report.reportID));
}}

You can also extract this onPress callback to a separate function for better code visibility.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Addressed by 73b8b7f

style={[styles.textStrong]}
onPress={() => Navigation.navigate(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(props.report.reportID))}
onPress={() => Navigation.navigate(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(report.reportID))}
suppressHighlighting
>
{ReportUtils.getReportName(props.report)}
{ReportUtils.getReportName(report)}
</Text>
)}
{roomWelcomeMessage.phrase2 !== undefined && <Text>{roomWelcomeMessage.phrase2}</Text>}
</>
)}
{isDefault && (
<Text>
<Text>{props.translate('reportActionsView.beginningOfChatHistory')}</Text>
{_.map(displayNamesWithTooltips, ({displayName, pronouns, accountID}, index) => (
<Text key={`${displayName}${pronouns}${index}`}>
<Text>{translate('reportActionsView.beginningOfChatHistory')}</Text>
{displayNamesWithTooltips.map(({displayName, pronouns, accountID}, index) => (
<Text key={`${displayName}${pronouns}${accountID}`}>
<UserDetailsTooltip accountID={accountID}>
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure if you have already noticed this, but I changed

<Text key={`${displayName}${pronouns}${index}`}>

to

<Text key={`${displayName}${pronouns}${accountID}`}>

ESlint complained Do not use Array index in keys react/no-array-index-key

I assume accountID will be unique inside this array, can you confirm it?

Copy link
Contributor

Choose a reason for hiding this comment

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

@nikosamofa Another alternative (if accountID can't be used) is to suppress the rule with // eslint-disable-next-line react/no-array-index-key.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Addressed by 73b8b7f

{ReportUtils.isOptimisticPersonalDetail(accountID) ? (
<Text style={[styles.textStrong]}>{displayName}</Text>
Expand All @@ -122,31 +94,24 @@ function ReportWelcomeText(props) {
</Text>
)}
</UserDetailsTooltip>
{!_.isEmpty(pronouns) && <Text>{` (${pronouns})`}</Text>}
{!!pronouns && <Text>{` (${pronouns})`}</Text>}
{index === displayNamesWithTooltips.length - 1 && <Text>.</Text>}
{index === displayNamesWithTooltips.length - 2 && <Text>{` ${props.translate('common.and')} `}</Text>}
{index === displayNamesWithTooltips.length - 2 && <Text>{` ${translate('common.and')} `}</Text>}
{index < displayNamesWithTooltips.length - 2 && <Text>, </Text>}
</Text>
))}
</Text>
)}
{(moneyRequestOptions.includes(CONST.IOU.TYPE.SEND) || moneyRequestOptions.includes(CONST.IOU.TYPE.REQUEST)) && (
<Text>{props.translate('reportActionsView.usePlusButton')}</Text>
)}
{(moneyRequestOptions.includes(CONST.IOU.TYPE.SEND) || moneyRequestOptions.includes(CONST.IOU.TYPE.REQUEST)) && <Text>{translate('reportActionsView.usePlusButton')}</Text>}
</Text>
</>
);
}

ReportWelcomeText.defaultProps = defaultProps;
ReportWelcomeText.propTypes = propTypes;
ReportWelcomeText.displayName = 'ReportWelcomeText';

export default compose(
withLocalize,
withOnyx({
personalDetails: {
key: ONYXKEYS.PERSONAL_DETAILS_LIST,
},
}),
)(ReportWelcomeText);
export default withOnyx<ReportWelcomeTextProps, ReportWelcomeTextOnyxProps>({
personalDetails: {
key: ONYXKEYS.PERSONAL_DETAILS_LIST,
},
})(ReportWelcomeText);
Loading