@@ -2,15 +2,12 @@ import {useIsFocused} from '@react-navigation/native';
22import { useEffect , useRef } from 'react' ;
33import type { OnyxEntry } from 'react-native-onyx' ;
44import useLocalize from '@hooks/useLocalize' ;
5- import clearOdometerDraftTransactionState from '@libs/actions/OdometerTransactionUtils' ;
6- import { navigateToStartMoneyRequestStep } from '@libs/IOUUtils' ;
75import Log from '@libs/Log' ;
86import { getOdometerImageName , getOdometerImageType , getOdometerImageUri } from '@libs/OdometerImageUtils' ;
97import stitchOdometerImages from '@libs/stitchOdometerImages' ;
108import { cancelSpan , endSpan , startSpan } from '@libs/telemetry/activeSpans' ;
11- import { checkIfLocalFileIsAccessible , setMoneyRequestReceipt } from '@userActions/IOU/Receipt' ;
9+ import { setMoneyRequestReceipt } from '@userActions/IOU/Receipt' ;
1210import CONST from '@src/CONST' ;
13- import type { IOUType } from '@src/CONST' ;
1411import type { Transaction } from '@src/types/onyx' ;
1512import type { FileObject } from '@src/types/utils/Attachment' ;
1613
@@ -19,9 +16,7 @@ type OdometerReceiptStitcherProps = {
1916 odometerStartImage : FileObject | string | null | undefined ;
2017 odometerEndImage : FileObject | string | null | undefined ;
2118 transaction : OnyxEntry < Transaction > ;
22- reportID : string ;
23- backToReport : string | undefined ;
24- iouType : IOUType ;
19+ hasVerifiedBlobs : boolean ;
2520 onStitchingChange : ( isStitching : boolean ) => void ;
2621 onStitchError : ( error : string ) => void ;
2722} ;
@@ -37,9 +32,7 @@ function OdometerReceiptStitcher({
3732 odometerStartImage,
3833 odometerEndImage,
3934 transaction,
40- reportID,
41- backToReport,
42- iouType,
35+ hasVerifiedBlobs,
4336 onStitchingChange,
4437 onStitchError,
4538} : OdometerReceiptStitcherProps ) {
@@ -51,7 +44,10 @@ function OdometerReceiptStitcher({
5144 } | null > ( null ) ;
5245
5346 useEffect ( ( ) => {
54- if ( ! isOdometerDistanceRequest || ! isFocused || ! transaction ) {
47+ // Wait until useRestartOnOdometerImagesFailure has confirmed the blob URLs are still
48+ // readable. Stitching a dead blob after a browser refresh would race with that hook's
49+ // redirect and leave the UI stuck on the E screen
50+ if ( ! isOdometerDistanceRequest || ! isFocused || ! transaction || ! hasVerifiedBlobs ) {
5551 return ;
5652 }
5753
@@ -83,76 +79,42 @@ function OdometerReceiptStitcher({
8379 onStitchingChange ( true ) ;
8480 onStitchError ( '' ) ;
8581
86- const runStitch = ( ) => {
87- startSpan ( CONST . TELEMETRY . SPAN_ODOMETER_IMAGE_STITCH , {
88- name : CONST . TELEMETRY . SPAN_ODOMETER_IMAGE_STITCH ,
89- op : CONST . TELEMETRY . SPAN_ODOMETER_IMAGE_STITCH ,
90- } ) ;
82+ startSpan ( CONST . TELEMETRY . SPAN_ODOMETER_IMAGE_STITCH , {
83+ name : CONST . TELEMETRY . SPAN_ODOMETER_IMAGE_STITCH ,
84+ op : CONST . TELEMETRY . SPAN_ODOMETER_IMAGE_STITCH ,
85+ } ) ;
9186
92- stitchOdometerImages ( odometerStartImage , odometerEndImage )
93- . then ( ( stitchedImage ) => {
94- if ( ignore || ! stitchedImage ) {
95- cancelSpan ( CONST . TELEMETRY . SPAN_ODOMETER_IMAGE_STITCH ) ;
96- return ;
97- }
98- setMoneyRequestReceipt ( transaction . transactionID , getOdometerImageUri ( stitchedImage ) , getOdometerImageName ( stitchedImage ) , true , getOdometerImageType ( stitchedImage ) ) ;
99- lastStitchedImages . current = { startImage : odometerStartImage , endImage : odometerEndImage } ;
100- endSpan ( CONST . TELEMETRY . SPAN_ODOMETER_IMAGE_STITCH ) ;
101- } )
102- . catch ( ( error : unknown ) => {
87+ stitchOdometerImages ( odometerStartImage , odometerEndImage )
88+ . then ( ( stitchedImage ) => {
89+ if ( ignore || ! stitchedImage ) {
10390 cancelSpan ( CONST . TELEMETRY . SPAN_ODOMETER_IMAGE_STITCH ) ;
104- if ( ignore ) {
105- return ;
106- }
107- Log . warn ( 'stitchOdometerImages failed' , { error} ) ;
108- onStitchError ( translate ( 'iou.error.stitchOdometerImagesFailed' ) ) ;
109- } )
110- . finally ( ( ) => {
111- if ( ignore ) {
112- return ;
113- }
114- onStitchingChange ( false ) ;
115- } ) ;
116- } ;
117-
118- // Pre-flight: verify blob URLs haven't expired before attempting to stitch.
119- const localImages = [
120- { uri : startUri , image : odometerStartImage } ,
121- { uri : endUri , image : odometerEndImage } ,
122- ] . filter ( ( item ) : item is { uri : string ; image : typeof odometerStartImage } => ! ! item . uri && item . uri . startsWith ( 'blob:' ) ) ;
123-
124- let hasExpiredImages = false ;
125- Promise . all (
126- localImages . map ( ( { uri, image} ) =>
127- checkIfLocalFileIsAccessible (
128- getOdometerImageName ( image ) ,
129- uri ,
130- typeof image === 'object' ? image ?. type : undefined ,
131- ( ) => { } ,
132- ( ) => {
133- hasExpiredImages = true ;
134- } ,
135- ) ,
136- ) ,
137- ) . then ( ( ) => {
138- if ( ignore ) {
139- return ;
140- }
141- if ( hasExpiredImages ) {
91+ return ;
92+ }
93+ setMoneyRequestReceipt ( transaction . transactionID , getOdometerImageUri ( stitchedImage ) , getOdometerImageName ( stitchedImage ) , true , getOdometerImageType ( stitchedImage ) ) ;
94+ lastStitchedImages . current = { startImage : odometerStartImage , endImage : odometerEndImage } ;
95+ endSpan ( CONST . TELEMETRY . SPAN_ODOMETER_IMAGE_STITCH ) ;
96+ } )
97+ . catch ( ( error : unknown ) => {
98+ cancelSpan ( CONST . TELEMETRY . SPAN_ODOMETER_IMAGE_STITCH ) ;
99+ if ( ignore ) {
100+ return ;
101+ }
102+ Log . warn ( 'stitchOdometerImages failed' , { error} ) ;
103+ onStitchError ( translate ( 'iou.error.stitchOdometerImagesFailed' ) ) ;
104+ } )
105+ . finally ( ( ) => {
106+ if ( ignore ) {
107+ return ;
108+ }
142109 onStitchingChange ( false ) ;
143- clearOdometerDraftTransactionState ( transaction ) ;
144- navigateToStartMoneyRequestStep ( CONST . IOU . REQUEST_TYPE . DISTANCE_ODOMETER , iouType , transaction . transactionID , reportID , CONST . IOU . ACTION . CREATE , backToReport ) ;
145- return ;
146- }
147- runStitch ( ) ;
148- } ) ;
110+ } ) ;
149111
150112 return ( ) => {
151113 ignore = true ;
152114 onStitchingChange ( false ) ;
153115 cancelSpan ( CONST . TELEMETRY . SPAN_ODOMETER_IMAGE_STITCH ) ;
154116 } ;
155- } , [ isOdometerDistanceRequest , isFocused , odometerStartImage , odometerEndImage , transaction , reportID , backToReport , translate , iouType , onStitchingChange , onStitchError ] ) ;
117+ } , [ isOdometerDistanceRequest , isFocused , odometerStartImage , odometerEndImage , transaction , hasVerifiedBlobs , translate , onStitchingChange , onStitchError ] ) ;
156118
157119 return null ;
158120}
0 commit comments