@kesha-antonov/react-native-background-downloader 4.0.0-alpha.1 → 4.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -1
- package/README.md +2 -4
- package/ios/RNBackgroundDownloader.mm +31 -14
- package/package.json +5 -5
- package/src/DownloadTask.ts +1 -1
- package/src/config.ts +23 -0
- package/src/index.ts +141 -117
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -5,11 +5,9 @@
|
|
|
5
5
|
|
|
6
6
|
[](https://badge.fury.io/js/@kesha-antonov%2Freact-native-background-downloader)
|
|
7
7
|
|
|
8
|
-
## 🎉 Version 4.0.0
|
|
8
|
+
## 🎉 Version 4.0.0 Released!
|
|
9
9
|
|
|
10
|
-
**v4.0.0
|
|
11
|
-
|
|
12
|
-
> ⚠️ **Alpha Release:** This version is in alpha state. Please report any issues you encounter.
|
|
10
|
+
**v4.0.0** is now available with full **React Native New Architecture (TurboModules)** support!
|
|
13
11
|
|
|
14
12
|
### What's New
|
|
15
13
|
- ✅ Full TurboModules support for iOS and Android
|
|
@@ -58,16 +58,16 @@ RCT_EXPORT_MODULE();
|
|
|
58
58
|
// Enable interop layer so NativeModules.RNBackgroundDownloader is available
|
|
59
59
|
// This is required for NativeEventEmitter to work with TurboModules
|
|
60
60
|
+ (BOOL)requiresMainQueueSetup {
|
|
61
|
-
return
|
|
61
|
+
return NO;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
#pragma mark - Helper methods
|
|
65
65
|
|
|
66
66
|
- (BOOL)canSendEvents {
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
return
|
|
67
|
+
// Only send events if JavaScript has fully loaded
|
|
68
|
+
// This prevents "Invariant Violation: Failed to call into JavaScript module method"
|
|
69
|
+
// errors that occur when native code tries to send events before the JS bridge is ready
|
|
70
|
+
return isJavascriptLoaded;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
- (RNBGDTaskConfig *)configForTask:(NSURLSessionTask *)task {
|
|
@@ -126,6 +126,11 @@ RCT_EXPORT_MODULE();
|
|
|
126
126
|
self = [super initWithDisabledObservation];
|
|
127
127
|
#endif
|
|
128
128
|
if (self) {
|
|
129
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
130
|
+
// TurboModules are lazily initialized when JS first accesses them,
|
|
131
|
+
// so JavaScript is ready when init is called
|
|
132
|
+
isJavascriptLoaded = YES;
|
|
133
|
+
#endif
|
|
129
134
|
[MMKV initializeMMKV:nil];
|
|
130
135
|
mmkv = [MMKV mmkvWithID:@"RNBackgroundDownloader"];
|
|
131
136
|
|
|
@@ -368,7 +373,7 @@ RCT_EXPORT_METHOD(download: (NSDictionary *) options) {
|
|
|
368
373
|
}
|
|
369
374
|
}
|
|
370
375
|
|
|
371
|
-
- (void)
|
|
376
|
+
- (void)pauseTaskInternal:(NSString *)identifier {
|
|
372
377
|
DLog(identifier, @"[RNBackgroundDownloader] - [pauseTask]");
|
|
373
378
|
@synchronized (sharedLock) {
|
|
374
379
|
NSURLSessionDownloadTask *task = self->idToTaskMap[identifier];
|
|
@@ -392,13 +397,17 @@ RCT_EXPORT_METHOD(download: (NSDictionary *) options) {
|
|
|
392
397
|
}
|
|
393
398
|
}
|
|
394
399
|
|
|
395
|
-
#
|
|
400
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
401
|
+
- (void)pauseTask:(NSString *)identifier {
|
|
402
|
+
[self pauseTaskInternal:identifier];
|
|
403
|
+
}
|
|
404
|
+
#else
|
|
396
405
|
RCT_EXPORT_METHOD(pauseTask: (NSString *)id) {
|
|
397
|
-
[self
|
|
406
|
+
[self pauseTaskInternal:id];
|
|
398
407
|
}
|
|
399
408
|
#endif
|
|
400
409
|
|
|
401
|
-
- (void)
|
|
410
|
+
- (void)resumeTaskInternal:(NSString *)identifier {
|
|
402
411
|
DLog(identifier, @"[RNBackgroundDownloader] - [resumeTask]");
|
|
403
412
|
@synchronized (sharedLock) {
|
|
404
413
|
[self lazyRegisterSession];
|
|
@@ -449,13 +458,17 @@ RCT_EXPORT_METHOD(pauseTask: (NSString *)id) {
|
|
|
449
458
|
}
|
|
450
459
|
}
|
|
451
460
|
|
|
452
|
-
#
|
|
461
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
462
|
+
- (void)resumeTask:(NSString *)identifier {
|
|
463
|
+
[self resumeTaskInternal:identifier];
|
|
464
|
+
}
|
|
465
|
+
#else
|
|
453
466
|
RCT_EXPORT_METHOD(resumeTask: (NSString *)id) {
|
|
454
|
-
[self
|
|
467
|
+
[self resumeTaskInternal:id];
|
|
455
468
|
}
|
|
456
469
|
#endif
|
|
457
470
|
|
|
458
|
-
- (void)
|
|
471
|
+
- (void)stopTaskInternal:(NSString *)identifier {
|
|
459
472
|
DLog(identifier, @"[RNBackgroundDownloader] - [stopTask]");
|
|
460
473
|
@synchronized (sharedLock) {
|
|
461
474
|
NSURLSessionDownloadTask *task = self->idToTaskMap[identifier];
|
|
@@ -468,9 +481,13 @@ RCT_EXPORT_METHOD(resumeTask: (NSString *)id) {
|
|
|
468
481
|
}
|
|
469
482
|
}
|
|
470
483
|
|
|
471
|
-
#
|
|
484
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
485
|
+
- (void)stopTask:(NSString *)identifier {
|
|
486
|
+
[self stopTaskInternal:identifier];
|
|
487
|
+
}
|
|
488
|
+
#else
|
|
472
489
|
RCT_EXPORT_METHOD(stopTask: (NSString *)id) {
|
|
473
|
-
[self
|
|
490
|
+
[self stopTaskInternal:id];
|
|
474
491
|
}
|
|
475
492
|
#endif
|
|
476
493
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kesha-antonov/react-native-background-downloader",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.2",
|
|
4
4
|
"description": "A library for React-Native to help you download large files on iOS and Android both in the foreground and most importantly in the background.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react-native",
|
|
@@ -83,9 +83,9 @@
|
|
|
83
83
|
"@babel/preset-typescript": "^7.28.5",
|
|
84
84
|
"@babel/runtime": "^7.28.4",
|
|
85
85
|
"@expo/config-plugins": "^54.0.2",
|
|
86
|
-
"@react-native/babel-preset": "^0.81.
|
|
87
|
-
"@react-native/eslint-config": "^0.81.
|
|
88
|
-
"@react-native/metro-config": "^0.81.
|
|
86
|
+
"@react-native/babel-preset": "^0.81.5",
|
|
87
|
+
"@react-native/eslint-config": "^0.81.5",
|
|
88
|
+
"@react-native/metro-config": "^0.81.5",
|
|
89
89
|
"@typescript-eslint/eslint-plugin": "^8.46.1",
|
|
90
90
|
"@typescript-eslint/parser": "^8.46.1",
|
|
91
91
|
"babel-jest": "^29.7.0",
|
|
@@ -105,7 +105,7 @@
|
|
|
105
105
|
"lint-staged": ">=16",
|
|
106
106
|
"metro-react-native-babel-preset": "^0.77.0",
|
|
107
107
|
"react": "19.1.0",
|
|
108
|
-
"react-native": "0.81.
|
|
108
|
+
"react-native": "0.81.5",
|
|
109
109
|
"react-native-fs": "^2.20.0",
|
|
110
110
|
"react-native-vector-icons": "^10.3.0",
|
|
111
111
|
"react-test-renderer": "19.2.0",
|
package/src/DownloadTask.ts
CHANGED
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
DownloadTaskState,
|
|
16
16
|
Metadata,
|
|
17
17
|
} from './types'
|
|
18
|
-
import { config, log } from '
|
|
18
|
+
import { config, log } from './config'
|
|
19
19
|
import type { Spec } from './NativeRNBackgroundDownloader'
|
|
20
20
|
|
|
21
21
|
// Try to get the native module using TurboModuleRegistry first (new architecture),
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Headers } from './types'
|
|
2
|
+
|
|
3
|
+
export const DEFAULT_PROGRESS_INTERVAL = 1000
|
|
4
|
+
export const DEFAULT_PROGRESS_MIN_BYTES = 1024 * 1024 // 1MB
|
|
5
|
+
|
|
6
|
+
interface ConfigState {
|
|
7
|
+
headers: Headers
|
|
8
|
+
progressInterval: number
|
|
9
|
+
progressMinBytes: number
|
|
10
|
+
isLogsEnabled: boolean
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const config: ConfigState = {
|
|
14
|
+
headers: {},
|
|
15
|
+
progressInterval: DEFAULT_PROGRESS_INTERVAL,
|
|
16
|
+
progressMinBytes: DEFAULT_PROGRESS_MIN_BYTES,
|
|
17
|
+
isLogsEnabled: false,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const log = (...args: unknown[]): void => {
|
|
21
|
+
if (config.isLogsEnabled)
|
|
22
|
+
console.log('[RNBackgroundDownloader]', ...args)
|
|
23
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { NativeModules, Platform, TurboModuleRegistry,
|
|
1
|
+
import { NativeModules, Platform, TurboModuleRegistry, NativeEventEmitter, NativeModule } from 'react-native'
|
|
2
2
|
import DownloadTask from './DownloadTask'
|
|
3
3
|
import { Config, DownloadParams, Headers, TaskInfo, TaskInfoNative } from './types'
|
|
4
|
+
import { config, log, DEFAULT_PROGRESS_INTERVAL, DEFAULT_PROGRESS_MIN_BYTES } from './config'
|
|
4
5
|
import type { Spec } from './NativeRNBackgroundDownloader'
|
|
5
6
|
|
|
6
|
-
type
|
|
7
|
+
type RNBackgroundDownloaderModule = Spec & {
|
|
7
8
|
TaskRunning: number
|
|
8
9
|
TaskSuspended: number
|
|
9
10
|
TaskCanceling: number
|
|
@@ -11,61 +12,61 @@ type NativeModule = Spec & {
|
|
|
11
12
|
documents: string
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
//
|
|
15
|
-
|
|
16
|
-
let
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
15
|
+
// Lazy initialization state
|
|
16
|
+
let RNBackgroundDownloader: RNBackgroundDownloaderModule & NativeModule
|
|
17
|
+
let turboModule: Spec | null = null
|
|
18
|
+
let isNewArchitecture = false
|
|
19
|
+
let isInitialized = false
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Lazily initialize the native module.
|
|
23
|
+
* This is called on first actual use of the module, not at import time.
|
|
24
|
+
* This prevents issues with module loading before React Native's bridge is ready.
|
|
25
|
+
*/
|
|
26
|
+
function ensureNativeModuleInitialized (): RNBackgroundDownloaderModule & NativeModule {
|
|
27
|
+
if (isInitialized && RNBackgroundDownloader)
|
|
28
|
+
return RNBackgroundDownloader
|
|
29
|
+
|
|
30
|
+
// Try TurboModules first
|
|
31
|
+
turboModule = TurboModuleRegistry.get<Spec>('RNBackgroundDownloader')
|
|
32
|
+
// Check if the new architecture event emitters are available
|
|
33
|
+
// TurboModuleRegistry.get() can return a module even with old arch, but event emitters won't exist
|
|
34
|
+
isNewArchitecture = turboModule != null && typeof turboModule.onDownloadBegin === 'function'
|
|
35
|
+
|
|
36
|
+
if (isNewArchitecture && turboModule) {
|
|
37
|
+
// New architecture: TurboModules use getConstants() method
|
|
38
|
+
const constants = turboModule.getConstants()
|
|
39
|
+
RNBackgroundDownloader = Object.assign(turboModule, constants) as RNBackgroundDownloaderModule & NativeModule
|
|
40
|
+
} else {
|
|
41
|
+
// Fall back to old architecture - must use NativeModules for proper event emission
|
|
42
|
+
RNBackgroundDownloader = NativeModules.RNBackgroundDownloader
|
|
43
|
+
|
|
44
|
+
// For old architecture, constants may need to be fetched via getConstants() as well
|
|
45
|
+
if (RNBackgroundDownloader && !RNBackgroundDownloader.documents && typeof RNBackgroundDownloader.getConstants === 'function') {
|
|
46
|
+
const constants = RNBackgroundDownloader.getConstants()
|
|
47
|
+
if (constants)
|
|
48
|
+
Object.assign(RNBackgroundDownloader, constants)
|
|
49
|
+
}
|
|
35
50
|
}
|
|
36
|
-
}
|
|
37
51
|
|
|
38
|
-
if (!RNBackgroundDownloader)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
52
|
+
if (!RNBackgroundDownloader)
|
|
53
|
+
throw new Error(
|
|
54
|
+
'The package \'@kesha-antonov/react-native-background-downloader\' doesn\'t seem to be linked. Make sure: \n\n' +
|
|
55
|
+
Platform.select({ ios: '- You have run \'pod install\'\n', default: '' }) +
|
|
56
|
+
'- You rebuilt the app after installing the package\n' +
|
|
57
|
+
'- You are not using Expo Go\n'
|
|
58
|
+
)
|
|
45
59
|
|
|
46
|
-
|
|
47
|
-
const DEFAULT_PROGRESS_INTERVAL = 1000
|
|
48
|
-
const DEFAULT_PROGRESS_MIN_BYTES = 1024 * 1024 // 1MB
|
|
49
|
-
const tasksMap = new Map<string, DownloadTask>()
|
|
60
|
+
isInitialized = true
|
|
50
61
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
progressInterval: number
|
|
54
|
-
progressMinBytes: number
|
|
55
|
-
isLogsEnabled: boolean
|
|
56
|
-
}
|
|
62
|
+
// Initialize event listeners after native module is ready
|
|
63
|
+
initializeEventListeners()
|
|
57
64
|
|
|
58
|
-
|
|
59
|
-
headers: {},
|
|
60
|
-
progressInterval: DEFAULT_PROGRESS_INTERVAL,
|
|
61
|
-
progressMinBytes: DEFAULT_PROGRESS_MIN_BYTES,
|
|
62
|
-
isLogsEnabled: false,
|
|
65
|
+
return RNBackgroundDownloader
|
|
63
66
|
}
|
|
64
67
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
console.log('[RNBackgroundDownloader]', ...args)
|
|
68
|
-
}
|
|
68
|
+
const MIN_PROGRESS_INTERVAL = 250
|
|
69
|
+
const tasksMap = new Map<string, DownloadTask>()
|
|
69
70
|
|
|
70
71
|
interface DownloadBeginEvent {
|
|
71
72
|
id: string
|
|
@@ -92,74 +93,89 @@ interface DownloadFailedEvent {
|
|
|
92
93
|
}
|
|
93
94
|
|
|
94
95
|
// Set up event listeners based on architecture
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
96
|
+
// For old architecture, we need to defer NativeEventEmitter creation
|
|
97
|
+
// to avoid issues during module initialization
|
|
98
|
+
let eventListenersInitialized = false
|
|
99
|
+
|
|
100
|
+
function initializeEventListeners () {
|
|
101
|
+
if (eventListenersInitialized) return
|
|
102
|
+
eventListenersInitialized = true
|
|
103
|
+
|
|
104
|
+
if (isNewArchitecture && turboModule) {
|
|
105
|
+
// New architecture: use EventEmitter from TurboModule spec
|
|
106
|
+
turboModule.onDownloadBegin((data: DownloadBeginEvent) => {
|
|
107
|
+
const { id, ...rest } = data
|
|
108
|
+
log('downloadBegin', id, rest)
|
|
108
109
|
const task = tasksMap.get(id)
|
|
109
|
-
task?.
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
110
|
+
task?.onBegin(rest)
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
turboModule.onDownloadProgress((events: DownloadProgressEvent[]) => {
|
|
114
|
+
log('downloadProgress', events)
|
|
115
|
+
for (const event of events) {
|
|
116
|
+
const { id, ...rest } = event
|
|
117
|
+
const task = tasksMap.get(id)
|
|
118
|
+
task?.onProgress(rest)
|
|
119
|
+
}
|
|
120
|
+
})
|
|
120
121
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
} else {
|
|
129
|
-
// Old architecture: use DeviceEventEmitter
|
|
130
|
-
DeviceEventEmitter.addListener('downloadBegin', (data: DownloadBeginEvent) => {
|
|
131
|
-
const { id, ...rest } = data
|
|
132
|
-
log('downloadBegin', id, rest)
|
|
133
|
-
const task = tasksMap.get(id)
|
|
134
|
-
task?.onBegin(rest)
|
|
135
|
-
})
|
|
122
|
+
turboModule.onDownloadComplete((data: DownloadCompleteEvent) => {
|
|
123
|
+
const { id, ...rest } = data
|
|
124
|
+
log('downloadComplete', id, rest)
|
|
125
|
+
const task = tasksMap.get(id)
|
|
126
|
+
task?.onDone(rest)
|
|
127
|
+
tasksMap.delete(id)
|
|
128
|
+
})
|
|
136
129
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const { id, ...rest } = event
|
|
130
|
+
turboModule.onDownloadFailed((data: DownloadFailedEvent) => {
|
|
131
|
+
const { id, ...rest } = data
|
|
132
|
+
log('downloadFailed', id, rest)
|
|
141
133
|
const task = tasksMap.get(id)
|
|
142
|
-
task?.
|
|
143
|
-
|
|
144
|
-
|
|
134
|
+
task?.onError(rest)
|
|
135
|
+
tasksMap.delete(id)
|
|
136
|
+
})
|
|
137
|
+
} else {
|
|
138
|
+
// Old architecture: use NativeEventEmitter with the native module
|
|
139
|
+
// RCTEventEmitter on native side requires NativeEventEmitter on JS side
|
|
140
|
+
const eventEmitter = new NativeEventEmitter(RNBackgroundDownloader)
|
|
141
|
+
|
|
142
|
+
eventEmitter.addListener('downloadBegin', (data: DownloadBeginEvent) => {
|
|
143
|
+
const { id, ...rest } = data
|
|
144
|
+
log('downloadBegin', id, rest)
|
|
145
|
+
const task = tasksMap.get(id)
|
|
146
|
+
task?.onBegin(rest)
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
eventEmitter.addListener('downloadProgress', (events: DownloadProgressEvent[]) => {
|
|
150
|
+
log('downloadProgress', events)
|
|
151
|
+
for (const event of events) {
|
|
152
|
+
const { id, ...rest } = event
|
|
153
|
+
const task = tasksMap.get(id)
|
|
154
|
+
task?.onProgress(rest)
|
|
155
|
+
}
|
|
156
|
+
})
|
|
145
157
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
158
|
+
eventEmitter.addListener('downloadComplete', (data: DownloadCompleteEvent) => {
|
|
159
|
+
const { id, ...rest } = data
|
|
160
|
+
log('downloadComplete', id, rest)
|
|
161
|
+
const task = tasksMap.get(id)
|
|
162
|
+
task?.onDone(rest)
|
|
163
|
+
tasksMap.delete(id)
|
|
164
|
+
})
|
|
153
165
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
166
|
+
eventEmitter.addListener('downloadFailed', (data: DownloadFailedEvent) => {
|
|
167
|
+
const { id, ...rest } = data
|
|
168
|
+
log('downloadFailed', id, rest)
|
|
169
|
+
const task = tasksMap.get(id)
|
|
170
|
+
task?.onError(rest)
|
|
171
|
+
tasksMap.delete(id)
|
|
172
|
+
})
|
|
173
|
+
}
|
|
161
174
|
}
|
|
162
175
|
|
|
176
|
+
// Event listeners are now initialized lazily when ensureNativeModuleInitialized() is called
|
|
177
|
+
// This ensures the bridge is ready before any native module access
|
|
178
|
+
|
|
163
179
|
export function setConfig ({
|
|
164
180
|
headers = {},
|
|
165
181
|
progressInterval = DEFAULT_PROGRESS_INTERVAL,
|
|
@@ -182,7 +198,8 @@ export function setConfig ({
|
|
|
182
198
|
}
|
|
183
199
|
|
|
184
200
|
export const getExistingDownloadTasks = async (): Promise<DownloadTask[]> => {
|
|
185
|
-
const
|
|
201
|
+
const nativeModule = ensureNativeModuleInitialized()
|
|
202
|
+
const downloads = await nativeModule.getExistingDownloadTasks()
|
|
186
203
|
const downloadTasks: DownloadTask[] = downloads.map(downloadInfo => {
|
|
187
204
|
// Parse metadata from JSON string to object
|
|
188
205
|
let metadata = {}
|
|
@@ -202,15 +219,15 @@ export const getExistingDownloadTasks = async (): Promise<DownloadTask[]> => {
|
|
|
202
219
|
const task = new DownloadTask(taskInfo, tasksMap.get(taskInfo.id))
|
|
203
220
|
|
|
204
221
|
switch (taskInfo.state) {
|
|
205
|
-
case
|
|
222
|
+
case nativeModule.TaskRunning: {
|
|
206
223
|
task.state = 'DOWNLOADING'
|
|
207
224
|
break
|
|
208
225
|
}
|
|
209
|
-
case
|
|
226
|
+
case nativeModule.TaskSuspended: {
|
|
210
227
|
task.state = 'PAUSED'
|
|
211
228
|
break
|
|
212
229
|
}
|
|
213
|
-
case
|
|
230
|
+
case nativeModule.TaskCanceling: {
|
|
214
231
|
// On iOS, paused tasks (via cancelByProducingResumeData) are in Canceling state with errorCode -999
|
|
215
232
|
if (taskInfo.errorCode === -999) {
|
|
216
233
|
task.state = 'PAUSED'
|
|
@@ -220,7 +237,7 @@ export const getExistingDownloadTasks = async (): Promise<DownloadTask[]> => {
|
|
|
220
237
|
}
|
|
221
238
|
break
|
|
222
239
|
}
|
|
223
|
-
case
|
|
240
|
+
case nativeModule.TaskCompleted: {
|
|
224
241
|
if (taskInfo.bytesDownloaded === taskInfo.bytesTotal)
|
|
225
242
|
task.state = 'DONE'
|
|
226
243
|
else
|
|
@@ -254,7 +271,8 @@ export const completeHandler = (jobId: string) => {
|
|
|
254
271
|
return
|
|
255
272
|
}
|
|
256
273
|
|
|
257
|
-
|
|
274
|
+
const nativeModule = ensureNativeModuleInitialized()
|
|
275
|
+
return nativeModule.completeHandler(jobId)
|
|
258
276
|
}
|
|
259
277
|
|
|
260
278
|
export function createDownloadTask ({
|
|
@@ -263,6 +281,9 @@ export function createDownloadTask ({
|
|
|
263
281
|
isNotificationVisible = false,
|
|
264
282
|
...rest
|
|
265
283
|
}: TaskInfo & DownloadParams) {
|
|
284
|
+
// Ensure native module and event listeners are initialized before creating tasks
|
|
285
|
+
ensureNativeModuleInitialized()
|
|
286
|
+
|
|
266
287
|
if (!rest.id || !rest.url || !rest.destination)
|
|
267
288
|
throw new Error('[RNBackgroundDownloader] id, url and destination are required')
|
|
268
289
|
|
|
@@ -287,8 +308,11 @@ export function createDownloadTask ({
|
|
|
287
308
|
return task
|
|
288
309
|
}
|
|
289
310
|
|
|
311
|
+
// Use getter to lazily initialize native module when directories are accessed
|
|
290
312
|
export const directories = {
|
|
291
|
-
documents
|
|
313
|
+
get documents () {
|
|
314
|
+
return ensureNativeModuleInitialized().documents
|
|
315
|
+
},
|
|
292
316
|
}
|
|
293
317
|
|
|
294
318
|
export default {
|