@@ -28,9 +28,10 @@ export interface ActiveDownload {
2828 uri : string ;
2929 fileUri : string ;
3030 cacheFileUri : string ;
31- /** Resolves the pending Promise<string | null> inside the fetch() loop. */
31+ // settle and reject are the resolve/reject of the Promise returned by handleRemote.
32+ // They are stored here so that cancel() and resume() in the fetcher class can
33+ // unblock the fetch() loop from outside the download flow.
3234 settle : ( path : string | null ) => void ;
33- /** Rejects the pending Promise inside the fetch() loop. */
3435 reject : ( error : unknown ) => void ;
3536}
3637
@@ -61,7 +62,9 @@ export async function handleAsset(
6162 const uri = asset . uri ;
6263
6364 if ( uri . startsWith ( 'http' ) ) {
64- // Dev mode: asset served from Metro dev server
65+ // Dev mode: asset served from Metro dev server.
66+ // uri is the resolved HTTP URL; source is the original require() number the
67+ // user holds, so it must be used as the downloads map key for pause/cancel to work.
6568 return handleRemote ( uri , source , progressCallback , downloads ) ;
6669 }
6770
@@ -81,7 +84,11 @@ export async function handleAsset(
8184 return ResourceFetcherUtils . removeFilePrefix ( fileUriWithType ) ;
8285}
8386
84- export function handleRemote (
87+ // uri and source are separate parameters because for asset sources (dev mode),
88+ // source is the require() number the user holds (used as the downloads map key),
89+ // while uri is the resolved HTTP URL needed for the actual download.
90+ // For plain remote strings they are the same value.
91+ export async function handleRemote (
8592 uri : string ,
8693 source : ResourceSource ,
8794 progressCallback : ( progress : number ) => void ,
@@ -94,57 +101,58 @@ export function handleRemote(
94101 ) ;
95102 }
96103
97- let settle ! : ( path : string | null ) => void ;
98- let reject ! : ( error : unknown ) => void ;
104+ const filename = ResourceFetcherUtils . getFilenameFromUri ( uri ) ;
105+ const fileUri = `${ RNEDirectory } ${ filename } ` ;
106+ const cacheFileUri = `${ cacheDirectory } ${ filename } ` ;
99107
108+ if ( await ResourceFetcherUtils . checkFileExists ( fileUri ) ) {
109+ return ResourceFetcherUtils . removeFilePrefix ( fileUri ) ;
110+ }
111+
112+ await ResourceFetcherUtils . createDirectoryIfNoExists ( ) ;
113+
114+ // We need a Promise whose resolution can be triggered from outside this function —
115+ // by cancel() or resume() in the fetcher class. A plain async function can't do that,
116+ // so we create the Promise manually and store settle/reject in the downloads map.
117+ let settle : ( path : string | null ) => void = ( ) => { } ;
118+ let reject : ( error : unknown ) => void = ( ) => { } ;
100119 const promise = new Promise < string | null > ( ( res , rej ) => {
101120 settle = res ;
102121 reject = rej ;
103122 } ) ;
104123
105- void ( async ( ) => {
106- const filename = ResourceFetcherUtils . getFilenameFromUri ( uri ) ;
107- const fileUri = `${ RNEDirectory } ${ filename } ` ;
108- const cacheFileUri = `${ cacheDirectory } ${ filename } ` ;
109-
110- try {
111- if ( await ResourceFetcherUtils . checkFileExists ( fileUri ) ) {
112- settle ( ResourceFetcherUtils . removeFilePrefix ( fileUri ) ) ;
113- return ;
124+ const downloadResumable = createDownloadResumable (
125+ uri ,
126+ cacheFileUri ,
127+ { sessionType : FileSystemSessionType . BACKGROUND } ,
128+ ( {
129+ totalBytesWritten,
130+ totalBytesExpectedToWrite,
131+ } : {
132+ totalBytesWritten : number ;
133+ totalBytesExpectedToWrite : number ;
134+ } ) => {
135+ if ( totalBytesExpectedToWrite === - 1 ) {
136+ progressCallback ( 0 ) ;
137+ } else {
138+ progressCallback ( totalBytesWritten / totalBytesExpectedToWrite ) ;
114139 }
140+ }
141+ ) ;
142+
143+ downloads . set ( source , {
144+ downloadResumable,
145+ status : DownloadStatus . ONGOING ,
146+ uri,
147+ fileUri,
148+ cacheFileUri,
149+ settle,
150+ reject,
151+ } ) ;
115152
116- await ResourceFetcherUtils . createDirectoryIfNoExists ( ) ;
117-
118- const downloadResumable = createDownloadResumable (
119- uri ,
120- cacheFileUri ,
121- { sessionType : FileSystemSessionType . BACKGROUND } ,
122- ( {
123- totalBytesWritten,
124- totalBytesExpectedToWrite,
125- } : {
126- totalBytesWritten : number ;
127- totalBytesExpectedToWrite : number ;
128- } ) => {
129- if ( totalBytesExpectedToWrite === - 1 ) {
130- progressCallback ( 0 ) ;
131- } else {
132- progressCallback ( totalBytesWritten / totalBytesExpectedToWrite ) ;
133- }
134- }
135- ) ;
136-
137- downloads . set ( source , {
138- downloadResumable,
139- status : DownloadStatus . ONGOING ,
140- uri,
141- fileUri,
142- cacheFileUri,
143- settle,
144- reject,
145- } ) ;
146-
147- const result = await downloadResumable . downloadAsync ( ) ;
153+ downloadResumable
154+ . downloadAsync ( )
155+ . then ( async ( result ) => {
148156 const dl = downloads . get ( source ) ;
149157 // If paused or canceled during the download, settle/reject will be called
150158 // externally by resume() or cancel() — do nothing here.
@@ -165,15 +173,22 @@ export function handleRemote(
165173 return ;
166174 }
167175
168- await moveAsync ( { from : cacheFileUri , to : fileUri } ) ;
176+ try {
177+ await moveAsync ( { from : cacheFileUri , to : fileUri } ) ;
178+ } catch ( error ) {
179+ downloads . delete ( source ) ;
180+ reject ( error ) ;
181+ return ;
182+ }
183+
169184 downloads . delete ( source ) ;
170185 ResourceFetcherUtils . triggerHuggingFaceDownloadCounter ( uri ) ;
171186 settle ( ResourceFetcherUtils . removeFilePrefix ( fileUri ) ) ;
172- } catch ( error ) {
187+ } )
188+ . catch ( ( error ) => {
173189 downloads . delete ( source ) ;
174190 reject ( error ) ;
175- }
176- } ) ( ) ;
191+ } ) ;
177192
178193 return promise ;
179194}
0 commit comments