Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ function OptionRowLHNData({
const styles = useThemeStyles();
const {currentReportID: currentReportIDValue} = useCurrentReportIDState();
const isReportFocused = isOptionFocused && currentReportIDValue === reportID;
const {translate, localeCompare, dateFnsLocale} = useLocalize();
const {translate, localeCompare, dateFnsLocale, formatPhoneNumber} = useLocalize();
const {convertToDisplayString} = useCurrencyListActions();
const {login, accountID: currentUserAccountID} = useCurrentUserPersonalDetails();

Expand Down Expand Up @@ -172,6 +172,7 @@ function OptionRowLHNData({
policyTags,
currentUserLogin: login ?? '',
isTrackIntentUser,
formatPhoneNumber,
});

// For single-sender IOUs, trim to the sender's avatar to match the header.
Expand Down
3 changes: 2 additions & 1 deletion src/components/ReportWelcomeText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ type ReportWelcomeTextProps = {
};

function ReportWelcomeText({report, policy}: ReportWelcomeTextProps) {
const {translate, localeCompare} = useLocalize();
const {translate, localeCompare, formatPhoneNumber} = useLocalize();
const styles = useThemeStyles();
const {environmentURL} = useEnvironment();
const derivedReportName = useDerivedReportNameByReportID(report?.reportID);
Expand Down Expand Up @@ -136,6 +136,7 @@ function ReportWelcomeText({report, policy}: ReportWelcomeTextProps) {
additionalText,
isTrackIntentUser: !!isTrackIntentUser,
currentUserAccountID,
formatPhoneNumber,
});

return (
Expand Down
42 changes: 29 additions & 13 deletions src/libs/SidebarUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ import type {OptionData} from './ReportUtils';

import {isAnonymousUser} from './actions/Session';
import {getAddAgentRuleMessage, getDeleteAgentRuleMessage, getUpdateAgentRuleMessage} from './AgentRuleChangeLogUtils';
import {formatPhoneNumber as formatPhoneNumberPhoneUtils} from './LocalePhoneNumber';
import {formatList} from './Localize';
import {
getLastActorDisplayName,
Expand Down Expand Up @@ -233,6 +232,7 @@ type WelcomeMessageParams = {
additionalText?: string;
isTrackIntentUser?: boolean;
currentUserAccountID?: number;
formatPhoneNumber: LocaleContextProps['formatPhoneNumber'];
};

function compareStringDates(a: string, b: string): 0 | 1 | -1 {
Expand Down Expand Up @@ -836,6 +836,7 @@ function getOptionData({
policyTags,
currentUserLogin,
isTrackIntentUser,
formatPhoneNumber,
}: {
report: OnyxEntry<Report>;
oneTransactionThreadReport: OnyxEntry<Report>;
Expand Down Expand Up @@ -863,6 +864,7 @@ function getOptionData({
policyTags?: OnyxEntry<PolicyTagLists>;
currentUserLogin: string;
isTrackIntentUser?: boolean;
formatPhoneNumber: LocaleContextProps['formatPhoneNumber'];
}): OptionData | undefined {
// When a user signs out, Onyx is cleared. Due to the lazy rendering with a virtual list, it's possible for
// this method to be called after the Onyx data has been cleared out. In that case, it's fine to do
Expand Down Expand Up @@ -984,7 +986,7 @@ function getOptionData({
(participantPersonalDetailList || []).slice(0, 10),
hasMultipleParticipants,
localeCompare,
formatPhoneNumberPhoneUtils,
formatPhoneNumber,
translate,
undefined,
isSelfDM(report),
Expand Down Expand Up @@ -1346,6 +1348,7 @@ function getOptionData({
isReportArchived,
isTrackIntentUser,
currentUserAccountID,
formatPhoneNumber,
}).messageText ?? translate('report.noActivityYet'),
);
}
Expand All @@ -1364,6 +1367,7 @@ function getOptionData({
conciergeReportID,
derivedReportName: reportAttributesDerived?.[report.reportID]?.reportName,
isReportArchived,
formatPhoneNumber,
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
}).messageText || translate('report.noActivityYet'),
);
Expand Down Expand Up @@ -1408,7 +1412,7 @@ function getOptionData({

const reportIcons = getIcons(
report,
formatPhoneNumberPhoneUtils,
formatPhoneNumber,
translate,
personalDetails,
personalDetail?.avatar,
Expand Down Expand Up @@ -1455,6 +1459,7 @@ function getWelcomeMessage(params: WelcomeMessageParams): WelcomeMessage {
additionalText = '',
isTrackIntentUser = false,
currentUserAccountID,
formatPhoneNumber,
} = params;

const welcomeMessage: WelcomeMessage = {};
Expand All @@ -1463,7 +1468,7 @@ function getWelcomeMessage(params: WelcomeMessageParams): WelcomeMessage {
}

if (isChatRoom(report)) {
return getRoomWelcomeMessage(translate, report, invoiceReceiverPolicy, derivedReportName, isReportArchived, reportDetailsLink);
return getRoomWelcomeMessage({translate, report, invoiceReceiverPolicy, derivedReportName, isReportArchived, reportDetailsLink, formatPhoneNumber});
}

if (isPolicyExpenseChat(report)) {
Expand All @@ -1477,7 +1482,7 @@ function getWelcomeMessage(params: WelcomeMessageParams): WelcomeMessage {
welcomeMessage.messageHtml = translate(
'reportActionsView.beginningOfChatHistoryPolicyExpenseChat',
getPolicyName({report, policy, unavailableTranslation: translate('workspace.common.unavailable')}),
getDisplayNameForParticipant({accountID: report?.ownerAccountID, formatPhoneNumber: formatPhoneNumberPhoneUtils, translate}),
getDisplayNameForParticipant({accountID: report?.ownerAccountID, formatPhoneNumber, translate}),
);
welcomeMessage.messageText = Parser.htmlToText(welcomeMessage.messageHtml);
}
Expand All @@ -1494,7 +1499,7 @@ function getWelcomeMessage(params: WelcomeMessageParams): WelcomeMessage {
return welcomeMessage;
}
const isMultipleParticipant = participantPersonalDetailList.length > 1;
const displayNamesWithTooltips = getDisplayNamesWithTooltips(participantPersonalDetailList, isMultipleParticipant, localeCompare, formatPhoneNumberPhoneUtils, translate);
const displayNamesWithTooltips = getDisplayNamesWithTooltips(participantPersonalDetailList, isMultipleParticipant, localeCompare, formatPhoneNumber, translate);

if (!displayNamesWithTooltips.length) {
return welcomeMessage;
Expand All @@ -1521,14 +1526,25 @@ function getWelcomeMessage(params: WelcomeMessageParams): WelcomeMessage {
/**
* Get welcome message based on room type
*/
function getRoomWelcomeMessage(
translate: LocalizedTranslate,
report: OnyxEntry<Report>,
invoiceReceiverPolicy: OnyxEntry<Policy>,
derivedReportName: string | undefined,
type GetRoomWelcomeMessageParams = {
translate: LocalizedTranslate;
report: OnyxEntry<Report>;
invoiceReceiverPolicy: OnyxEntry<Policy>;
derivedReportName: string | undefined;
isReportArchived?: boolean;
reportDetailsLink?: string;
formatPhoneNumber: LocaleContextProps['formatPhoneNumber'];
};

function getRoomWelcomeMessage({
translate,
report,
invoiceReceiverPolicy,
derivedReportName,
isReportArchived = false,
reportDetailsLink = '',
): WelcomeMessage {
formatPhoneNumber,
}: GetRoomWelcomeMessageParams): WelcomeMessage {
const welcomeMessage: WelcomeMessage = {};
const workspaceName = getPolicyName({report, unavailableTranslation: translate('workspace.common.unavailable')});
const reportName = getReportName(report ?? undefined, derivedReportName);
Expand All @@ -1550,7 +1566,7 @@ function getRoomWelcomeMessage(
} else if (isInvoiceRoom(report)) {
const payer =
report?.invoiceReceiver?.type === CONST.REPORT.INVOICE_RECEIVER_TYPE.INDIVIDUAL
? getDisplayNameForParticipant({accountID: report?.invoiceReceiver?.accountID, formatPhoneNumber: formatPhoneNumberPhoneUtils, translate})
? getDisplayNameForParticipant({accountID: report?.invoiceReceiver?.accountID, formatPhoneNumber, translate})
: invoiceReceiverPolicy?.name;
const receiver = getPolicyName({report, unavailableTranslation: translate('workspace.common.unavailable')});
welcomeMessage.messageHtml = translate('reportActionsView.beginningOfChatHistoryInvoiceRoom', payer ?? '', receiver);
Expand Down
5 changes: 3 additions & 2 deletions src/pages/Debug/Report/DebugReportActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ type DebugReportActionsProps = {
};

function DebugReportActions({reportID}: DebugReportActionsProps) {
const {translate, datetimeToCalendarTime, localeCompare} = useLocalize();
const {translate, datetimeToCalendarTime, localeCompare, formatPhoneNumber} = useLocalize();
const styles = useThemeStyles();
const [searchValue, debouncedSearchValue, setSearchValue] = useDebouncedState('');
const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`);
Expand Down Expand Up @@ -75,6 +75,7 @@ function DebugReportActions({reportID}: DebugReportActionsProps) {
conciergeReportID,
derivedReportName,
isReportArchived,
formatPhoneNumber,
}).messageText ?? translate('report.noActivityYet'),
);
}
Expand All @@ -85,7 +86,7 @@ function DebugReportActions({reportID}: DebugReportActionsProps) {

return getReportActionMessageText(reportAction);
},
[translate, report, policy, invoiceReceiverPolicy, participantPersonalDetailList, localeCompare, conciergeReportID, derivedReportName, isReportArchived],
[translate, report, policy, invoiceReceiverPolicy, participantPersonalDetailList, localeCompare, conciergeReportID, derivedReportName, isReportArchived, formatPhoneNumber],
);

const searchedReportActions = useMemo(() => {
Expand Down
3 changes: 1 addition & 2 deletions src/pages/Travel/EnableTravel/subPages/LegalNameStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import useOnyx from '@hooks/useOnyx';
import useThemeStyles from '@hooks/useThemeStyles';

import {updateLegalName} from '@libs/actions/PersonalDetails';
import {formatPhoneNumber} from '@libs/LocalePhoneNumber';

import {validateLegalName} from '@pages/settings/Profile/PersonalDetails/BaseLegalNamePage';
import type {EnableTravelSubPageProps} from '@pages/Travel/EnableTravel/types';
Expand All @@ -24,7 +23,7 @@ import {View} from 'react-native';

function LegalNameStep({onNext}: EnableTravelSubPageProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const {translate, formatPhoneNumber} = useLocalize();
const currentUserPersonalDetails = useCurrentUserPersonalDetails();
const [privatePersonalDetails] = useOnyx(ONYXKEYS.PRIVATE_PERSONAL_DETAILS);
const [draftValues] = useOnyx(ONYXKEYS.FORMS.PERSONAL_DETAILS_FORM_DRAFT);
Expand Down
3 changes: 2 additions & 1 deletion tests/perf-test/SidebarUtils.perf-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import createPersonalDetails from '../utils/collections/personalDetails';
import createRandomPolicy from '../utils/collections/policies';
import createRandomReportAction, {getRandomDate} from '../utils/collections/reportActions';
import {createRandomReport} from '../utils/collections/reports';
import {convertToDisplayString, localeCompare, translateLocal} from '../utils/TestHelper';
import {convertToDisplayString, localeCompare, translateLocal, formatPhoneNumber} from '../utils/TestHelper';
import waitForBatchedUpdates from '../utils/waitForBatchedUpdates';

const REPORTS_COUNT = 15000;
Expand Down Expand Up @@ -94,6 +94,7 @@ describe('SidebarUtils', () => {
isReportArchived: undefined,
currentUserAccountID: 1,
currentUserLogin: CURRENT_USER_LOGIN,
formatPhoneNumber,
}),
);
});
Expand Down
101 changes: 101 additions & 0 deletions tests/ui/EnableTravel/LegalNameStepTest.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import {act, fireEvent, render, screen, waitFor} from '@testing-library/react-native';

import ComposeProviders from '@components/ComposeProviders';
import OnyxListItemProvider from '@components/OnyxListItemProvider';

import {updateLegalName} from '@libs/actions/PersonalDetails';
import type * as PersonalDetailsActions from '@libs/actions/PersonalDetails';
import createPlatformStackNavigator from '@libs/Navigation/PlatformStackNavigation/createPlatformStackNavigator';

import LegalNameStep from '@pages/Travel/EnableTravel/subPages/LegalNameStep';

import ONYXKEYS from '@src/ONYXKEYS';

import {NavigationContainer} from '@react-navigation/native';
import React from 'react';
import Onyx from 'react-native-onyx';

import waitForBatchedUpdatesWithAct from '../../utils/waitForBatchedUpdatesWithAct';

const mockFormatPhoneNumber = jest.fn((phoneNumber: string) => `formatted:${phoneNumber}`);
const mockOnNext = jest.fn();
const mockCurrentUserPersonalDetails = {
accountID: 1,
email: '+15555550100@expensify.sms',
firstName: '',
lastName: '',
};

jest.mock('@hooks/useCurrentUserPersonalDetails', () => () => mockCurrentUserPersonalDetails);

jest.mock('@hooks/useLocalize', () => () => ({
translate: (key: string) => key,
formatPhoneNumber: mockFormatPhoneNumber,
}));

jest.mock('@libs/actions/PersonalDetails', () => ({
...jest.requireActual<typeof PersonalDetailsActions>('@libs/actions/PersonalDetails'),
updateLegalName: jest.fn(),
}));

const mockUpdateLegalName = jest.mocked(updateLegalName);

const Stack = createPlatformStackNavigator();

function LegalNameStepScreen() {
return (
<LegalNameStep
isEditing={false}
onNext={mockOnNext}
onMove={jest.fn()}
policy={undefined}
policyID="policy-1"
resolvedDomain="example.com"
/>
);
}

function renderStep() {
return render(
<ComposeProviders components={[OnyxListItemProvider]}>
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="LegalNameStepScreen"
component={LegalNameStepScreen}
/>
</Stack.Navigator>
</NavigationContainer>
</ComposeProviders>,
);
}

describe('LegalNameStep', () => {
beforeAll(() => {
Onyx.init({keys: ONYXKEYS});
});

afterEach(async () => {
await act(async () => {
await Onyx.clear();
});
jest.clearAllMocks();
});

it('submits trimmed legal names with the localized phone-number formatter and advances', async () => {
renderStep();
await waitForBatchedUpdatesWithAct();

fireEvent.changeText(screen.getByLabelText('privatePersonalDetails.legalFirstName'), ' Jane ');
fireEvent.changeText(screen.getByLabelText('privatePersonalDetails.legalLastName'), ' Doe ');
await waitForBatchedUpdatesWithAct();

fireEvent.press(screen.getByText('common.next'));
await waitForBatchedUpdatesWithAct();

await waitFor(() => {
expect(mockUpdateLegalName).toHaveBeenCalledWith('Jane', 'Doe', mockFormatPhoneNumber, mockCurrentUserPersonalDetails, false);
});
expect(mockOnNext).toHaveBeenCalledTimes(1);
});
});
Loading
Loading