@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
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DownloadTask = void 0;
|
|
4
|
+
const config_1 = require("./config");
|
|
5
|
+
// Import shared native module getter to avoid duplicating TurboModule lookup
|
|
6
|
+
// This is lazily imported to avoid circular dependency issues at module load time
|
|
7
|
+
let getNativeModuleImpl = null;
|
|
8
|
+
function getNativeModule() {
|
|
9
|
+
if (!getNativeModuleImpl)
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
11
|
+
getNativeModuleImpl = require('./index').getNativeModule;
|
|
12
|
+
return getNativeModuleImpl();
|
|
13
|
+
}
|
|
14
|
+
class DownloadTask {
|
|
15
|
+
constructor(taskParams, originalTask) {
|
|
16
|
+
var _a, _b;
|
|
17
|
+
this.id = '';
|
|
18
|
+
this.metadata = {};
|
|
19
|
+
this.state = 'PENDING';
|
|
20
|
+
this.errorCode = 0;
|
|
21
|
+
this.bytesDownloaded = 0;
|
|
22
|
+
this.bytesTotal = 0;
|
|
23
|
+
this.id = taskParams.id;
|
|
24
|
+
if (taskParams.bytesDownloaded)
|
|
25
|
+
this.bytesDownloaded = taskParams.bytesDownloaded;
|
|
26
|
+
if (taskParams.bytesTotal)
|
|
27
|
+
this.bytesTotal = taskParams.bytesTotal;
|
|
28
|
+
if (taskParams.destination)
|
|
29
|
+
this.destination = (_a = taskParams.destination) !== null && _a !== void 0 ? _a : undefined;
|
|
30
|
+
this.metadata = (_b = this.tryParseJson(taskParams.metadata)) !== null && _b !== void 0 ? _b : {};
|
|
31
|
+
if (originalTask) {
|
|
32
|
+
this.beginHandler = originalTask.beginHandler;
|
|
33
|
+
this.progressHandler = originalTask.progressHandler;
|
|
34
|
+
this.doneHandler = originalTask.doneHandler;
|
|
35
|
+
this.errorHandler = originalTask.errorHandler;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// event listeners setters
|
|
39
|
+
begin(handler) {
|
|
40
|
+
if (typeof handler !== 'function')
|
|
41
|
+
throw new Error('begin handler must be a function');
|
|
42
|
+
this.beginHandler = handler;
|
|
43
|
+
return this;
|
|
44
|
+
}
|
|
45
|
+
progress(handler) {
|
|
46
|
+
if (typeof handler !== 'function')
|
|
47
|
+
throw new Error('progress handler must be a function');
|
|
48
|
+
this.progressHandler = handler;
|
|
49
|
+
return this;
|
|
50
|
+
}
|
|
51
|
+
done(handler) {
|
|
52
|
+
if (typeof handler !== 'function')
|
|
53
|
+
throw new Error('done handler must be a function');
|
|
54
|
+
this.doneHandler = handler;
|
|
55
|
+
return this;
|
|
56
|
+
}
|
|
57
|
+
error(handler) {
|
|
58
|
+
if (typeof handler !== 'function')
|
|
59
|
+
throw new Error('error handler must be a function');
|
|
60
|
+
this.errorHandler = handler;
|
|
61
|
+
return this;
|
|
62
|
+
}
|
|
63
|
+
// event listeners
|
|
64
|
+
onBegin(params) {
|
|
65
|
+
var _a;
|
|
66
|
+
this.state = 'DOWNLOADING';
|
|
67
|
+
this.bytesTotal = params.expectedBytes;
|
|
68
|
+
(_a = this.beginHandler) === null || _a === void 0 ? void 0 : _a.call(this, params);
|
|
69
|
+
}
|
|
70
|
+
onProgress(params) {
|
|
71
|
+
var _a;
|
|
72
|
+
this.bytesDownloaded = params.bytesDownloaded;
|
|
73
|
+
this.bytesTotal = params.bytesTotal;
|
|
74
|
+
(_a = this.progressHandler) === null || _a === void 0 ? void 0 : _a.call(this, params);
|
|
75
|
+
}
|
|
76
|
+
onDone(params) {
|
|
77
|
+
var _a;
|
|
78
|
+
this.state = 'DONE';
|
|
79
|
+
this.bytesDownloaded = params.bytesDownloaded;
|
|
80
|
+
this.bytesTotal = params.bytesTotal;
|
|
81
|
+
(_a = this.doneHandler) === null || _a === void 0 ? void 0 : _a.call(this, params);
|
|
82
|
+
}
|
|
83
|
+
onError(params) {
|
|
84
|
+
var _a;
|
|
85
|
+
this.state = 'FAILED';
|
|
86
|
+
(_a = this.errorHandler) === null || _a === void 0 ? void 0 : _a.call(this, params);
|
|
87
|
+
}
|
|
88
|
+
// methods
|
|
89
|
+
/**
|
|
90
|
+
* Update download parameters.
|
|
91
|
+
* If the task is paused, this will also update headers in the native layer.
|
|
92
|
+
* If the task is in-progress or completed, only the local JS object is updated.
|
|
93
|
+
*
|
|
94
|
+
* @param downloadParams - The new download parameters
|
|
95
|
+
* @returns Promise<boolean> - true if native headers were updated, false otherwise
|
|
96
|
+
*/
|
|
97
|
+
async setDownloadParams(downloadParams) {
|
|
98
|
+
this.downloadParams = downloadParams;
|
|
99
|
+
// If task is paused, update headers in native layer
|
|
100
|
+
if (this.state === 'PAUSED' && downloadParams.headers) {
|
|
101
|
+
const headers = this.headersToUnsafeObject(downloadParams.headers);
|
|
102
|
+
if (headers) {
|
|
103
|
+
(0, config_1.log)('DownloadTask: setDownloadParams updating native headers', this.id);
|
|
104
|
+
return getNativeModule().updateTaskHeaders(this.id, headers);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
async pause() {
|
|
110
|
+
(0, config_1.log)('DownloadTask: pause', this.id);
|
|
111
|
+
this.state = 'PAUSED';
|
|
112
|
+
await getNativeModule().pauseTask(this.id);
|
|
113
|
+
}
|
|
114
|
+
async resume() {
|
|
115
|
+
(0, config_1.log)('DownloadTask: resume', this.id);
|
|
116
|
+
this.state = 'DOWNLOADING';
|
|
117
|
+
this.errorCode = 0;
|
|
118
|
+
await getNativeModule().resumeTask(this.id);
|
|
119
|
+
}
|
|
120
|
+
start() {
|
|
121
|
+
var _a, _b, _c, _d, _e;
|
|
122
|
+
if (this.state !== 'PENDING') {
|
|
123
|
+
(0, config_1.log)('DownloadTask: start. Download already started, can\' start again... ', this.id);
|
|
124
|
+
(_a = this.errorHandler) === null || _a === void 0 ? void 0 : _a.call(this, { error: 'Download already started', errorCode: -1 });
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (!this.downloadParams) {
|
|
128
|
+
(0, config_1.log)('DownloadTask: start. downloadParams is missing. "setDownloadParams" wasn\'t called before "start"', this.id);
|
|
129
|
+
(_b = this.errorHandler) === null || _b === void 0 ? void 0 : _b.call(this, { error: 'downloadParams is missing. setDownloadParams must be called before start', errorCode: -2 });
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
this.state = 'DOWNLOADING';
|
|
133
|
+
// kick-off download after returning the task
|
|
134
|
+
getNativeModule().download({
|
|
135
|
+
id: this.id,
|
|
136
|
+
metadata: JSON.stringify(this.metadata),
|
|
137
|
+
progressInterval: config_1.config.progressInterval,
|
|
138
|
+
progressMinBytes: config_1.config.progressMinBytes,
|
|
139
|
+
...this.downloadParams,
|
|
140
|
+
headers: this.headersToUnsafeObject(this.downloadParams.headers),
|
|
141
|
+
isAllowedOverRoaming: (_c = this.downloadParams.isAllowedOverRoaming) !== null && _c !== void 0 ? _c : false,
|
|
142
|
+
isAllowedOverMetered: (_d = this.downloadParams.isAllowedOverMetered) !== null && _d !== void 0 ? _d : false,
|
|
143
|
+
// iOS-only; ignored on Android. Falls back to the global setConfig default.
|
|
144
|
+
iosDataProtection: (_e = this.downloadParams.iosDataProtection) !== null && _e !== void 0 ? _e : config_1.config.iosDataProtection,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
async stop() {
|
|
148
|
+
(0, config_1.log)('DownloadTask: stop', this.id);
|
|
149
|
+
this.state = 'STOPPED';
|
|
150
|
+
await getNativeModule().stopTask(this.id);
|
|
151
|
+
}
|
|
152
|
+
tryParseJson(metadata) {
|
|
153
|
+
var _a;
|
|
154
|
+
try {
|
|
155
|
+
if (typeof metadata === 'string')
|
|
156
|
+
return JSON.parse(metadata);
|
|
157
|
+
return (_a = metadata) !== null && _a !== void 0 ? _a : null;
|
|
158
|
+
}
|
|
159
|
+
catch (e) {
|
|
160
|
+
(0, config_1.log)('DownloadTask tryParseJson', e);
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
headersToUnsafeObject(headers) {
|
|
165
|
+
if (!headers)
|
|
166
|
+
return undefined;
|
|
167
|
+
// Filter out null values from headers to match native UnsafeObject type
|
|
168
|
+
return Object.keys(headers)
|
|
169
|
+
.reduce((mapped, key) => {
|
|
170
|
+
if (headers[key])
|
|
171
|
+
mapped[key] = headers[key];
|
|
172
|
+
return mapped;
|
|
173
|
+
}, {});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
exports.DownloadTask = DownloadTask;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import type { TurboModule } from 'react-native';
|
|
2
|
+
import type { EventEmitter } from 'react-native/Libraries/Types/CodegenTypes';
|
|
3
|
+
import type { UnsafeObject } from './types';
|
|
4
|
+
export type DownloadBeginEvent = {
|
|
5
|
+
id: string;
|
|
6
|
+
expectedBytes: number;
|
|
7
|
+
headers: UnsafeObject;
|
|
8
|
+
};
|
|
9
|
+
export type DownloadProgressEvent = {
|
|
10
|
+
id: string;
|
|
11
|
+
bytesDownloaded: number;
|
|
12
|
+
bytesTotal: number;
|
|
13
|
+
};
|
|
14
|
+
export type DownloadCompleteEvent = {
|
|
15
|
+
id: string;
|
|
16
|
+
location: string;
|
|
17
|
+
bytesDownloaded: number;
|
|
18
|
+
bytesTotal: number;
|
|
19
|
+
};
|
|
20
|
+
export type DownloadFailedEvent = {
|
|
21
|
+
id: string;
|
|
22
|
+
error: string;
|
|
23
|
+
errorCode: number;
|
|
24
|
+
};
|
|
25
|
+
export type UploadBeginEvent = {
|
|
26
|
+
id: string;
|
|
27
|
+
expectedBytes: number;
|
|
28
|
+
};
|
|
29
|
+
export type UploadProgressEvent = {
|
|
30
|
+
id: string;
|
|
31
|
+
bytesUploaded: number;
|
|
32
|
+
bytesTotal: number;
|
|
33
|
+
};
|
|
34
|
+
export type UploadCompleteEvent = {
|
|
35
|
+
id: string;
|
|
36
|
+
responseCode: number;
|
|
37
|
+
responseBody: string;
|
|
38
|
+
bytesUploaded: number;
|
|
39
|
+
bytesTotal: number;
|
|
40
|
+
};
|
|
41
|
+
export type UploadFailedEvent = {
|
|
42
|
+
id: string;
|
|
43
|
+
error: string;
|
|
44
|
+
errorCode: number;
|
|
45
|
+
};
|
|
46
|
+
export interface Spec extends TurboModule {
|
|
47
|
+
getConstants(): {
|
|
48
|
+
documents: string;
|
|
49
|
+
TaskRunning: number;
|
|
50
|
+
TaskSuspended: number;
|
|
51
|
+
TaskCanceling: number;
|
|
52
|
+
TaskCompleted: number;
|
|
53
|
+
};
|
|
54
|
+
download(options: {
|
|
55
|
+
id: string;
|
|
56
|
+
url: string;
|
|
57
|
+
destination: string;
|
|
58
|
+
headers?: UnsafeObject;
|
|
59
|
+
metadata?: string;
|
|
60
|
+
progressInterval?: number;
|
|
61
|
+
progressMinBytes?: number;
|
|
62
|
+
isAllowedOverRoaming: boolean;
|
|
63
|
+
isAllowedOverMetered: boolean;
|
|
64
|
+
maxRedirects?: number;
|
|
65
|
+
/** iOS only: NSFileProtection level for the saved file. Ignored on Android. */
|
|
66
|
+
iosDataProtection?: string;
|
|
67
|
+
}): void;
|
|
68
|
+
pauseTask(id: string): Promise<void>;
|
|
69
|
+
resumeTask(id: string): Promise<void>;
|
|
70
|
+
stopTask(id: string): Promise<void>;
|
|
71
|
+
updateTaskHeaders(id: string, headers: UnsafeObject): Promise<boolean>;
|
|
72
|
+
completeHandler(jobId: string): Promise<void>;
|
|
73
|
+
setLogsEnabled(enabled: boolean): void;
|
|
74
|
+
setMaxParallelDownloads(max: number): void;
|
|
75
|
+
setAllowsCellularAccess(allows: boolean): void;
|
|
76
|
+
setNotificationGroupingConfig?(config: {
|
|
77
|
+
enabled: boolean;
|
|
78
|
+
showNotificationsEnabled: boolean;
|
|
79
|
+
mode: string;
|
|
80
|
+
texts: UnsafeObject;
|
|
81
|
+
}): void;
|
|
82
|
+
getExistingDownloadTasks(): Promise<Array<{
|
|
83
|
+
id: string;
|
|
84
|
+
metadata: string;
|
|
85
|
+
state: number;
|
|
86
|
+
bytesDownloaded: number;
|
|
87
|
+
bytesTotal: number;
|
|
88
|
+
errorCode?: number | null;
|
|
89
|
+
destination?: string | null;
|
|
90
|
+
}>>;
|
|
91
|
+
upload?(options: {
|
|
92
|
+
id: string;
|
|
93
|
+
url: string;
|
|
94
|
+
source: string;
|
|
95
|
+
method: string;
|
|
96
|
+
headers?: UnsafeObject;
|
|
97
|
+
metadata?: string;
|
|
98
|
+
progressInterval?: number;
|
|
99
|
+
progressMinBytes?: number;
|
|
100
|
+
fieldName?: string;
|
|
101
|
+
mimeType?: string;
|
|
102
|
+
parameters?: UnsafeObject;
|
|
103
|
+
isAllowedOverRoaming: boolean;
|
|
104
|
+
isAllowedOverMetered: boolean;
|
|
105
|
+
}): void;
|
|
106
|
+
pauseUploadTask?(id: string): Promise<void>;
|
|
107
|
+
resumeUploadTask?(id: string): Promise<void>;
|
|
108
|
+
stopUploadTask?(id: string): Promise<void>;
|
|
109
|
+
getExistingUploadTasks?(): Promise<Array<{
|
|
110
|
+
id: string;
|
|
111
|
+
metadata: string;
|
|
112
|
+
state: number;
|
|
113
|
+
bytesUploaded: number;
|
|
114
|
+
bytesTotal: number;
|
|
115
|
+
errorCode?: number | null;
|
|
116
|
+
}>>;
|
|
117
|
+
readonly onDownloadBegin: EventEmitter<DownloadBeginEvent>;
|
|
118
|
+
readonly onDownloadProgress: EventEmitter<DownloadProgressEvent[]>;
|
|
119
|
+
readonly onDownloadComplete: EventEmitter<DownloadCompleteEvent>;
|
|
120
|
+
readonly onDownloadFailed: EventEmitter<DownloadFailedEvent>;
|
|
121
|
+
readonly onUploadBegin?: EventEmitter<UploadBeginEvent>;
|
|
122
|
+
readonly onUploadProgress?: EventEmitter<UploadProgressEvent[]>;
|
|
123
|
+
readonly onUploadComplete?: EventEmitter<UploadCompleteEvent>;
|
|
124
|
+
readonly onUploadFailed?: EventEmitter<UploadFailedEvent>;
|
|
125
|
+
}
|
|
126
|
+
declare const _default: Spec;
|
|
127
|
+
export default _default;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { UploadTaskInfo, UploadTask as UploadTaskType, UploadBeginHandler, UploadProgressHandler, UploadDoneHandler, UploadErrorHandler, UploadBeginHandlerParams, UploadProgressHandlerParams, UploadDoneHandlerParams, UploadErrorHandlerParams, UploadTaskInfoNative, UploadParams, UploadTaskState, Metadata } from './types';
|
|
2
|
+
export declare class UploadTask {
|
|
3
|
+
id: string;
|
|
4
|
+
metadata: Metadata;
|
|
5
|
+
state: UploadTaskState;
|
|
6
|
+
errorCode: number;
|
|
7
|
+
bytesUploaded: number;
|
|
8
|
+
bytesTotal: number;
|
|
9
|
+
uploadParams?: UploadParams;
|
|
10
|
+
beginHandler?: UploadBeginHandler;
|
|
11
|
+
progressHandler?: UploadProgressHandler;
|
|
12
|
+
doneHandler?: UploadDoneHandler;
|
|
13
|
+
errorHandler?: UploadErrorHandler;
|
|
14
|
+
constructor(taskParams: UploadTaskInfo | UploadTaskInfoNative, originalTask?: UploadTaskType);
|
|
15
|
+
begin(handler: UploadBeginHandler): this;
|
|
16
|
+
progress(handler: UploadProgressHandler): this;
|
|
17
|
+
done(handler: UploadDoneHandler): this;
|
|
18
|
+
error(handler: UploadErrorHandler): this;
|
|
19
|
+
onBegin(params: UploadBeginHandlerParams): void;
|
|
20
|
+
onProgress(params: UploadProgressHandlerParams): void;
|
|
21
|
+
onDone(params: UploadDoneHandlerParams): void;
|
|
22
|
+
onError(params: UploadErrorHandlerParams): void;
|
|
23
|
+
setUploadParams(uploadParams: UploadParams): void;
|
|
24
|
+
pause(): Promise<void>;
|
|
25
|
+
resume(): Promise<void>;
|
|
26
|
+
start(): void;
|
|
27
|
+
stop(): Promise<void>;
|
|
28
|
+
tryParseJson(metadata?: string | Metadata | object): Metadata | null;
|
|
29
|
+
private headersToUnsafeObject;
|
|
30
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UploadTask = void 0;
|
|
4
|
+
const config_1 = require("./config");
|
|
5
|
+
// Import shared native module getter to avoid duplicating TurboModule lookup
|
|
6
|
+
// This is lazily imported to avoid circular dependency issues at module load time
|
|
7
|
+
let getNativeModuleImpl = null;
|
|
8
|
+
function getNativeModule() {
|
|
9
|
+
if (!getNativeModuleImpl)
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
11
|
+
getNativeModuleImpl = require('./index').getNativeModule;
|
|
12
|
+
return getNativeModuleImpl();
|
|
13
|
+
}
|
|
14
|
+
class UploadTask {
|
|
15
|
+
constructor(taskParams, originalTask) {
|
|
16
|
+
var _a;
|
|
17
|
+
this.id = '';
|
|
18
|
+
this.metadata = {};
|
|
19
|
+
this.state = 'PENDING';
|
|
20
|
+
this.errorCode = 0;
|
|
21
|
+
this.bytesUploaded = 0;
|
|
22
|
+
this.bytesTotal = 0;
|
|
23
|
+
this.id = taskParams.id;
|
|
24
|
+
if (taskParams.bytesUploaded)
|
|
25
|
+
this.bytesUploaded = taskParams.bytesUploaded;
|
|
26
|
+
if (taskParams.bytesTotal)
|
|
27
|
+
this.bytesTotal = taskParams.bytesTotal;
|
|
28
|
+
this.metadata = (_a = this.tryParseJson(taskParams.metadata)) !== null && _a !== void 0 ? _a : {};
|
|
29
|
+
if (originalTask) {
|
|
30
|
+
this.beginHandler = originalTask.beginHandler;
|
|
31
|
+
this.progressHandler = originalTask.progressHandler;
|
|
32
|
+
this.doneHandler = originalTask.doneHandler;
|
|
33
|
+
this.errorHandler = originalTask.errorHandler;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// event listeners setters
|
|
37
|
+
begin(handler) {
|
|
38
|
+
if (typeof handler !== 'function')
|
|
39
|
+
throw new Error('begin handler must be a function');
|
|
40
|
+
this.beginHandler = handler;
|
|
41
|
+
return this;
|
|
42
|
+
}
|
|
43
|
+
progress(handler) {
|
|
44
|
+
if (typeof handler !== 'function')
|
|
45
|
+
throw new Error('progress handler must be a function');
|
|
46
|
+
this.progressHandler = handler;
|
|
47
|
+
return this;
|
|
48
|
+
}
|
|
49
|
+
done(handler) {
|
|
50
|
+
if (typeof handler !== 'function')
|
|
51
|
+
throw new Error('done handler must be a function');
|
|
52
|
+
this.doneHandler = handler;
|
|
53
|
+
return this;
|
|
54
|
+
}
|
|
55
|
+
error(handler) {
|
|
56
|
+
if (typeof handler !== 'function')
|
|
57
|
+
throw new Error('error handler must be a function');
|
|
58
|
+
this.errorHandler = handler;
|
|
59
|
+
return this;
|
|
60
|
+
}
|
|
61
|
+
// event listeners
|
|
62
|
+
onBegin(params) {
|
|
63
|
+
var _a;
|
|
64
|
+
this.state = 'UPLOADING';
|
|
65
|
+
this.bytesTotal = params.expectedBytes;
|
|
66
|
+
(_a = this.beginHandler) === null || _a === void 0 ? void 0 : _a.call(this, params);
|
|
67
|
+
}
|
|
68
|
+
onProgress(params) {
|
|
69
|
+
var _a;
|
|
70
|
+
this.bytesUploaded = params.bytesUploaded;
|
|
71
|
+
this.bytesTotal = params.bytesTotal;
|
|
72
|
+
(_a = this.progressHandler) === null || _a === void 0 ? void 0 : _a.call(this, params);
|
|
73
|
+
}
|
|
74
|
+
onDone(params) {
|
|
75
|
+
var _a;
|
|
76
|
+
this.state = 'DONE';
|
|
77
|
+
this.bytesUploaded = params.bytesUploaded;
|
|
78
|
+
this.bytesTotal = params.bytesTotal;
|
|
79
|
+
(_a = this.doneHandler) === null || _a === void 0 ? void 0 : _a.call(this, params);
|
|
80
|
+
}
|
|
81
|
+
onError(params) {
|
|
82
|
+
var _a;
|
|
83
|
+
this.state = 'FAILED';
|
|
84
|
+
(_a = this.errorHandler) === null || _a === void 0 ? void 0 : _a.call(this, params);
|
|
85
|
+
}
|
|
86
|
+
// methods
|
|
87
|
+
setUploadParams(uploadParams) {
|
|
88
|
+
this.uploadParams = uploadParams;
|
|
89
|
+
}
|
|
90
|
+
async pause() {
|
|
91
|
+
(0, config_1.log)('UploadTask: pause', this.id);
|
|
92
|
+
this.state = 'PAUSED';
|
|
93
|
+
const nativeModule = getNativeModule();
|
|
94
|
+
if (nativeModule.pauseUploadTask)
|
|
95
|
+
await nativeModule.pauseUploadTask(this.id);
|
|
96
|
+
else
|
|
97
|
+
(0, config_1.log)('UploadTask: pause not supported - native implementation missing');
|
|
98
|
+
}
|
|
99
|
+
async resume() {
|
|
100
|
+
(0, config_1.log)('UploadTask: resume', this.id);
|
|
101
|
+
this.state = 'UPLOADING';
|
|
102
|
+
this.errorCode = 0;
|
|
103
|
+
const nativeModule = getNativeModule();
|
|
104
|
+
if (nativeModule.resumeUploadTask)
|
|
105
|
+
await nativeModule.resumeUploadTask(this.id);
|
|
106
|
+
else
|
|
107
|
+
(0, config_1.log)('UploadTask: resume not supported - native implementation missing');
|
|
108
|
+
}
|
|
109
|
+
start() {
|
|
110
|
+
var _a, _b, _c, _d, _e, _f;
|
|
111
|
+
if (this.state !== 'PENDING') {
|
|
112
|
+
(0, config_1.log)('UploadTask: start. Upload already started, can\' start again... ', this.id);
|
|
113
|
+
(_a = this.errorHandler) === null || _a === void 0 ? void 0 : _a.call(this, { error: 'Upload already started', errorCode: -1 });
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (!this.uploadParams) {
|
|
117
|
+
(0, config_1.log)('UploadTask: start. uploadParams is missing. "setUploadParams" wasn\'t called before "start"', this.id);
|
|
118
|
+
(_b = this.errorHandler) === null || _b === void 0 ? void 0 : _b.call(this, { error: 'uploadParams is missing. setUploadParams must be called before start', errorCode: -2 });
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const nativeModule = getNativeModule();
|
|
122
|
+
if (!nativeModule.upload) {
|
|
123
|
+
(0, config_1.log)('UploadTask: start. Upload not supported - native implementation missing');
|
|
124
|
+
(_c = this.errorHandler) === null || _c === void 0 ? void 0 : _c.call(this, { error: 'Upload not supported - native implementation missing', errorCode: -3 });
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
this.state = 'UPLOADING';
|
|
128
|
+
// kick-off upload after returning the task
|
|
129
|
+
nativeModule.upload({
|
|
130
|
+
id: this.id,
|
|
131
|
+
metadata: JSON.stringify(this.metadata),
|
|
132
|
+
progressInterval: config_1.config.progressInterval,
|
|
133
|
+
progressMinBytes: config_1.config.progressMinBytes,
|
|
134
|
+
...this.uploadParams,
|
|
135
|
+
method: (_d = this.uploadParams.method) !== null && _d !== void 0 ? _d : 'POST',
|
|
136
|
+
headers: this.headersToUnsafeObject(this.uploadParams.headers),
|
|
137
|
+
isAllowedOverRoaming: (_e = this.uploadParams.isAllowedOverRoaming) !== null && _e !== void 0 ? _e : false,
|
|
138
|
+
isAllowedOverMetered: (_f = this.uploadParams.isAllowedOverMetered) !== null && _f !== void 0 ? _f : false,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
async stop() {
|
|
142
|
+
(0, config_1.log)('UploadTask: stop', this.id);
|
|
143
|
+
this.state = 'STOPPED';
|
|
144
|
+
const nativeModule = getNativeModule();
|
|
145
|
+
if (nativeModule.stopUploadTask)
|
|
146
|
+
await nativeModule.stopUploadTask(this.id);
|
|
147
|
+
else
|
|
148
|
+
(0, config_1.log)('UploadTask: stop not supported - native implementation missing');
|
|
149
|
+
}
|
|
150
|
+
tryParseJson(metadata) {
|
|
151
|
+
var _a;
|
|
152
|
+
try {
|
|
153
|
+
if (typeof metadata === 'string')
|
|
154
|
+
return JSON.parse(metadata);
|
|
155
|
+
return (_a = metadata) !== null && _a !== void 0 ? _a : null;
|
|
156
|
+
}
|
|
157
|
+
catch (e) {
|
|
158
|
+
(0, config_1.log)('UploadTask tryParseJson', e);
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
headersToUnsafeObject(headers) {
|
|
163
|
+
if (!headers)
|
|
164
|
+
return undefined;
|
|
165
|
+
// Filter out null values from headers to match native UnsafeObject type
|
|
166
|
+
return Object.keys(headers)
|
|
167
|
+
.reduce((mapped, key) => {
|
|
168
|
+
if (headers[key])
|
|
169
|
+
mapped[key] = headers[key];
|
|
170
|
+
return mapped;
|
|
171
|
+
}, {});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
exports.UploadTask = UploadTask;
|
package/lib/config.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Headers, IosDataProtection, NotificationGroupingMode, NotificationsGroupingConfig, NotificationTexts } from './types';
|
|
2
|
+
export declare const DEFAULT_PROGRESS_INTERVAL = 1000;
|
|
3
|
+
export declare const DEFAULT_PROGRESS_MIN_BYTES: number;
|
|
4
|
+
export declare const DEFAULT_MAX_PARALLEL_DOWNLOADS = 4;
|
|
5
|
+
export declare const DEFAULT_ALLOWS_CELLULAR_ACCESS = true;
|
|
6
|
+
export declare const DEFAULT_IOS_DATA_PROTECTION: IosDataProtection;
|
|
7
|
+
export declare const DEFAULT_NOTIFICATION_TEXTS: Required<NotificationTexts>;
|
|
8
|
+
type LogCallback = (tag: string, message: string, ...args: unknown[]) => void;
|
|
9
|
+
interface ConfigState {
|
|
10
|
+
headers: Headers;
|
|
11
|
+
progressInterval: number;
|
|
12
|
+
progressMinBytes: number;
|
|
13
|
+
isLogsEnabled: boolean;
|
|
14
|
+
logCallback?: LogCallback;
|
|
15
|
+
maxParallelDownloads: number;
|
|
16
|
+
allowsCellularAccess: boolean;
|
|
17
|
+
showNotificationsEnabled: boolean;
|
|
18
|
+
notificationsGrouping: NotificationsGroupingConfig & {
|
|
19
|
+
mode: NotificationGroupingMode;
|
|
20
|
+
};
|
|
21
|
+
iosDataProtection: IosDataProtection;
|
|
22
|
+
}
|
|
23
|
+
export declare const config: ConfigState;
|
|
24
|
+
/**
|
|
25
|
+
* Get notification text with proper pluralization.
|
|
26
|
+
*/
|
|
27
|
+
export declare function getGroupText(count: number): string;
|
|
28
|
+
/**
|
|
29
|
+
* Get notification texts config for native side (serializable).
|
|
30
|
+
*/
|
|
31
|
+
export declare function getNotificationTextsForNative(): Record<string, string>;
|
|
32
|
+
export declare const log: (...args: unknown[]) => void;
|
|
33
|
+
export {};
|
package/lib/config.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.log = exports.config = exports.DEFAULT_NOTIFICATION_TEXTS = exports.DEFAULT_IOS_DATA_PROTECTION = exports.DEFAULT_ALLOWS_CELLULAR_ACCESS = exports.DEFAULT_MAX_PARALLEL_DOWNLOADS = exports.DEFAULT_PROGRESS_MIN_BYTES = exports.DEFAULT_PROGRESS_INTERVAL = void 0;
|
|
4
|
+
exports.getGroupText = getGroupText;
|
|
5
|
+
exports.getNotificationTextsForNative = getNotificationTextsForNative;
|
|
6
|
+
exports.DEFAULT_PROGRESS_INTERVAL = 1000;
|
|
7
|
+
exports.DEFAULT_PROGRESS_MIN_BYTES = 1024 * 1024; // 1MB
|
|
8
|
+
exports.DEFAULT_MAX_PARALLEL_DOWNLOADS = 4;
|
|
9
|
+
exports.DEFAULT_ALLOWS_CELLULAR_ACCESS = true;
|
|
10
|
+
// Lets a background download save its file even while the device is locked
|
|
11
|
+
// (after the first unlock since boot). See the iOS background-download notes in the README.
|
|
12
|
+
exports.DEFAULT_IOS_DATA_PROTECTION = 'completeUntilFirstUserAuthentication';
|
|
13
|
+
// Default notification texts
|
|
14
|
+
exports.DEFAULT_NOTIFICATION_TEXTS = {
|
|
15
|
+
downloadTitle: 'Download',
|
|
16
|
+
downloadStarting: 'Starting download...',
|
|
17
|
+
downloadProgress: 'Downloading... {progress}%',
|
|
18
|
+
downloadPaused: 'Paused',
|
|
19
|
+
downloadFinished: 'Download complete',
|
|
20
|
+
groupTitle: 'Downloads',
|
|
21
|
+
groupText: (count) => `${count} download${count !== 1 ? 's' : ''} in progress`,
|
|
22
|
+
};
|
|
23
|
+
exports.config = {
|
|
24
|
+
headers: {},
|
|
25
|
+
progressInterval: exports.DEFAULT_PROGRESS_INTERVAL,
|
|
26
|
+
progressMinBytes: exports.DEFAULT_PROGRESS_MIN_BYTES,
|
|
27
|
+
isLogsEnabled: false,
|
|
28
|
+
logCallback: undefined,
|
|
29
|
+
maxParallelDownloads: exports.DEFAULT_MAX_PARALLEL_DOWNLOADS,
|
|
30
|
+
allowsCellularAccess: exports.DEFAULT_ALLOWS_CELLULAR_ACCESS,
|
|
31
|
+
showNotificationsEnabled: false,
|
|
32
|
+
iosDataProtection: exports.DEFAULT_IOS_DATA_PROTECTION,
|
|
33
|
+
notificationsGrouping: {
|
|
34
|
+
enabled: false,
|
|
35
|
+
mode: 'individual',
|
|
36
|
+
texts: exports.DEFAULT_NOTIFICATION_TEXTS,
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Get notification text with proper pluralization.
|
|
41
|
+
*/
|
|
42
|
+
function getGroupText(count) {
|
|
43
|
+
var _a, _b;
|
|
44
|
+
const groupText = (_b = (_a = exports.config.notificationsGrouping.texts) === null || _a === void 0 ? void 0 : _a.groupText) !== null && _b !== void 0 ? _b : exports.DEFAULT_NOTIFICATION_TEXTS.groupText;
|
|
45
|
+
if (typeof groupText === 'function')
|
|
46
|
+
return groupText(count);
|
|
47
|
+
return groupText.replace('{count}', String(count));
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Get notification texts config for native side (serializable).
|
|
51
|
+
*/
|
|
52
|
+
function getNotificationTextsForNative() {
|
|
53
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
54
|
+
const texts = (_a = exports.config.notificationsGrouping.texts) !== null && _a !== void 0 ? _a : exports.DEFAULT_NOTIFICATION_TEXTS;
|
|
55
|
+
return {
|
|
56
|
+
downloadTitle: (_b = texts.downloadTitle) !== null && _b !== void 0 ? _b : exports.DEFAULT_NOTIFICATION_TEXTS.downloadTitle,
|
|
57
|
+
downloadStarting: (_c = texts.downloadStarting) !== null && _c !== void 0 ? _c : exports.DEFAULT_NOTIFICATION_TEXTS.downloadStarting,
|
|
58
|
+
downloadProgress: (_d = texts.downloadProgress) !== null && _d !== void 0 ? _d : exports.DEFAULT_NOTIFICATION_TEXTS.downloadProgress,
|
|
59
|
+
downloadPaused: (_e = texts.downloadPaused) !== null && _e !== void 0 ? _e : exports.DEFAULT_NOTIFICATION_TEXTS.downloadPaused,
|
|
60
|
+
downloadFinished: (_f = texts.downloadFinished) !== null && _f !== void 0 ? _f : exports.DEFAULT_NOTIFICATION_TEXTS.downloadFinished,
|
|
61
|
+
groupTitle: (_g = texts.groupTitle) !== null && _g !== void 0 ? _g : exports.DEFAULT_NOTIFICATION_TEXTS.groupTitle,
|
|
62
|
+
// For native side, we send a pattern with {count} placeholder
|
|
63
|
+
groupText: typeof texts.groupText === 'function'
|
|
64
|
+
? '{count} download(s) in progress' // Default pattern if function provided
|
|
65
|
+
: ((_h = texts.groupText) !== null && _h !== void 0 ? _h : '{count} download(s) in progress'),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
const log = (...args) => {
|
|
69
|
+
if (exports.config.isLogsEnabled) {
|
|
70
|
+
console.log('[RNBackgroundDownloader]', ...args);
|
|
71
|
+
// Call external log callback if provided
|
|
72
|
+
if (exports.config.logCallback) {
|
|
73
|
+
const message = args.length > 0 && typeof args[0] === 'string' ? args[0] : 'log';
|
|
74
|
+
const restArgs = args.length > 1 ? args.slice(1) : [];
|
|
75
|
+
exports.config.logCallback('RNBD', message, ...restArgs);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
exports.log = log;
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { DownloadTask } from './DownloadTask';
|
|
2
|
+
import { UploadTask } from './UploadTask';
|
|
3
|
+
import { Config, DownloadParams, TaskInfo, UploadParams, UploadTaskInfo } from './types';
|
|
4
|
+
import type { Spec } from './NativeRNBackgroundDownloader';
|
|
5
|
+
/**
|
|
6
|
+
* Clean up event listeners. Call this before hot reload or module invalidation.
|
|
7
|
+
* This prevents memory leaks from accumulated event listeners.
|
|
8
|
+
*/
|
|
9
|
+
export declare function cleanup(): void;
|
|
10
|
+
export declare function setConfig({ headers, progressInterval, progressMinBytes, isLogsEnabled, logCallback, maxParallelDownloads, allowsCellularAccess, showNotificationsEnabled, notificationsGrouping, iosDataProtection, }: Config): void;
|
|
11
|
+
export declare const getExistingDownloadTasks: () => Promise<DownloadTask[]>;
|
|
12
|
+
export declare const completeHandler: (jobId: string) => Promise<void> | undefined;
|
|
13
|
+
export declare function createDownloadTask({ isAllowedOverRoaming, isAllowedOverMetered, metadata, ...rest }: TaskInfo & DownloadParams): DownloadTask;
|
|
14
|
+
export declare const getExistingUploadTasks: () => Promise<UploadTask[]>;
|
|
15
|
+
export declare function createUploadTask({ isAllowedOverRoaming, isAllowedOverMetered, metadata, ...rest }: UploadTaskInfo & UploadParams): UploadTask;
|
|
16
|
+
export declare const directories: {
|
|
17
|
+
readonly documents: string;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Get the native module instance.
|
|
21
|
+
* This is exported for internal use by DownloadTask to avoid duplicating
|
|
22
|
+
* the TurboModule/NativeModule lookup logic.
|
|
23
|
+
* @internal
|
|
24
|
+
*/
|
|
25
|
+
export declare function getNativeModule(): Spec;
|
|
26
|
+
export type * from './types';
|