diff --git a/src/components/MoneyRequestConfirmationList/hooks/useConfirmationSections.ts b/src/components/MoneyRequestConfirmationList/hooks/useConfirmationSections.ts index d029d311fa05..556020210c30 100644 --- a/src/components/MoneyRequestConfirmationList/hooks/useConfirmationSections.ts +++ b/src/components/MoneyRequestConfirmationList/hooks/useConfirmationSections.ts @@ -64,14 +64,14 @@ function useConfirmationSections({ selectedParticipants, getSplitSectionHeader, }: UseConfirmationSectionsParams) { - const {translate} = useLocalize(); + const {translate, formatPhoneNumber} = useLocalize(); const options: Array> = []; if (isTypeSplit) { options.push( { title: translate('moneyRequestConfirmationList.paidBy'), - data: [getIOUConfirmationOptionsFromPayeePersonalDetail(payeePersonalDetails, translate)], + data: [getIOUConfirmationOptionsFromPayeePersonalDetail(payeePersonalDetails, translate, formatPhoneNumber)], sectionIndex: 0, }, { diff --git a/src/components/MoneyRequestConfirmationList/hooks/useSplitParticipants.tsx b/src/components/MoneyRequestConfirmationList/hooks/useSplitParticipants.tsx index 959d934fe04e..fb41a877c4ba 100644 --- a/src/components/MoneyRequestConfirmationList/hooks/useSplitParticipants.tsx +++ b/src/components/MoneyRequestConfirmationList/hooks/useSplitParticipants.tsx @@ -70,7 +70,7 @@ function useSplitParticipants({ currentUserAccountID, }: UseSplitParticipantsParams) { const styles = useThemeStyles(); - const {translate} = useLocalize(); + const {translate, formatPhoneNumber} = useLocalize(); const {convertToDisplayString, convertToDisplayStringWithoutCurrency, getCurrencySymbol} = useCurrencyListActions(); const transactionID = transaction?.transactionID; @@ -86,7 +86,7 @@ function useSplitParticipants({ return []; } - const payeeOption = getIOUConfirmationOptionsFromPayeePersonalDetail(payeePersonalDetails, translate); + const payeeOption = getIOUConfirmationOptionsFromPayeePersonalDetail(payeePersonalDetails, translate, formatPhoneNumber); if (shouldShowReadOnlySplits) { return [payeeOption, ...selectedParticipants].map((participantOption: Participant) => { const isPayer = participantOption.accountID === payeeOption.accountID; diff --git a/src/libs/OptionsListUtils/index.ts b/src/libs/OptionsListUtils/index.ts index 06939caa81fa..12a2d8d2aa0a 100644 --- a/src/libs/OptionsListUtils/index.ts +++ b/src/libs/OptionsListUtils/index.ts @@ -2967,13 +2967,16 @@ function getSearchOptions({ /** * Build the IOUConfirmation options for showing the payee personalDetail */ -function getIOUConfirmationOptionsFromPayeePersonalDetail(personalDetail: OnyxEntry, translate: LocalizedTranslate, amountText?: string): PayeePersonalDetails { +function getIOUConfirmationOptionsFromPayeePersonalDetail( + personalDetail: OnyxEntry, + translate: LocalizedTranslate, + formatPhoneNumber: LocaleContextProps['formatPhoneNumber'], + amountText?: string, +): PayeePersonalDetails { const login = personalDetail?.login ?? ''; return { - text: formatPhoneNumberPhoneUtils(temporaryGetDisplayNameOrDefault({passedPersonalDetails: personalDetail, defaultValue: login, translate})), - alternateText: formatPhoneNumberPhoneUtils( - login || temporaryGetDisplayNameOrDefault({passedPersonalDetails: personalDetail, defaultValue: '', shouldFallbackToHidden: false, translate}), - ), + text: formatPhoneNumber(temporaryGetDisplayNameOrDefault({passedPersonalDetails: personalDetail, defaultValue: login, translate})), + alternateText: formatPhoneNumber(login || temporaryGetDisplayNameOrDefault({passedPersonalDetails: personalDetail, defaultValue: '', shouldFallbackToHidden: false, translate})), icons: [ { source: personalDetail?.avatar ?? FallbackAvatar, diff --git a/tests/unit/OptionsListUtilsTest.tsx b/tests/unit/OptionsListUtilsTest.tsx index f18890a48899..3718687f0aa7 100644 --- a/tests/unit/OptionsListUtilsTest.tsx +++ b/tests/unit/OptionsListUtilsTest.tsx @@ -25,6 +25,7 @@ import { filterWorkspaceChats, formatMemberForList, formatSectionsFromSearchTerm, + getIOUConfirmationOptionsFromPayeePersonalDetail, getLastActorDisplayName, getLastActorDisplayNameFromLastVisibleActions, getLastMessageTextForReport, @@ -94,7 +95,7 @@ import {createRandomReport, createRegularChat} from '../utils/collections/report import createRandomTransaction from '../utils/collections/transaction'; import createMock from '../utils/createMock'; import {getFakeAdvancedReportAction} from '../utils/LHNTestUtils'; -import {localeCompare, translateLocal} from '../utils/TestHelper'; +import {formatPhoneNumber, localeCompare, translateLocal} from '../utils/TestHelper'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; jest.mock('@rnmapbox/maps', () => { @@ -4479,6 +4480,22 @@ describe('OptionsListUtils', () => { }); }); + describe('getIOUConfirmationOptionsFromPayeePersonalDetail', () => { + it('formats payee text and alternate text with the injected phone-number formatter', () => { + const personalDetail = { + accountID: 42, + login: '+18332403627@expensify.sms', + keyForList: '+18332403627@expensify.sms', + reportID: '', + }; + + const result = getIOUConfirmationOptionsFromPayeePersonalDetail(personalDetail, translateLocal, formatPhoneNumber); + + expect(result.text).toBe('(833) 240-3627'); + expect(result.alternateText).toBe('(833) 240-3627'); + }); + }); + describe('getLastMessageTextForReport', () => { describe('getReportPreviewMessage', () => { it('should format report preview message correctly for non-policy expense chat with IOU action', async () => { diff --git a/tests/unit/hooks/useConfirmationSections.test.tsx b/tests/unit/hooks/useConfirmationSections.test.tsx index 6b20d2c5df22..835e36a005e5 100644 --- a/tests/unit/hooks/useConfirmationSections.test.tsx +++ b/tests/unit/hooks/useConfirmationSections.test.tsx @@ -18,6 +18,7 @@ import waitForBatchedUpdatesWithAct from '../../utils/waitForBatchedUpdatesWithA type Params = Parameters[0]; const payee = {accountID: 1, login: 'me@test.com'} as CurrentUserPersonalDetails; +const smsPayee = {accountID: 3, login: '+18332403627@expensify.sms'} as CurrentUserPersonalDetails; const otherParticipant = {accountID: 2, login: 'other@test.com', keyForList: '2'} as unknown as Participant; const splitParticipant = {accountID: 2, keyForList: '2', login: 'other@test.com'} as Participant & {keyForList: string}; @@ -56,6 +57,11 @@ describe('useConfirmationSections', () => { expect(result.current.at(1)?.data).toHaveLength(1); }); + it('formats split payee SMS login using the localized phone-number formatter', () => { + const {result} = renderHook(() => useConfirmationSections(makeBase({isTypeSplit: true, payeePersonalDetails: smsPayee as OnyxTypes.PersonalDetails})), {wrapper: Wrapper}); + expect(result.current.at(0)?.data.at(0)?.text).toBe('(833) 240-3627'); + }); + it('produces a single "to" section for non-split types', () => { const {result} = renderHook(() => useConfirmationSections(makeBase()), {wrapper: Wrapper}); expect(result.current).toHaveLength(1); diff --git a/tests/unit/hooks/useSplitParticipants.test.tsx b/tests/unit/hooks/useSplitParticipants.test.tsx index a34c24e57894..d8aa97b0cfd1 100644 --- a/tests/unit/hooks/useSplitParticipants.test.tsx +++ b/tests/unit/hooks/useSplitParticipants.test.tsx @@ -48,6 +48,7 @@ jest.mock('@components/MoneyRequestAmountInput', () => { type Params = Parameters[0]; const payee = {accountID: 1, login: 'me@test.com'} as CurrentUserPersonalDetails; +const smsPayee = {accountID: 3, login: '+18332403627@expensify.sms'} as CurrentUserPersonalDetails; const otherParticipant = {accountID: 2, login: 'other@test.com', keyForList: '2'} as unknown as Participant; function makeBase(overrides: Partial = {}): Params { @@ -104,6 +105,11 @@ describe('useSplitParticipants', () => { expect(result.current.splitParticipants.at(1)?.accountID).toBe(otherParticipant.accountID); }); + it('formats payee SMS login using the localized phone-number formatter', () => { + const {result} = renderHook(() => useSplitParticipants(makeBase({isTypeSplit: true, payeePersonalDetails: smsPayee as OnyxTypes.PersonalDetails})), {wrapper: Wrapper}); + expect(result.current.splitParticipants.at(0)?.text).toBe('(833) 240-3627'); + }); + it('marks split rows as non-interactive', () => { const {result} = renderHook(() => useSplitParticipants(makeBase({isTypeSplit: true})), {wrapper: Wrapper}); for (const row of result.current.splitParticipants) {