11import type { StackScreenProps } from '@react-navigation/stack' ;
22import { hasSeenTourSelector } from '@selectors/Onboarding' ;
33import { validTransactionDraftsSelector } from '@selectors/TransactionDraft' ;
4- import React , { useCallback , useEffect , useMemo , useState } from 'react' ;
4+ import React , { useCallback , useEffect , useMemo , useRef , useState } from 'react' ;
55import { View } from 'react-native' ;
66import type { OnyxEntry } from 'react-native-onyx' ;
77import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView' ;
@@ -102,6 +102,10 @@ function SubmitDetailsPage({
102102 const currentUserPersonalDetails = useCurrentUserPersonalDetails ( ) ;
103103 const personalPolicy = usePersonalPolicy ( ) ;
104104 const [ startLocationPermissionFlow , setStartLocationPermissionFlow ] = useState ( false ) ;
105+ const [ selectedParticipantList , setSelectedParticipantList ] = useState < Participant [ ] > ( [ ] ) ;
106+ const [ isConfirming , setIsConfirming ] = useState ( false ) ;
107+ const formHasBeenSubmitted = useRef ( false ) ;
108+ const [ userLocation ] = useOnyx ( ONYXKEYS . USER_LOCATION ) ;
105109
106110 const [ errorTitle , setErrorTitle ] = useState < string | undefined > ( undefined ) ;
107111 const [ errorMessage , setErrorMessage ] = useState < string | undefined > ( undefined ) ;
@@ -294,52 +298,69 @@ function SubmitDetailsPage({
294298 const onSuccess = ( participant : Participant , file : File , locationPermissionGranted ?: boolean ) => {
295299 const receipt : Receipt = file ;
296300 receipt . state = file && CONST . IOU . RECEIPT_STATE . SCAN_READY ;
297- if ( locationPermissionGranted ) {
298- getCurrentPosition (
299- ( successData ) => {
300- finishRequestAndNavigate ( participant , receipt , {
301- lat : successData . coords . latitude ,
302- long : successData . coords . longitude ,
303- } ) ;
304- } ,
305- ( errorData ) => {
306- Log . info ( '[SubmitDetailsPage] getCurrentPosition failed' , false , errorData ) ;
307- finishRequestAndNavigate ( participant , receipt ) ;
308- } ,
309- ) ;
301+ if ( ! locationPermissionGranted ) {
302+ finishRequestAndNavigate ( participant , receipt ) ;
303+ return ;
304+ }
305+ // Use cached userLocation when available — avoids an extra getCurrentPosition round-trip.
306+ if ( userLocation ) {
307+ finishRequestAndNavigate ( participant , receipt , {
308+ lat : userLocation . latitude ,
309+ long : userLocation . longitude ,
310+ } ) ;
310311 return ;
311312 }
312- finishRequestAndNavigate ( participant , receipt ) ;
313+ getCurrentPosition (
314+ ( successData ) => {
315+ finishRequestAndNavigate ( participant , receipt , {
316+ lat : successData . coords . latitude ,
317+ long : successData . coords . longitude ,
318+ } ) ;
319+ } ,
320+ ( errorData ) => {
321+ Log . info ( '[SubmitDetailsPage] getCurrentPosition failed' , false , errorData ) ;
322+ finishRequestAndNavigate ( participant , receipt ) ;
323+ } ,
324+ ) ;
313325 } ;
314326
315327 // Extracted from onConfirm — re-entering onConfirm from the permission modal deadlocked when OS permission was pre-granted.
316328 const performUpload = ( participant : Participant , locationPermissionGranted : boolean ) => {
317- if ( ! currentAttachment ) {
329+ if ( formHasBeenSubmitted . current || ! currentAttachment ) {
330+ setIsConfirming ( false ) ;
318331 return ;
319332 }
333+ formHasBeenSubmitted . current = true ;
320334 readFileAsync (
321335 currentReceiptSource ,
322336 currentReceiptName ,
323337 ( file ) => onSuccess ( participant , file , locationPermissionGranted ) ,
324- ( ) => { } ,
338+ ( ) => {
339+ // Allow retry after a file-read failure.
340+ formHasBeenSubmitted . current = false ;
341+ setIsConfirming ( false ) ;
342+ } ,
325343 currentReceiptType ,
326344 ) ;
327345 } ;
328346
329347 const onConfirm = ( listOfParticipants ?: Participant [ ] , gpsRequired ?: boolean ) => {
348+ setIsConfirming ( true ) ;
330349 const shouldStartLocationPermissionFlow =
331350 gpsRequired &&
332351 ( ! lastLocationPermissionPrompt ||
333352 ( DateUtils . isValidDateString ( lastLocationPermissionPrompt ?? '' ) &&
334353 DateUtils . getDifferenceInDaysFromNow ( new Date ( lastLocationPermissionPrompt ?? '' ) ) > CONST . IOU . LOCATION_PERMISSION_PROMPT_THRESHOLD_DAYS ) ) ;
335354
336355 if ( shouldStartLocationPermissionFlow ) {
356+ setSelectedParticipantList ( listOfParticipants ?? selectedParticipants ) ;
337357 setStartLocationPermissionFlow ( true ) ;
338358 return ;
339359 }
340360
341361 const participant = listOfParticipants ?. at ( 0 ) ?? selectedParticipants . at ( 0 ) ;
342362 if ( ! participant ) {
363+ setIsConfirming ( false ) ;
343364 return ;
344365 }
345366 performUpload ( participant , false ) ;
@@ -362,24 +383,30 @@ function SubmitDetailsPage({
362383 />
363384 < LocationPermissionModal
364385 startPermissionFlow = { startLocationPermissionFlow }
365- resetPermissionFlow = { ( ) => setStartLocationPermissionFlow ( false ) }
386+ resetPermissionFlow = { ( ) => {
387+ setStartLocationPermissionFlow ( false ) ;
388+ setIsConfirming ( false ) ;
389+ } }
366390 onGrant = { ( ) => {
367391 setStartLocationPermissionFlow ( false ) ;
368- const participant = selectedParticipants . at ( 0 ) ;
392+ const participant = selectedParticipantList . at ( 0 ) ?? selectedParticipants . at ( 0 ) ;
369393 if ( ! participant ) {
394+ setIsConfirming ( false ) ;
370395 return ;
371396 }
372397 navigateAfterInteraction ( ( ) => performUpload ( participant , true ) ) ;
373398 } }
374399 onDeny = { ( ) => {
375400 updateLastLocationPermissionPrompt ( ) ;
376401 setStartLocationPermissionFlow ( false ) ;
377- const participant = selectedParticipants . at ( 0 ) ;
402+ const participant = selectedParticipantList . at ( 0 ) ?? selectedParticipants . at ( 0 ) ;
378403 if ( ! participant ) {
404+ setIsConfirming ( false ) ;
379405 return ;
380406 }
381407 navigateAfterInteraction ( ( ) => performUpload ( participant , false ) ) ;
382408 } }
409+ onInitialGetLocationCompleted = { ( ) => setIsConfirming ( false ) }
383410 />
384411 < View style = { [ styles . containerWithSpaceBetween , styles . pointerEventsBoxNone ] } >
385412 < MoneyRequestConfirmationList
@@ -390,6 +417,7 @@ function SubmitDetailsPage({
390417 onToggleReimbursable = { setReimbursable }
391418 isPolicyExpenseChat = { isPolicyExpenseChat }
392419 policyID = { policy ?. id }
420+ isConfirming = { isConfirming }
393421 onConfirm = { ( updatedParticipants ) => onConfirm ( updatedParticipants , true ) }
394422 receiptPath = { currentReceiptSource }
395423 receiptFilename = { currentReceiptName }
0 commit comments