11import { useFocusEffect , useIsFocused } from '@react-navigation/native' ;
2- import React , { useCallback , useEffect , useRef , useState } from 'react' ;
2+ import React , { useCallback , useEffect , useMemo , useRef , useState } from 'react' ;
33// eslint-disable-next-line no-restricted-imports
44import { InteractionManager , View } from 'react-native' ;
55import type { OnyxEntry } from 'react-native-onyx' ;
66import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails' ;
77import useIsInLandscapeMode from '@hooks/useIsInLandscapeMode' ;
8+ import useLocalize from '@hooks/useLocalize' ;
89import { MouseProvider } from '@hooks/useMouseContext' ;
910import usePermissions from '@hooks/usePermissions' ;
1011import usePolicyForMovingExpenses from '@hooks/usePolicyForMovingExpenses' ;
@@ -64,6 +65,9 @@ type MoneyRequestConfirmationListProps = {
6465 /** Callback to inform parent modal of success */
6566 onConfirm ?: ( selectedParticipants : Participant [ ] ) => void ;
6667
68+ /** When set, used in the new manual expense flow to open the parent-owned participant picker instead of navigating away */
69+ onOpenParticipantPicker ?: ( ) => void ;
70+
6771 /** Callback to parent modal to pay someone */
6872 onSendMoney ?: ( paymentMethod : PaymentMethodType | undefined ) => void ;
6973
@@ -164,6 +168,7 @@ function MoneyRequestConfirmationList({
164168 transaction,
165169 onSendMoney,
166170 onConfirm,
171+ onOpenParticipantPicker,
167172 iouType = CONST . IOU . TYPE . SUBMIT ,
168173 isOdometerDistanceRequest = false ,
169174 isLoadingReceipt = false ,
@@ -204,6 +209,7 @@ function MoneyRequestConfirmationList({
204209 const { isDelegateAccessRestricted} = useDelegateNoAccessState ( ) ;
205210 const { showDelegateNoAccessModal} = useDelegateNoAccessActions ( ) ;
206211 const isInLandscapeMode = useIsInLandscapeMode ( ) ;
212+ const { translate} = useLocalize ( ) ;
207213
208214 const { isTestReceipt, shouldShowProductTrainingTooltip, renderProductTrainingTooltip} = useReceiptTraining ( {
209215 transaction,
@@ -257,7 +263,6 @@ function MoneyRequestConfirmationList({
257263 iouCurrencyCode,
258264 } ) ;
259265
260- // A flag for showing the categories field
261266 const shouldShowCategories = isTrackExpense
262267 ? ! policy || shouldSelectPolicy || ! ! iouCategory || hasEnabledOptions ( Object . values ( policyCategories ?? { } ) )
263268 : ( isPolicyExpenseChat || isTypeInvoice ) && ( ! ! iouCategory || hasEnabledOptions ( Object . values ( policyCategories ?? { } ) ) ) ;
@@ -293,6 +298,9 @@ function MoneyRequestConfirmationList({
293298 prevSubRates,
294299 } ) ;
295300
301+ const isManualRequest = transaction ?. iouRequestType === CONST . IOU . REQUEST_TYPE . MANUAL ;
302+ const shouldForceTopEmptySections = isNewManualExpenseFlowEnabled && ( iouType === CONST . IOU . TYPE . CREATE || isManualRequest || isScanRequest ) ;
303+
296304 const isFocused = useIsFocused ( ) ;
297305
298306 const [ didConfirm , setDidConfirm ] = useState ( isConfirmed ) ;
@@ -307,7 +315,7 @@ function MoneyRequestConfirmationList({
307315 const isTypeSplit = iouType === CONST . IOU . TYPE . SPLIT ;
308316 const shouldShowReadOnlySplits = isPolicyExpenseChat || isReadOnly || isScanRequest ;
309317
310- const { formError, setFormError, clearFormErrors, shouldDisplayFieldError, isMerchantEmpty, isMerchantRequired, errorMessage} = useFormErrorManagement ( {
318+ const { formError, setFormError, clearFormErrors, shouldDisplayFieldError, isMerchantEmpty, isMerchantFieldValid , isMerchantRequired, errorMessage} = useFormErrorManagement ( {
311319 transaction,
312320 transactionReport,
313321 iouMerchant,
@@ -360,6 +368,24 @@ function MoneyRequestConfirmationList({
360368 const selectedParticipants = selectedParticipantsProp . filter ( ( participant ) => participant . selected ) ;
361369 const payeePersonalDetails = payeePersonalDetailsProp ?? currentUserPersonalDetails ;
362370
371+ const participantRowErrors = useMemo ( ( ) => {
372+ if ( formError !== 'iou.error.noParticipantSelected' && formError !== 'violations.missingAttendees' ) {
373+ return undefined ;
374+ }
375+ return { participants : translate ( formError ) } ;
376+ } , [ formError , translate ] ) ;
377+
378+ useEffect ( ( ) => {
379+ if ( selectedParticipants . length === 0 ) {
380+ return ;
381+ }
382+ clearFormErrors ( [ 'iou.error.noParticipantSelected' ] ) ;
383+ } , [ selectedParticipants . length , clearFormErrors ] ) ;
384+
385+ const dismissParticipantRowError = useCallback ( ( ) => {
386+ clearFormErrors ( [ 'iou.error.noParticipantSelected' , 'violations.missingAttendees' ] ) ;
387+ } , [ clearFormErrors ] ) ;
388+
363389 const { splitParticipants, getSplitSectionHeader} = useSplitParticipants ( {
364390 isTypeSplit,
365391 shouldShowReadOnlySplits,
@@ -375,6 +401,8 @@ function MoneyRequestConfirmationList({
375401 const sections = useConfirmationSections ( {
376402 isTypeSplit,
377403 shouldHideToSection,
404+ shouldForceTopEmptySections,
405+ participantRowErrors,
378406 canEditParticipant,
379407 payeePersonalDetails,
380408 splitParticipants,
@@ -390,8 +418,13 @@ function MoneyRequestConfirmationList({
390418 return ;
391419 }
392420
421+ if ( isNewManualExpenseFlowEnabled ) {
422+ onOpenParticipantPicker ?.( ) ;
423+ return ;
424+ }
425+
393426 const newIOUType = iouType === CONST . IOU . TYPE . SUBMIT || iouType === CONST . IOU . TYPE . TRACK ? CONST . IOU . TYPE . CREATE : iouType ;
394- Navigation . navigate ( ROUTES . MONEY_REQUEST_STEP_PARTICIPANTS . getRoute ( newIOUType , transactionID , transaction . reportID , Navigation . getActiveRoute ( ) , action ) ) ;
427+ Navigation . navigate ( ROUTES . MONEY_REQUEST_STEP_PARTICIPANTS . getRoute ( newIOUType , transactionID , transaction ? .reportID , Navigation . getActiveRoute ( ) , action ) ) ;
395428 } ;
396429
397430 const { validate} = useConfirmationValidation ( {
@@ -412,15 +445,16 @@ function MoneyRequestConfirmationList({
412445 currentUserPersonalDetails,
413446 isEditingSplitBill,
414447 isMerchantRequired,
448+ isMerchantFieldValid,
415449 isMerchantEmpty,
416450 shouldDisplayFieldError,
417451 shouldShowTax,
418452 isDistanceRequest,
419453 isDistanceRequestWithPendingRoute,
420454 isPerDiemRequest,
421455 isTimeRequest,
422- isNewManualExpenseFlowEnabled,
423456 routeError,
457+ isNewManualExpenseFlowEnabled,
424458 } ) ;
425459
426460 const confirm = buildConfirmAction ( {
@@ -443,13 +477,17 @@ function MoneyRequestConfirmationList({
443477 const focusTimeoutRef = useRef < NodeJS . Timeout | null > ( null ) ;
444478 useFocusEffect (
445479 useCallback ( ( ) => {
480+ // Blurring the active element after transition fights AmountField focus in the new manual flow (RHP reopen).
481+ if ( isNewManualExpenseFlowEnabled ) {
482+ return undefined ;
483+ }
446484 focusTimeoutRef . current = setTimeout ( ( ) => {
447485 InteractionManager . runAfterInteractions ( ( ) => {
448486 blurActiveElement ( ) ;
449487 } ) ;
450488 } , CONST . ANIMATED_TRANSITION ) ;
451489 return ( ) => focusTimeoutRef . current && clearTimeout ( focusTimeoutRef . current ) ;
452- } , [ ] ) ,
490+ } , [ isNewManualExpenseFlowEnabled ] ) ,
453491 ) ;
454492
455493 const isCompactMode = ! showMoreFields && isScanRequest && ! isInLandscapeMode ;
@@ -489,6 +527,8 @@ function MoneyRequestConfirmationList({
489527 formattedAmount = { formattedAmount }
490528 formattedAmountPerAttendee = { formattedAmountPerAttendee }
491529 formError = { formError }
530+ clearFormErrors = { clearFormErrors }
531+ setFormError = { setFormError }
492532 hasRoute = { hasRoute }
493533 iouType = { iouType }
494534 isCategoryRequired = { isCategoryRequired }
@@ -532,6 +572,7 @@ function MoneyRequestConfirmationList({
532572 isDescriptionRequired = { isDescriptionRequired }
533573 showMoreFields = { showMoreFields }
534574 setShowMoreFields = { setShowMoreFields }
575+ onSubmitForm = { confirm }
535576 />
536577 </ View >
537578 ) ;
@@ -603,6 +644,7 @@ function MoneyRequestConfirmationList({
603644 sections = { sections }
604645 ListItem = { BareUserListItem }
605646 onSelectRow = { navigateToParticipantPage }
647+ onDismissError = { dismissParticipantRowError }
606648 shouldSingleExecuteRowSelect
607649 shouldPreventDefaultFocusOnSelectRow
608650 shouldShowListEmptyContent = { false }
0 commit comments