@kesha-antonov/react-native-background-downloader 4.5.3 → 4.5.5
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/README.md +74 -8
- package/android/build.gradle +4 -2
- package/android/src/main/java/com/eko/DownloadConstants.kt +3 -0
- package/android/src/main/java/com/eko/Downloader.kt +102 -0
- package/android/src/main/java/com/eko/RNBackgroundDownloaderModuleImpl.kt +86 -5
- package/android/src/main/java/com/eko/ResumableDownloader.kt +7 -0
- package/android/src/main/java/com/eko/UIDTDownloadJobService.kt +42 -4
- package/android/src/main/java/com/eko/uidt/UIDTJobManager.kt +11 -1
- package/android/src/main/java/com/eko/uidt/UIDTJobState.kt +62 -0
- package/android/src/main/java/com/eko/utils/StorageManager.kt +95 -0
- package/ios/RNBGDTaskConfig.h +3 -0
- package/ios/RNBGDTaskConfig.mm +3 -0
- package/ios/RNBackgroundDownloader.mm +375 -48
- package/lib/DownloadTask.d.ts +39 -0
- package/lib/DownloadTask.js +176 -0
- package/lib/NativeRNBackgroundDownloader.d.ts +127 -0
- package/lib/NativeRNBackgroundDownloader.js +4 -0
- package/lib/UploadTask.d.ts +30 -0
- package/lib/UploadTask.js +174 -0
- package/lib/config.d.ts +33 -0
- package/lib/config.js +79 -0
- package/lib/index.d.ts +26 -0
- package/lib/index.js +498 -0
- package/lib/types.d.ts +249 -0
- package/lib/types.js +2 -0
- package/package.json +7 -4
- package/plugin/build/index.d.ts +2 -2
- package/plugin/build/index.js +1 -1
- package/react-native-background-downloader.podspec +8 -2
- package/src/DownloadTask.ts +2 -0
- package/src/NativeRNBackgroundDownloader.ts +2 -0
- package/src/config.ts +6 -1
- package/src/index.ts +4 -0
- package/src/types.ts +27 -0
package/lib/index.js
ADDED
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.directories = exports.getExistingUploadTasks = exports.completeHandler = exports.getExistingDownloadTasks = void 0;
|
|
4
|
+
exports.cleanup = cleanup;
|
|
5
|
+
exports.setConfig = setConfig;
|
|
6
|
+
exports.createDownloadTask = createDownloadTask;
|
|
7
|
+
exports.createUploadTask = createUploadTask;
|
|
8
|
+
exports.getNativeModule = getNativeModule;
|
|
9
|
+
const react_native_1 = require("react-native");
|
|
10
|
+
const DownloadTask_1 = require("./DownloadTask");
|
|
11
|
+
const UploadTask_1 = require("./UploadTask");
|
|
12
|
+
const config_1 = require("./config");
|
|
13
|
+
// Lazy initialization state
|
|
14
|
+
let RNBackgroundDownloader = null;
|
|
15
|
+
let turboModule = null;
|
|
16
|
+
let isIOSNewArchitecture = false;
|
|
17
|
+
let isInitialized = false;
|
|
18
|
+
/**
|
|
19
|
+
* Lazily initialize the native module.
|
|
20
|
+
* This is called on first actual use of the module, not at import time.
|
|
21
|
+
* This prevents issues with module loading before React Native's bridge is ready.
|
|
22
|
+
*/
|
|
23
|
+
function ensureNativeModuleInitialized() {
|
|
24
|
+
if (isInitialized && RNBackgroundDownloader != null)
|
|
25
|
+
return RNBackgroundDownloader;
|
|
26
|
+
// Try TurboModules first
|
|
27
|
+
turboModule = react_native_1.TurboModuleRegistry.get('RNBackgroundDownloader');
|
|
28
|
+
// Check if iOS new architecture event emitters are available
|
|
29
|
+
// On Android, we always use NativeEventEmitter because Android uses RCTDeviceEventEmitter
|
|
30
|
+
isIOSNewArchitecture = react_native_1.Platform.OS === 'ios' && turboModule != null && typeof turboModule.onDownloadBegin === 'function';
|
|
31
|
+
if (isIOSNewArchitecture && turboModule) {
|
|
32
|
+
// New architecture: TurboModules use getConstants() method
|
|
33
|
+
const constants = turboModule.getConstants();
|
|
34
|
+
RNBackgroundDownloader = Object.assign(turboModule, constants);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
// Fall back to old architecture - must use NativeModules for proper event emission
|
|
38
|
+
RNBackgroundDownloader = react_native_1.NativeModules.RNBackgroundDownloader;
|
|
39
|
+
// For old architecture, constants may need to be fetched via getConstants() as well
|
|
40
|
+
if (RNBackgroundDownloader && !RNBackgroundDownloader.documents && typeof RNBackgroundDownloader.getConstants === 'function') {
|
|
41
|
+
const constants = RNBackgroundDownloader.getConstants();
|
|
42
|
+
if (constants)
|
|
43
|
+
Object.assign(RNBackgroundDownloader, constants);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (!RNBackgroundDownloader)
|
|
47
|
+
throw new Error('The package \'@kesha-antonov/react-native-background-downloader\' doesn\'t seem to be linked. Make sure: \n\n' +
|
|
48
|
+
react_native_1.Platform.select({ ios: '- You have run \'pod install\'\n', default: '' }) +
|
|
49
|
+
'- You rebuilt the app after installing the package\n' +
|
|
50
|
+
'- You are not using Expo Go\n');
|
|
51
|
+
isInitialized = true;
|
|
52
|
+
// Initialize event listeners after native module is ready
|
|
53
|
+
initializeEventListeners();
|
|
54
|
+
return RNBackgroundDownloader;
|
|
55
|
+
}
|
|
56
|
+
const MIN_PROGRESS_INTERVAL = 250;
|
|
57
|
+
const tasksMap = new Map();
|
|
58
|
+
const uploadTasksMap = new Map();
|
|
59
|
+
// Set up event listeners based on architecture
|
|
60
|
+
// For old architecture, we need to defer NativeEventEmitter creation
|
|
61
|
+
// to avoid issues during module initialization
|
|
62
|
+
let eventListenersInitialized = false;
|
|
63
|
+
let eventSubscriptions = [];
|
|
64
|
+
/**
|
|
65
|
+
* Clean up event listeners. Call this before hot reload or module invalidation.
|
|
66
|
+
* This prevents memory leaks from accumulated event listeners.
|
|
67
|
+
*/
|
|
68
|
+
function cleanup() {
|
|
69
|
+
for (const subscription of eventSubscriptions)
|
|
70
|
+
subscription.remove();
|
|
71
|
+
eventSubscriptions = [];
|
|
72
|
+
eventListenersInitialized = false;
|
|
73
|
+
isInitialized = false;
|
|
74
|
+
// Clear module references to allow proper re-initialization
|
|
75
|
+
RNBackgroundDownloader = null;
|
|
76
|
+
turboModule = null;
|
|
77
|
+
isIOSNewArchitecture = false;
|
|
78
|
+
tasksMap.clear();
|
|
79
|
+
uploadTasksMap.clear();
|
|
80
|
+
}
|
|
81
|
+
function initializeEventListeners() {
|
|
82
|
+
var _a, _b, _c, _d;
|
|
83
|
+
if (eventListenersInitialized)
|
|
84
|
+
return;
|
|
85
|
+
eventListenersInitialized = true;
|
|
86
|
+
if (isIOSNewArchitecture && turboModule) {
|
|
87
|
+
// iOS new architecture: use EventEmitter from TurboModule spec
|
|
88
|
+
turboModule.onDownloadBegin((data) => {
|
|
89
|
+
const { id, ...rest } = data;
|
|
90
|
+
(0, config_1.log)('downloadBegin', id, rest);
|
|
91
|
+
const task = tasksMap.get(id);
|
|
92
|
+
if (!task) {
|
|
93
|
+
(0, config_1.log)('downloadBegin: task not found in tasksMap', id);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
task.onBegin(rest);
|
|
97
|
+
});
|
|
98
|
+
turboModule.onDownloadProgress((events) => {
|
|
99
|
+
(0, config_1.log)('downloadProgress', events);
|
|
100
|
+
for (const event of events) {
|
|
101
|
+
const { id, ...rest } = event;
|
|
102
|
+
const task = tasksMap.get(id);
|
|
103
|
+
if (task)
|
|
104
|
+
task.onProgress(rest);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
turboModule.onDownloadComplete((data) => {
|
|
108
|
+
const { id, ...rest } = data;
|
|
109
|
+
(0, config_1.log)('downloadComplete', id, rest);
|
|
110
|
+
const task = tasksMap.get(id);
|
|
111
|
+
if (!task)
|
|
112
|
+
(0, config_1.log)('downloadComplete: task not found in tasksMap', id);
|
|
113
|
+
else
|
|
114
|
+
task.onDone(rest);
|
|
115
|
+
tasksMap.delete(id);
|
|
116
|
+
});
|
|
117
|
+
turboModule.onDownloadFailed((data) => {
|
|
118
|
+
const { id, ...rest } = data;
|
|
119
|
+
(0, config_1.log)('downloadFailed', id, rest);
|
|
120
|
+
const task = tasksMap.get(id);
|
|
121
|
+
if (!task)
|
|
122
|
+
(0, config_1.log)('downloadFailed: task not found in tasksMap', id);
|
|
123
|
+
else
|
|
124
|
+
task.onError(rest);
|
|
125
|
+
tasksMap.delete(id);
|
|
126
|
+
});
|
|
127
|
+
// Upload events for new architecture (optional - may not exist in all versions)
|
|
128
|
+
if (typeof turboModule.onUploadBegin === 'function') {
|
|
129
|
+
(_a = turboModule.onUploadBegin) === null || _a === void 0 ? void 0 : _a.call(turboModule, (data) => {
|
|
130
|
+
const { id, ...rest } = data;
|
|
131
|
+
(0, config_1.log)('uploadBegin', id, rest);
|
|
132
|
+
const task = uploadTasksMap.get(id);
|
|
133
|
+
if (!task) {
|
|
134
|
+
(0, config_1.log)('uploadBegin: task not found in uploadTasksMap', id);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
task.onBegin(rest);
|
|
138
|
+
});
|
|
139
|
+
(_b = turboModule.onUploadProgress) === null || _b === void 0 ? void 0 : _b.call(turboModule, (events) => {
|
|
140
|
+
(0, config_1.log)('uploadProgress', events);
|
|
141
|
+
for (const event of events) {
|
|
142
|
+
const { id, ...rest } = event;
|
|
143
|
+
const task = uploadTasksMap.get(id);
|
|
144
|
+
if (task)
|
|
145
|
+
task.onProgress(rest);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
(_c = turboModule.onUploadComplete) === null || _c === void 0 ? void 0 : _c.call(turboModule, (data) => {
|
|
149
|
+
const { id, ...rest } = data;
|
|
150
|
+
(0, config_1.log)('uploadComplete', id, rest);
|
|
151
|
+
const task = uploadTasksMap.get(id);
|
|
152
|
+
if (!task)
|
|
153
|
+
(0, config_1.log)('uploadComplete: task not found in uploadTasksMap', id);
|
|
154
|
+
else
|
|
155
|
+
task.onDone(rest);
|
|
156
|
+
uploadTasksMap.delete(id);
|
|
157
|
+
});
|
|
158
|
+
(_d = turboModule.onUploadFailed) === null || _d === void 0 ? void 0 : _d.call(turboModule, (data) => {
|
|
159
|
+
const { id, ...rest } = data;
|
|
160
|
+
(0, config_1.log)('uploadFailed', id, rest);
|
|
161
|
+
const task = uploadTasksMap.get(id);
|
|
162
|
+
if (!task)
|
|
163
|
+
(0, config_1.log)('uploadFailed: task not found in uploadTasksMap', id);
|
|
164
|
+
else
|
|
165
|
+
task.onError(rest);
|
|
166
|
+
uploadTasksMap.delete(id);
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
// Old architecture: use NativeEventEmitter with the native module
|
|
172
|
+
// RCTEventEmitter on native side requires NativeEventEmitter on JS side
|
|
173
|
+
// RNBackgroundDownloader is guaranteed to be non-null here since initializeEventListeners
|
|
174
|
+
// is only called after ensureNativeModuleInitialized() succeeds
|
|
175
|
+
const eventEmitter = new react_native_1.NativeEventEmitter(RNBackgroundDownloader);
|
|
176
|
+
eventSubscriptions.push(eventEmitter.addListener('downloadBegin', (data) => {
|
|
177
|
+
const { id, ...rest } = data;
|
|
178
|
+
(0, config_1.log)('downloadBegin', id, rest);
|
|
179
|
+
const task = tasksMap.get(id);
|
|
180
|
+
if (!task) {
|
|
181
|
+
(0, config_1.log)('downloadBegin: task not found in tasksMap', id);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
task.onBegin(rest);
|
|
185
|
+
}));
|
|
186
|
+
eventSubscriptions.push(eventEmitter.addListener('downloadProgress', (events) => {
|
|
187
|
+
(0, config_1.log)('downloadProgress', events);
|
|
188
|
+
for (const event of events) {
|
|
189
|
+
const { id, ...rest } = event;
|
|
190
|
+
const task = tasksMap.get(id);
|
|
191
|
+
if (task)
|
|
192
|
+
task.onProgress(rest);
|
|
193
|
+
}
|
|
194
|
+
}));
|
|
195
|
+
eventSubscriptions.push(eventEmitter.addListener('downloadComplete', (data) => {
|
|
196
|
+
const { id, ...rest } = data;
|
|
197
|
+
(0, config_1.log)('downloadComplete', id, rest);
|
|
198
|
+
const task = tasksMap.get(id);
|
|
199
|
+
if (!task)
|
|
200
|
+
(0, config_1.log)('downloadComplete: task not found in tasksMap', id);
|
|
201
|
+
else
|
|
202
|
+
task.onDone(rest);
|
|
203
|
+
tasksMap.delete(id);
|
|
204
|
+
}));
|
|
205
|
+
eventSubscriptions.push(eventEmitter.addListener('downloadFailed', (data) => {
|
|
206
|
+
const { id, ...rest } = data;
|
|
207
|
+
(0, config_1.log)('downloadFailed', id, rest);
|
|
208
|
+
const task = tasksMap.get(id);
|
|
209
|
+
if (!task)
|
|
210
|
+
(0, config_1.log)('downloadFailed: task not found in tasksMap', id);
|
|
211
|
+
else
|
|
212
|
+
task.onError(rest);
|
|
213
|
+
tasksMap.delete(id);
|
|
214
|
+
}));
|
|
215
|
+
// Upload events for old architecture
|
|
216
|
+
eventSubscriptions.push(eventEmitter.addListener('uploadBegin', (data) => {
|
|
217
|
+
const { id, ...rest } = data;
|
|
218
|
+
(0, config_1.log)('uploadBegin', id, rest);
|
|
219
|
+
const task = uploadTasksMap.get(id);
|
|
220
|
+
if (!task) {
|
|
221
|
+
(0, config_1.log)('uploadBegin: task not found in uploadTasksMap', id);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
task.onBegin(rest);
|
|
225
|
+
}));
|
|
226
|
+
eventSubscriptions.push(eventEmitter.addListener('uploadProgress', (events) => {
|
|
227
|
+
(0, config_1.log)('uploadProgress', events);
|
|
228
|
+
for (const event of events) {
|
|
229
|
+
const { id, ...rest } = event;
|
|
230
|
+
const task = uploadTasksMap.get(id);
|
|
231
|
+
if (task)
|
|
232
|
+
task.onProgress(rest);
|
|
233
|
+
}
|
|
234
|
+
}));
|
|
235
|
+
eventSubscriptions.push(eventEmitter.addListener('uploadComplete', (data) => {
|
|
236
|
+
const { id, ...rest } = data;
|
|
237
|
+
(0, config_1.log)('uploadComplete', id, rest);
|
|
238
|
+
const task = uploadTasksMap.get(id);
|
|
239
|
+
if (!task)
|
|
240
|
+
(0, config_1.log)('uploadComplete: task not found in uploadTasksMap', id);
|
|
241
|
+
else
|
|
242
|
+
task.onDone(rest);
|
|
243
|
+
uploadTasksMap.delete(id);
|
|
244
|
+
}));
|
|
245
|
+
eventSubscriptions.push(eventEmitter.addListener('uploadFailed', (data) => {
|
|
246
|
+
const { id, ...rest } = data;
|
|
247
|
+
(0, config_1.log)('uploadFailed', id, rest);
|
|
248
|
+
const task = uploadTasksMap.get(id);
|
|
249
|
+
if (!task)
|
|
250
|
+
(0, config_1.log)('uploadFailed: task not found in uploadTasksMap', id);
|
|
251
|
+
else
|
|
252
|
+
task.onError(rest);
|
|
253
|
+
uploadTasksMap.delete(id);
|
|
254
|
+
}));
|
|
255
|
+
// Native debug log events - forward native iOS logs to JS logCallback
|
|
256
|
+
eventSubscriptions.push(eventEmitter.addListener('nativeDebugLog', (data) => {
|
|
257
|
+
(0, config_1.log)('[Native]', data.taskId || '', data.message);
|
|
258
|
+
}));
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
// Event listeners are now initialized lazily when ensureNativeModuleInitialized() is called
|
|
262
|
+
// This ensures the bridge is ready before any native module access
|
|
263
|
+
function setConfig({ headers = {}, progressInterval = config_1.DEFAULT_PROGRESS_INTERVAL, progressMinBytes = config_1.DEFAULT_PROGRESS_MIN_BYTES, isLogsEnabled = false, logCallback, maxParallelDownloads, allowsCellularAccess, showNotificationsEnabled, notificationsGrouping, iosDataProtection, }) {
|
|
264
|
+
var _a, _b, _c;
|
|
265
|
+
config_1.config.headers = headers;
|
|
266
|
+
if (iosDataProtection !== undefined)
|
|
267
|
+
config_1.config.iosDataProtection = iosDataProtection;
|
|
268
|
+
if (progressInterval >= MIN_PROGRESS_INTERVAL)
|
|
269
|
+
config_1.config.progressInterval = progressInterval;
|
|
270
|
+
else
|
|
271
|
+
console.warn(`[RNBackgroundDownloader] progressInterval must be a number >= ${MIN_PROGRESS_INTERVAL}. You passed ${progressInterval}`);
|
|
272
|
+
if (progressMinBytes >= 0)
|
|
273
|
+
config_1.config.progressMinBytes = progressMinBytes;
|
|
274
|
+
else
|
|
275
|
+
console.warn(`[RNBackgroundDownloader] progressMinBytes must be a number >= 0. You passed ${progressMinBytes}`);
|
|
276
|
+
if (maxParallelDownloads !== undefined)
|
|
277
|
+
if (maxParallelDownloads >= 1)
|
|
278
|
+
config_1.config.maxParallelDownloads = maxParallelDownloads;
|
|
279
|
+
else
|
|
280
|
+
console.warn(`[RNBackgroundDownloader] maxParallelDownloads must be a number >= 1. You passed ${maxParallelDownloads}`);
|
|
281
|
+
if (allowsCellularAccess !== undefined)
|
|
282
|
+
config_1.config.allowsCellularAccess = allowsCellularAccess;
|
|
283
|
+
// Update showNotificationsEnabled
|
|
284
|
+
if (showNotificationsEnabled !== undefined)
|
|
285
|
+
config_1.config.showNotificationsEnabled = showNotificationsEnabled;
|
|
286
|
+
// Update notification grouping config
|
|
287
|
+
if (notificationsGrouping !== undefined)
|
|
288
|
+
config_1.config.notificationsGrouping = {
|
|
289
|
+
enabled: (_a = notificationsGrouping.enabled) !== null && _a !== void 0 ? _a : false,
|
|
290
|
+
mode: (_b = notificationsGrouping.mode) !== null && _b !== void 0 ? _b : 'individual',
|
|
291
|
+
texts: {
|
|
292
|
+
...config_1.DEFAULT_NOTIFICATION_TEXTS,
|
|
293
|
+
...notificationsGrouping.texts,
|
|
294
|
+
},
|
|
295
|
+
};
|
|
296
|
+
config_1.config.isLogsEnabled = isLogsEnabled;
|
|
297
|
+
config_1.config.logCallback = logCallback;
|
|
298
|
+
// Notify native side about configuration changes
|
|
299
|
+
try {
|
|
300
|
+
const nativeModule = ensureNativeModuleInitialized();
|
|
301
|
+
if (nativeModule.setLogsEnabled)
|
|
302
|
+
nativeModule.setLogsEnabled(isLogsEnabled);
|
|
303
|
+
// Only call native methods if config was successfully updated
|
|
304
|
+
if (nativeModule.setMaxParallelDownloads && maxParallelDownloads !== undefined && maxParallelDownloads >= 1)
|
|
305
|
+
nativeModule.setMaxParallelDownloads(config_1.config.maxParallelDownloads);
|
|
306
|
+
if (nativeModule.setAllowsCellularAccess && allowsCellularAccess !== undefined)
|
|
307
|
+
nativeModule.setAllowsCellularAccess(config_1.config.allowsCellularAccess);
|
|
308
|
+
// Update notification config on native side (Android)
|
|
309
|
+
if (react_native_1.Platform.OS === 'android' && nativeModule.setNotificationGroupingConfig)
|
|
310
|
+
nativeModule.setNotificationGroupingConfig({
|
|
311
|
+
enabled: config_1.config.notificationsGrouping.enabled,
|
|
312
|
+
showNotificationsEnabled: (_c = config_1.config.showNotificationsEnabled) !== null && _c !== void 0 ? _c : false,
|
|
313
|
+
mode: config_1.config.notificationsGrouping.mode,
|
|
314
|
+
texts: (0, config_1.getNotificationTextsForNative)(),
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
catch {
|
|
318
|
+
// Ignore if native module is not available yet
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
const getExistingDownloadTasks = async () => {
|
|
322
|
+
const nativeModule = ensureNativeModuleInitialized();
|
|
323
|
+
const downloads = await nativeModule.getExistingDownloadTasks();
|
|
324
|
+
const downloadTasks = downloads.map(downloadInfo => {
|
|
325
|
+
var _a;
|
|
326
|
+
// Parse metadata from JSON string to object
|
|
327
|
+
let metadata = {};
|
|
328
|
+
if (downloadInfo.metadata)
|
|
329
|
+
try {
|
|
330
|
+
metadata = JSON.parse(downloadInfo.metadata);
|
|
331
|
+
}
|
|
332
|
+
catch {
|
|
333
|
+
// Keep empty object if parsing fails
|
|
334
|
+
}
|
|
335
|
+
const taskInfo = {
|
|
336
|
+
...downloadInfo,
|
|
337
|
+
metadata,
|
|
338
|
+
errorCode: (_a = downloadInfo.errorCode) !== null && _a !== void 0 ? _a : 0,
|
|
339
|
+
};
|
|
340
|
+
// second argument re-assigns event handlers
|
|
341
|
+
const task = new DownloadTask_1.DownloadTask(taskInfo, tasksMap.get(taskInfo.id));
|
|
342
|
+
switch (taskInfo.state) {
|
|
343
|
+
case nativeModule.TaskRunning: {
|
|
344
|
+
task.state = 'DOWNLOADING';
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
case nativeModule.TaskSuspended: {
|
|
348
|
+
task.state = 'PAUSED';
|
|
349
|
+
break;
|
|
350
|
+
}
|
|
351
|
+
case nativeModule.TaskCanceling: {
|
|
352
|
+
// On iOS, paused tasks (via cancelByProducingResumeData) are in Canceling state with errorCode -999
|
|
353
|
+
if (taskInfo.errorCode === -999) {
|
|
354
|
+
task.state = 'PAUSED';
|
|
355
|
+
}
|
|
356
|
+
else {
|
|
357
|
+
task.stop();
|
|
358
|
+
return undefined;
|
|
359
|
+
}
|
|
360
|
+
break;
|
|
361
|
+
}
|
|
362
|
+
case nativeModule.TaskCompleted: {
|
|
363
|
+
if (taskInfo.bytesDownloaded === taskInfo.bytesTotal)
|
|
364
|
+
task.state = 'DONE';
|
|
365
|
+
else
|
|
366
|
+
// IOS completed the download but it was not done.
|
|
367
|
+
return undefined;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
return task;
|
|
371
|
+
}).filter((task) => task !== undefined);
|
|
372
|
+
for (const task of downloadTasks)
|
|
373
|
+
tasksMap.set(task.id, task);
|
|
374
|
+
return downloadTasks;
|
|
375
|
+
};
|
|
376
|
+
exports.getExistingDownloadTasks = getExistingDownloadTasks;
|
|
377
|
+
const completeHandler = (jobId) => {
|
|
378
|
+
if (jobId == null) {
|
|
379
|
+
(0, config_1.log)('completeHandler: jobId is empty');
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
const nativeModule = ensureNativeModuleInitialized();
|
|
383
|
+
return nativeModule.completeHandler(jobId);
|
|
384
|
+
};
|
|
385
|
+
exports.completeHandler = completeHandler;
|
|
386
|
+
function createDownloadTask({ isAllowedOverRoaming = true, isAllowedOverMetered = true, metadata, ...rest }) {
|
|
387
|
+
// Ensure native module and event listeners are initialized before creating tasks
|
|
388
|
+
ensureNativeModuleInitialized();
|
|
389
|
+
if (!rest.id || !rest.url || !rest.destination)
|
|
390
|
+
throw new Error('[RNBackgroundDownloader] id, url and destination are required');
|
|
391
|
+
rest.headers = { ...config_1.config.headers, ...rest.headers };
|
|
392
|
+
rest.destination = rest.destination.replace('file://', '');
|
|
393
|
+
const task = new DownloadTask_1.DownloadTask({
|
|
394
|
+
id: rest.id,
|
|
395
|
+
metadata,
|
|
396
|
+
});
|
|
397
|
+
task.setDownloadParams({
|
|
398
|
+
isAllowedOverRoaming,
|
|
399
|
+
isAllowedOverMetered,
|
|
400
|
+
...rest,
|
|
401
|
+
});
|
|
402
|
+
tasksMap.set(rest.id, task);
|
|
403
|
+
return task;
|
|
404
|
+
}
|
|
405
|
+
const getExistingUploadTasks = async () => {
|
|
406
|
+
const nativeModule = ensureNativeModuleInitialized();
|
|
407
|
+
if (!nativeModule.getExistingUploadTasks) {
|
|
408
|
+
(0, config_1.log)('getExistingUploadTasks: not supported - native implementation missing');
|
|
409
|
+
return [];
|
|
410
|
+
}
|
|
411
|
+
const uploads = await nativeModule.getExistingUploadTasks();
|
|
412
|
+
const uploadTasks = uploads.map(uploadInfo => {
|
|
413
|
+
var _a;
|
|
414
|
+
// Parse metadata from JSON string to object
|
|
415
|
+
let metadata = {};
|
|
416
|
+
if (uploadInfo.metadata)
|
|
417
|
+
try {
|
|
418
|
+
metadata = JSON.parse(uploadInfo.metadata);
|
|
419
|
+
}
|
|
420
|
+
catch {
|
|
421
|
+
// Keep empty object if parsing fails
|
|
422
|
+
}
|
|
423
|
+
const taskInfo = {
|
|
424
|
+
...uploadInfo,
|
|
425
|
+
metadata,
|
|
426
|
+
errorCode: (_a = uploadInfo.errorCode) !== null && _a !== void 0 ? _a : 0,
|
|
427
|
+
};
|
|
428
|
+
// second argument re-assigns event handlers
|
|
429
|
+
const task = new UploadTask_1.UploadTask(taskInfo, uploadTasksMap.get(taskInfo.id));
|
|
430
|
+
switch (taskInfo.state) {
|
|
431
|
+
case nativeModule.TaskRunning: {
|
|
432
|
+
task.state = 'UPLOADING';
|
|
433
|
+
break;
|
|
434
|
+
}
|
|
435
|
+
case nativeModule.TaskSuspended: {
|
|
436
|
+
task.state = 'PAUSED';
|
|
437
|
+
break;
|
|
438
|
+
}
|
|
439
|
+
case nativeModule.TaskCanceling: {
|
|
440
|
+
// On iOS, paused tasks (via cancelByProducingResumeData) are in Canceling state with errorCode -999
|
|
441
|
+
if (taskInfo.errorCode === -999) {
|
|
442
|
+
task.state = 'PAUSED';
|
|
443
|
+
}
|
|
444
|
+
else {
|
|
445
|
+
task.stop();
|
|
446
|
+
return undefined;
|
|
447
|
+
}
|
|
448
|
+
break;
|
|
449
|
+
}
|
|
450
|
+
case nativeModule.TaskCompleted: {
|
|
451
|
+
if (taskInfo.bytesUploaded === taskInfo.bytesTotal)
|
|
452
|
+
task.state = 'DONE';
|
|
453
|
+
else
|
|
454
|
+
// IOS completed the upload but it was not done.
|
|
455
|
+
return undefined;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
return task;
|
|
459
|
+
}).filter((task) => task !== undefined);
|
|
460
|
+
for (const task of uploadTasks)
|
|
461
|
+
uploadTasksMap.set(task.id, task);
|
|
462
|
+
return uploadTasks;
|
|
463
|
+
};
|
|
464
|
+
exports.getExistingUploadTasks = getExistingUploadTasks;
|
|
465
|
+
function createUploadTask({ isAllowedOverRoaming = true, isAllowedOverMetered = true, metadata, ...rest }) {
|
|
466
|
+
// Ensure native module and event listeners are initialized before creating tasks
|
|
467
|
+
ensureNativeModuleInitialized();
|
|
468
|
+
if (!rest.id || !rest.url || !rest.source)
|
|
469
|
+
throw new Error('[RNBackgroundDownloader] id, url and source are required');
|
|
470
|
+
rest.headers = { ...config_1.config.headers, ...rest.headers };
|
|
471
|
+
rest.source = rest.source.replace('file://', '');
|
|
472
|
+
const task = new UploadTask_1.UploadTask({
|
|
473
|
+
id: rest.id,
|
|
474
|
+
metadata,
|
|
475
|
+
});
|
|
476
|
+
task.setUploadParams({
|
|
477
|
+
isAllowedOverRoaming,
|
|
478
|
+
isAllowedOverMetered,
|
|
479
|
+
...rest,
|
|
480
|
+
});
|
|
481
|
+
uploadTasksMap.set(rest.id, task);
|
|
482
|
+
return task;
|
|
483
|
+
}
|
|
484
|
+
// Use getter to lazily initialize native module when directories are accessed
|
|
485
|
+
exports.directories = {
|
|
486
|
+
get documents() {
|
|
487
|
+
return ensureNativeModuleInitialized().documents;
|
|
488
|
+
},
|
|
489
|
+
};
|
|
490
|
+
/**
|
|
491
|
+
* Get the native module instance.
|
|
492
|
+
* This is exported for internal use by DownloadTask to avoid duplicating
|
|
493
|
+
* the TurboModule/NativeModule lookup logic.
|
|
494
|
+
* @internal
|
|
495
|
+
*/
|
|
496
|
+
function getNativeModule() {
|
|
497
|
+
return ensureNativeModuleInitialized();
|
|
498
|
+
}
|