mirror of
https://github.com/zoriya/react-native-background-downloader.git
synced 2025-12-06 06:56:10 +00:00
format js code with eslint
This commit is contained in:
82
.eslintrc.js
Normal file
82
.eslintrc.js
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
module.exports = {
|
||||||
|
env: {
|
||||||
|
es2020: true,
|
||||||
|
jest: true,
|
||||||
|
},
|
||||||
|
parser: '@babel/eslint-parser',
|
||||||
|
extends: [
|
||||||
|
'standard',
|
||||||
|
'eslint:recommended',
|
||||||
|
'plugin:react/recommended',
|
||||||
|
'plugin:react-hooks/recommended',
|
||||||
|
],
|
||||||
|
parserOptions: {
|
||||||
|
ecmaFeatures: {
|
||||||
|
jsx: true,
|
||||||
|
},
|
||||||
|
ecmaVersion: 11,
|
||||||
|
sourceType: 'module',
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
'react',
|
||||||
|
'react-hooks',
|
||||||
|
],
|
||||||
|
settings: {
|
||||||
|
react: {
|
||||||
|
version: 'detect',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
indent: [
|
||||||
|
'error',
|
||||||
|
2, {
|
||||||
|
SwitchCase: 1,
|
||||||
|
ignoredNodes: [
|
||||||
|
'TemplateLiteral',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'template-curly-spacing': 'off',
|
||||||
|
'linebreak-style': [
|
||||||
|
'error',
|
||||||
|
'unix',
|
||||||
|
],
|
||||||
|
quotes: [
|
||||||
|
'error',
|
||||||
|
'single',
|
||||||
|
],
|
||||||
|
semi: [
|
||||||
|
'error',
|
||||||
|
'never',
|
||||||
|
],
|
||||||
|
'comma-dangle': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
arrays: 'always-multiline',
|
||||||
|
objects: 'always-multiline',
|
||||||
|
imports: 'always-multiline',
|
||||||
|
exports: 'never',
|
||||||
|
functions: 'never',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'no-func-assign': 'off',
|
||||||
|
'no-class-assign': 'off',
|
||||||
|
'no-useless-escape': 'off',
|
||||||
|
curly: [2, 'multi', 'consistent'],
|
||||||
|
'react/prop-types': 'off', // TODO: TURN ON AND FIX ALL WARNINGS
|
||||||
|
'react/display-name': 'off',
|
||||||
|
},
|
||||||
|
globals: {
|
||||||
|
describe: 'readonly',
|
||||||
|
test: 'readonly',
|
||||||
|
jest: 'readonly',
|
||||||
|
expect: 'readonly',
|
||||||
|
fetch: 'readonly',
|
||||||
|
navigator: 'readonly',
|
||||||
|
__DEV__: 'readonly',
|
||||||
|
XMLHttpRequest: 'readonly',
|
||||||
|
FormData: 'readonly',
|
||||||
|
React$Element: 'readonly',
|
||||||
|
requestAnimationFrame: 'readonly',
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -29,19 +29,22 @@ test('download function', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('begin event', () => {
|
test('begin event', () => {
|
||||||
|
const mockedHeaders = { Etag: '123' }
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const beginDT = RNBackgroundDownloader.download({
|
const beginDT = RNBackgroundDownloader.download({
|
||||||
id: 'testBegin',
|
id: 'testBegin',
|
||||||
url: 'test',
|
url: 'test',
|
||||||
destination: 'test'
|
destination: 'test'
|
||||||
}).begin((expectedBytes) => {
|
}).begin(({ expectedBytes, headers }) => {
|
||||||
expect(expectedBytes).toBe(9001);
|
expect(expectedBytes).toBe(9001);
|
||||||
|
expect(headers).toBe(mockedHeaders);
|
||||||
expect(beginDT.state).toBe('DOWNLOADING');
|
expect(beginDT.state).toBe('DOWNLOADING');
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
NativeEventEmitter.listeners.downloadBegin({
|
NativeEventEmitter.listeners.downloadBegin({
|
||||||
id: 'testBegin',
|
id: 'testBegin',
|
||||||
expectedBytes: 9001
|
expectedBytes: 9001,
|
||||||
|
headers: mockedHeaders,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
presets: ["module:metro-react-native-babel-preset"]
|
presets: ['module:metro-react-native-babel-preset'],
|
||||||
}
|
}
|
||||||
179
index.js
179
index.js
@@ -1,119 +1,116 @@
|
|||||||
import { NativeModules, NativeEventEmitter } from 'react-native';
|
import { NativeModules, NativeEventEmitter } from 'react-native'
|
||||||
const { RNBackgroundDownloader } = NativeModules;
|
import DownloadTask from './lib/downloadTask'
|
||||||
const RNBackgroundDownloaderEmitter = new NativeEventEmitter(RNBackgroundDownloader);
|
const { RNBackgroundDownloader } = NativeModules
|
||||||
import DownloadTask from './lib/downloadTask';
|
const RNBackgroundDownloaderEmitter = new NativeEventEmitter(RNBackgroundDownloader)
|
||||||
|
|
||||||
const tasksMap = new Map();
|
const tasksMap = new Map()
|
||||||
let headers = {};
|
let headers = {}
|
||||||
|
|
||||||
RNBackgroundDownloaderEmitter.addListener('downloadProgress', events => {
|
RNBackgroundDownloaderEmitter.addListener('downloadProgress', events => {
|
||||||
for (let event of events) {
|
for (const event of events) {
|
||||||
let task = tasksMap.get(event.id);
|
const task = tasksMap.get(event.id)
|
||||||
if (task) {
|
if (task)
|
||||||
task._onProgress(event.percent, event.written, event.total);
|
task._onProgress(event.percent, event.written, event.total)
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
});
|
|
||||||
|
|
||||||
RNBackgroundDownloaderEmitter.addListener('downloadComplete', ({ id, location }) => {
|
RNBackgroundDownloaderEmitter.addListener('downloadComplete', ({ id, location }) => {
|
||||||
let task = tasksMap.get(id);
|
const task = tasksMap.get(id)
|
||||||
if (task) {
|
if (task)
|
||||||
task._onDone({ location });
|
task._onDone({ location })
|
||||||
}
|
|
||||||
tasksMap.delete(id);
|
tasksMap.delete(id)
|
||||||
});
|
})
|
||||||
|
|
||||||
RNBackgroundDownloaderEmitter.addListener('downloadFailed', event => {
|
RNBackgroundDownloaderEmitter.addListener('downloadFailed', event => {
|
||||||
let task = tasksMap.get(event.id);
|
const task = tasksMap.get(event.id)
|
||||||
if (task) {
|
if (task)
|
||||||
task._onError(event.error, event.errorcode);
|
task._onError(event.error, event.errorcode)
|
||||||
}
|
|
||||||
tasksMap.delete(event.id);
|
tasksMap.delete(event.id)
|
||||||
});
|
})
|
||||||
|
|
||||||
RNBackgroundDownloaderEmitter.addListener('downloadBegin', ({ id, expectedBytes, headers }) => {
|
RNBackgroundDownloaderEmitter.addListener('downloadBegin', ({ id, expectedBytes, headers }) => {
|
||||||
let task = tasksMap.get(id);
|
const task = tasksMap.get(id)
|
||||||
if (task) {
|
if (task)
|
||||||
task._onBegin({ expectedBytes, headers });
|
task._onBegin({ expectedBytes, headers })
|
||||||
}
|
})
|
||||||
});
|
|
||||||
|
|
||||||
export function setHeaders(h = {}) {
|
export function setHeaders (h = {}) {
|
||||||
if (typeof h !== 'object') {
|
if (typeof h !== 'object')
|
||||||
throw new Error('[RNBackgroundDownloader] headers must be an object');
|
throw new Error('[RNBackgroundDownloader] headers must be an object')
|
||||||
}
|
|
||||||
headers = h;
|
headers = h
|
||||||
}
|
}
|
||||||
|
|
||||||
export function checkForExistingDownloads() {
|
export function checkForExistingDownloads () {
|
||||||
return RNBackgroundDownloader.checkForExistingDownloads()
|
return RNBackgroundDownloader.checkForExistingDownloads()
|
||||||
.then(foundTasks => {
|
.then(foundTasks => {
|
||||||
return foundTasks.map(taskInfo => {
|
return foundTasks.map(taskInfo => {
|
||||||
let task = new DownloadTask(taskInfo,tasksMap.get(taskInfo.id));
|
const task = new DownloadTask(taskInfo, tasksMap.get(taskInfo.id))
|
||||||
if (taskInfo.state === RNBackgroundDownloader.TaskRunning) {
|
if (taskInfo.state === RNBackgroundDownloader.TaskRunning) {
|
||||||
task.state = 'DOWNLOADING';
|
task.state = 'DOWNLOADING'
|
||||||
} else if (taskInfo.state === RNBackgroundDownloader.TaskSuspended) {
|
} else if (taskInfo.state === RNBackgroundDownloader.TaskSuspended) {
|
||||||
task.state = 'PAUSED';
|
task.state = 'PAUSED'
|
||||||
} else if (taskInfo.state === RNBackgroundDownloader.TaskCanceling) {
|
} else if (taskInfo.state === RNBackgroundDownloader.TaskCanceling) {
|
||||||
task.stop();
|
task.stop()
|
||||||
return null;
|
return null
|
||||||
} else if (taskInfo.state === RNBackgroundDownloader.TaskCompleted) {
|
} else if (taskInfo.state === RNBackgroundDownloader.TaskCompleted) {
|
||||||
if (taskInfo.bytesWritten === taskInfo.totalBytes) {
|
if (taskInfo.bytesWritten === taskInfo.totalBytes)
|
||||||
task.state = 'DONE';
|
task.state = 'DONE'
|
||||||
} else {
|
else
|
||||||
// IOS completed the download but it was not done.
|
// IOS completed the download but it was not done.
|
||||||
return null;
|
return null
|
||||||
}
|
}
|
||||||
}
|
tasksMap.set(taskInfo.id, task)
|
||||||
tasksMap.set(taskInfo.id, task);
|
return task
|
||||||
return task;
|
}).filter(task => task !== null)
|
||||||
}).filter(task => task !== null);
|
})
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function completeHandler (jobId) {
|
export function completeHandler (jobId) {
|
||||||
return RNBackgroundDownloader.completeHandler(jobId)
|
return RNBackgroundDownloader.completeHandler(jobId)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function download(options) {
|
export function download (options) {
|
||||||
if (!options.id || !options.url || !options.destination) {
|
if (!options.id || !options.url || !options.destination)
|
||||||
throw new Error('[RNBackgroundDownloader] id, url and destination are required');
|
throw new Error('[RNBackgroundDownloader] id, url and destination are required')
|
||||||
|
|
||||||
|
if (options.headers && typeof options.headers === 'object')
|
||||||
|
options.headers = {
|
||||||
|
...headers,
|
||||||
|
...options.headers,
|
||||||
}
|
}
|
||||||
if (options.headers && typeof options.headers === 'object') {
|
else
|
||||||
options.headers = {
|
options.headers = headers
|
||||||
...headers,
|
|
||||||
...options.headers
|
RNBackgroundDownloader.download(options)
|
||||||
};
|
const task = new DownloadTask(options.id)
|
||||||
} else {
|
tasksMap.set(options.id, task)
|
||||||
options.headers = headers;
|
return task
|
||||||
}
|
|
||||||
RNBackgroundDownloader.download(options);
|
|
||||||
let task = new DownloadTask(options.id);
|
|
||||||
tasksMap.set(options.id, task);
|
|
||||||
return task;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const directories = {
|
export const directories = {
|
||||||
documents: RNBackgroundDownloader.documents
|
documents: RNBackgroundDownloader.documents,
|
||||||
};
|
}
|
||||||
|
|
||||||
export const Network = {
|
export const Network = {
|
||||||
WIFI_ONLY: RNBackgroundDownloader.OnlyWifi,
|
WIFI_ONLY: RNBackgroundDownloader.OnlyWifi,
|
||||||
ALL: RNBackgroundDownloader.AllNetworks
|
ALL: RNBackgroundDownloader.AllNetworks,
|
||||||
};
|
}
|
||||||
|
|
||||||
export const Priority = {
|
export const Priority = {
|
||||||
HIGH: RNBackgroundDownloader.PriorityHigh,
|
HIGH: RNBackgroundDownloader.PriorityHigh,
|
||||||
MEDIUM: RNBackgroundDownloader.PriorityNormal,
|
MEDIUM: RNBackgroundDownloader.PriorityNormal,
|
||||||
LOW: RNBackgroundDownloader.PriorityLow
|
LOW: RNBackgroundDownloader.PriorityLow,
|
||||||
};
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
download,
|
download,
|
||||||
checkForExistingDownloads,
|
checkForExistingDownloads,
|
||||||
completeHandler,
|
completeHandler,
|
||||||
setHeaders,
|
setHeaders,
|
||||||
directories,
|
directories,
|
||||||
Network,
|
Network,
|
||||||
Priority
|
Priority,
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import { NativeModules } from 'react-native';
|
import { NativeModules } from 'react-native'
|
||||||
const { RNBackgroundDownloader } = NativeModules;
|
const { RNBackgroundDownloader } = NativeModules
|
||||||
|
|
||||||
function validateHandler(handler) {
|
function validateHandler (handler) {
|
||||||
if (!(typeof handler === 'function')) {
|
if (!(typeof handler === 'function'))
|
||||||
throw new TypeError(`[RNBackgroundDownloader] expected argument to be a function, got: ${typeof handler}`);
|
throw new TypeError(`[RNBackgroundDownloader] expected argument to be a function, got: ${typeof handler}`)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
export default class DownloadTask {
|
export default class DownloadTask {
|
||||||
state = 'PENDING'
|
state = 'PENDING'
|
||||||
@@ -13,89 +12,85 @@ export default class DownloadTask {
|
|||||||
totalBytes = 0
|
totalBytes = 0
|
||||||
|
|
||||||
constructor (taskInfo, originalTask) {
|
constructor (taskInfo, originalTask) {
|
||||||
if (typeof taskInfo === 'string') {
|
if (typeof taskInfo === 'string') {
|
||||||
this.id = taskInfo;
|
this.id = taskInfo
|
||||||
} else {
|
} else {
|
||||||
this.id = taskInfo.id;
|
this.id = taskInfo.id
|
||||||
this.percent = taskInfo.percent;
|
this.percent = taskInfo.percent
|
||||||
this.bytesWritten = taskInfo.bytesWritten;
|
this.bytesWritten = taskInfo.bytesWritten
|
||||||
this.totalBytes = taskInfo.totalBytes;
|
this.totalBytes = taskInfo.totalBytes
|
||||||
}
|
}
|
||||||
|
|
||||||
if (originalTask) {
|
if (originalTask) {
|
||||||
this._beginHandler = originalTask._beginHandler
|
this._beginHandler = originalTask._beginHandler
|
||||||
this._progressHandler = originalTask._progressHandler
|
this._progressHandler = originalTask._progressHandler
|
||||||
this._doneHandler = originalTask._doneHandler
|
this._doneHandler = originalTask._doneHandler
|
||||||
this._errorHandler = originalTask._errorHandler
|
this._errorHandler = originalTask._errorHandler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
begin (handler) {
|
begin (handler) {
|
||||||
validateHandler(handler);
|
validateHandler(handler)
|
||||||
this._beginHandler = handler;
|
this._beginHandler = handler
|
||||||
return this;
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
progress (handler) {
|
progress (handler) {
|
||||||
validateHandler(handler);
|
validateHandler(handler)
|
||||||
this._progressHandler = handler;
|
this._progressHandler = handler
|
||||||
return this;
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
done (handler) {
|
done (handler) {
|
||||||
validateHandler(handler);
|
validateHandler(handler)
|
||||||
this._doneHandler = handler;
|
this._doneHandler = handler
|
||||||
return this;
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
error (handler) {
|
error (handler) {
|
||||||
validateHandler(handler);
|
validateHandler(handler)
|
||||||
this._errorHandler = handler;
|
this._errorHandler = handler
|
||||||
return this;
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
_onBegin ({ expectedBytes, headers }) {
|
_onBegin ({ expectedBytes, headers }) {
|
||||||
this.state = 'DOWNLOADING';
|
this.state = 'DOWNLOADING'
|
||||||
if (this._beginHandler) {
|
if (this._beginHandler)
|
||||||
this._beginHandler({ expectedBytes, headers });
|
this._beginHandler({ expectedBytes, headers })
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_onProgress (percent, bytesWritten, totalBytes) {
|
_onProgress (percent, bytesWritten, totalBytes) {
|
||||||
this.percent = percent;
|
this.percent = percent
|
||||||
this.bytesWritten = bytesWritten;
|
this.bytesWritten = bytesWritten
|
||||||
this.totalBytes = totalBytes;
|
this.totalBytes = totalBytes
|
||||||
if (this._progressHandler) {
|
if (this._progressHandler)
|
||||||
this._progressHandler(percent, bytesWritten, totalBytes);
|
this._progressHandler(percent, bytesWritten, totalBytes)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_onDone ({ location }) {
|
_onDone ({ location }) {
|
||||||
this.state = 'DONE';
|
this.state = 'DONE'
|
||||||
if (this._doneHandler) {
|
if (this._doneHandler)
|
||||||
this._doneHandler({ location });
|
this._doneHandler({ location })
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_onError (error, errorCode) {
|
_onError (error, errorCode) {
|
||||||
this.state = 'FAILED';
|
this.state = 'FAILED'
|
||||||
if (this._errorHandler) {
|
if (this._errorHandler)
|
||||||
this._errorHandler(error, errorCode);
|
this._errorHandler(error, errorCode)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pause () {
|
pause () {
|
||||||
this.state = 'PAUSED';
|
this.state = 'PAUSED'
|
||||||
RNBackgroundDownloader.pauseTask(this.id);
|
RNBackgroundDownloader.pauseTask(this.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
resume () {
|
resume () {
|
||||||
this.state = 'DOWNLOADING';
|
this.state = 'DOWNLOADING'
|
||||||
RNBackgroundDownloader.resumeTask(this.id);
|
RNBackgroundDownloader.resumeTask(this.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
stop () {
|
stop () {
|
||||||
this.state = 'STOPPED';
|
this.state = 'STOPPED'
|
||||||
RNBackgroundDownloader.stopTask(this.id);
|
RNBackgroundDownloader.stopTask(this.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
21
package.json
21
package.json
@@ -6,7 +6,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"prepublish": "jest && npm run lint",
|
"prepublish": "jest && npm run lint",
|
||||||
"lint": "eslint index.js lib/**"
|
"lint": "eslint ."
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -40,15 +40,32 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"lint-staged": {
|
||||||
|
"*.js": "eslint --cache"
|
||||||
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react-native": ">=0.57.0"
|
"react-native": ">=0.57.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.16.0",
|
||||||
|
"@babel/preset-env": "^7.16.4",
|
||||||
|
"@babel/runtime": "^7.16.3",
|
||||||
|
"@babel/eslint-parser": "^7.16.3",
|
||||||
|
"@react-native-community/eslint-config": "^3.0.1",
|
||||||
|
"eslint": "7.32.0",
|
||||||
|
"eslint-config-standard": "^16.0.3",
|
||||||
|
"eslint-config-standard-jsx": "^10.0.0",
|
||||||
|
"eslint-plugin-import": "^2.25.3",
|
||||||
|
"eslint-plugin-node": "^11.1.0",
|
||||||
|
"eslint-plugin-promise": "^5.1.1",
|
||||||
|
"eslint-plugin-react": "^7.27.1",
|
||||||
|
"eslint-plugin-react-hooks": "^4.3.0",
|
||||||
|
"eslint-plugin-standard": "^5.0.0",
|
||||||
"@babel/core": "^7.4.0",
|
"@babel/core": "^7.4.0",
|
||||||
"@babel/runtime": "^7.4.2",
|
"@babel/runtime": "^7.4.2",
|
||||||
"@react-native-community/eslint-config": "^0.0.3",
|
"@react-native-community/eslint-config": "^0.0.3",
|
||||||
"babel-jest": "^24.5.0",
|
"babel-jest": "^24.5.0",
|
||||||
"eslint": "^5.16.0",
|
"lint-staged": ">=12",
|
||||||
"immer": "^3.2.0",
|
"immer": "^3.2.0",
|
||||||
"jest": "^24.5.0",
|
"jest": "^24.5.0",
|
||||||
"metro-react-native-babel-preset": "^0.53.1",
|
"metro-react-native-babel-preset": "^0.53.1",
|
||||||
|
|||||||
387
testApp/App.js
387
testApp/App.js
@@ -6,227 +6,226 @@
|
|||||||
* @flow
|
* @flow
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react'
|
||||||
import { Text, SafeAreaView, TextInput, Button, FlatList, View, AsyncStorage, TouchableOpacity, Slider } from 'react-native';
|
import { Text, SafeAreaView, TextInput, Button, FlatList, View, AsyncStorage, TouchableOpacity, Slider } from 'react-native'
|
||||||
import Icon from 'react-native-vector-icons/Ionicons';
|
import Icon from 'react-native-vector-icons/Ionicons'
|
||||||
import RNFS from 'react-native-fs';
|
import RNFS from 'react-native-fs'
|
||||||
import produce from 'immer';
|
import produce from 'immer'
|
||||||
import RNBGD from '../index';
|
import RNBGD from '../index'
|
||||||
import styles from './Style';
|
import styles from './Style'
|
||||||
|
|
||||||
const testURL = 'https://speed.hetzner.de/100MB.bin';
|
const testURL = 'https://speed.hetzner.de/100MB.bin'
|
||||||
const urlRegex = /^(?:https?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$/;
|
const urlRegex = /^(?:https?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$/
|
||||||
|
|
||||||
function isValid(url) {
|
function isValid (url) {
|
||||||
return urlRegex.test(url);
|
return urlRegex.test(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class App extends Component {
|
export default class App extends Component {
|
||||||
constructor(props) {
|
constructor (props) {
|
||||||
super(props);
|
super(props)
|
||||||
this.idsToData = {};
|
this.idsToData = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
url: '',
|
url: '',
|
||||||
status: 'idle',
|
status: 'idle',
|
||||||
percent: 0,
|
percent: 0,
|
||||||
downloads: [],
|
downloads: [],
|
||||||
downloadsData: {},
|
downloadsData: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount () {
|
||||||
const tasks = await RNBGD.checkForExistingDownloads();
|
const tasks = await RNBGD.checkForExistingDownloads()
|
||||||
if (tasks && tasks.length) {
|
if (tasks && tasks.length) {
|
||||||
await this.loadDownloads();
|
await this.loadDownloads()
|
||||||
const downloadsData = {};
|
const downloadsData = {}
|
||||||
const downloads = [];
|
const downloads = []
|
||||||
for (let task of tasks) {
|
for (const task of tasks) {
|
||||||
downloads.push(task.id);
|
downloads.push(task.id)
|
||||||
downloadsData[task.id] = {
|
downloadsData[task.id] = {
|
||||||
url: this.idsToData[task.id].url,
|
url: this.idsToData[task.id].url,
|
||||||
percent: task.percent,
|
percent: task.percent,
|
||||||
total: task.totalBytes,
|
total: task.totalBytes,
|
||||||
status: task.state === 'DOWNLOADING' ? 'downloading' : 'paused',
|
status: task.state === 'DOWNLOADING' ? 'downloading' : 'paused',
|
||||||
task: task
|
task: task,
|
||||||
};
|
}
|
||||||
this.attachToTask(task, this.idsToData[task.id].filePath);
|
this.attachToTask(task, this.idsToData[task.id].filePath)
|
||||||
}
|
|
||||||
this.setState({
|
|
||||||
downloadsData,
|
|
||||||
downloads
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
this.setState({
|
||||||
|
downloadsData,
|
||||||
|
downloads,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
saveDownloads() {
|
saveDownloads () {
|
||||||
AsyncStorage.setItem('idsToData', JSON.stringify(this.idsToData));
|
AsyncStorage.setItem('idsToData', JSON.stringify(this.idsToData))
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadDownloads() {
|
async loadDownloads () {
|
||||||
const mapStr = await AsyncStorage.getItem('idsToData');
|
const mapStr = await AsyncStorage.getItem('idsToData')
|
||||||
try {
|
try {
|
||||||
this.idsToData = JSON.parse(mapStr) || {};
|
this.idsToData = JSON.parse(mapStr) || {}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pauseOrResume(id) {
|
pauseOrResume (id) {
|
||||||
let newStatus;
|
let newStatus
|
||||||
const download = this.state.downloadsData[id];
|
const download = this.state.downloadsData[id]
|
||||||
if (download.status === 'downloading') {
|
if (download.status === 'downloading') {
|
||||||
download.task.pause();
|
download.task.pause()
|
||||||
newStatus = 'paused';
|
newStatus = 'paused'
|
||||||
} else if (download.status === 'paused') {
|
} else if (download.status === 'paused') {
|
||||||
download.task.resume();
|
download.task.resume()
|
||||||
newStatus = 'downloading';
|
newStatus = 'downloading'
|
||||||
} else {
|
} else {
|
||||||
console.error(`Unknown status for play or pause: ${download.status}`);
|
console.error(`Unknown status for play or pause: ${download.status}`)
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState(produce(draft => {
|
this.setState(produce(draft => {
|
||||||
draft.downloadsData[id].status = newStatus;
|
draft.downloadsData[id].status = newStatus
|
||||||
}));
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel(id) {
|
cancel (id) {
|
||||||
const download = this.state.downloadsData[id];
|
const download = this.state.downloadsData[id]
|
||||||
download.task.stop();
|
download.task.stop()
|
||||||
delete this.idsToData[id];
|
delete this.idsToData[id]
|
||||||
this.saveDownloads();
|
this.saveDownloads()
|
||||||
this.setState(produce(draft => {
|
this.setState(produce(draft => {
|
||||||
delete draft.downloadsData[id];
|
delete draft.downloadsData[id]
|
||||||
draft.downloads.splice(draft.downloads.indexOf(id), 1);
|
draft.downloads.splice(draft.downloads.indexOf(id), 1)
|
||||||
}));
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
renderRow({ item: downloadId }) {
|
renderRow ({ item: downloadId }) {
|
||||||
const download = this.state.downloadsData[downloadId];
|
const download = this.state.downloadsData[downloadId]
|
||||||
let iconName = 'ios-pause';
|
let iconName = 'ios-pause'
|
||||||
if (download.status === 'paused') {
|
if (download.status === 'paused')
|
||||||
iconName = 'ios-play';
|
iconName = 'ios-play'
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View key={downloadId} style={styles.downloadItem}>
|
<View key={downloadId} style={styles.downloadItem}>
|
||||||
<View style={{flex: 1}}>
|
<View style={{ flex: 1 }}>
|
||||||
<View>
|
<View>
|
||||||
<Text>{downloadId}</Text>
|
<Text>{downloadId}</Text>
|
||||||
<Text>{download.url}</Text>
|
<Text>{download.url}</Text>
|
||||||
</View>
|
|
||||||
<Slider
|
|
||||||
value={download.percent}
|
|
||||||
disabled
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
<View style={styles.buttonsContainer}>
|
|
||||||
<TouchableOpacity style={styles.button} onPress={() => this.pauseOrResume(downloadId)}>
|
|
||||||
<Icon name={iconName} size={26}/>
|
|
||||||
</TouchableOpacity>
|
|
||||||
<TouchableOpacity style={styles.button} onPress={() => this.cancel(downloadId)}>
|
|
||||||
<Icon name="ios-close" size={40}/>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
</View>
|
</View>
|
||||||
);
|
<Slider
|
||||||
|
value={download.percent}
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View style={styles.buttonsContainer}>
|
||||||
|
<TouchableOpacity style={styles.button} onPress={() => this.pauseOrResume(downloadId)}>
|
||||||
|
<Icon name={iconName} size={26}/>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<TouchableOpacity style={styles.button} onPress={() => this.cancel(downloadId)}>
|
||||||
|
<Icon name="ios-close" size={40}/>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
attachToTask(task, filePath) {
|
attachToTask (task, filePath) {
|
||||||
task.begin(expectedBytes => {
|
task.begin(expectedBytes => {
|
||||||
this.setState(produce(draft => {
|
|
||||||
draft.downloadsData[task.id].total = expectedBytes;
|
|
||||||
draft.downloadsData[task.id].status = 'downloading';
|
|
||||||
}));
|
|
||||||
})
|
|
||||||
.progress(percent => {
|
|
||||||
this.setState(produce(draft => {
|
|
||||||
draft.downloadsData[task.id].percent = percent;
|
|
||||||
}));
|
|
||||||
})
|
|
||||||
.done(async() => {
|
|
||||||
try {
|
|
||||||
console.log(`Finished downloading: ${task.id}, deleting it...`);
|
|
||||||
await RNFS.unlink(filePath);
|
|
||||||
console.log(`Deleted ${task.id}`);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
delete this.idsToData[task.id];
|
|
||||||
this.saveDownloads();
|
|
||||||
this.setState(produce(draft => {
|
|
||||||
delete draft.downloadsData[task.id];
|
|
||||||
draft.downloads.splice(draft.downloads.indexOf(task.id), 1);
|
|
||||||
}));
|
|
||||||
})
|
|
||||||
.error(err => {
|
|
||||||
console.error(`Download ${task.id} has an error: ${err}`);
|
|
||||||
delete this.idsToData[task.id];
|
|
||||||
this.saveDownloads();
|
|
||||||
this.setState(produce(draft => {
|
|
||||||
delete draft.downloadsData[task.id];
|
|
||||||
draft.downloads.splice(draft.downloads.indexOf(task.id), 1);
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
addDownload() {
|
|
||||||
const id = Math.random()
|
|
||||||
.toString(36)
|
|
||||||
.substr(2, 6);
|
|
||||||
const filePath = `${RNBGD.directories.documents}/${id}`;
|
|
||||||
const url = this.state.url || testURL;
|
|
||||||
const task = RNBGD.download({
|
|
||||||
id: id,
|
|
||||||
url: url,
|
|
||||||
destination: filePath,
|
|
||||||
});
|
|
||||||
this.attachToTask(task, filePath);
|
|
||||||
this.idsToData[id] = {
|
|
||||||
url,
|
|
||||||
filePath
|
|
||||||
};
|
|
||||||
this.saveDownloads();
|
|
||||||
|
|
||||||
this.setState(produce(draft => {
|
this.setState(produce(draft => {
|
||||||
draft.downloadsData[id] = {
|
draft.downloadsData[task.id].total = expectedBytes
|
||||||
url: url,
|
draft.downloadsData[task.id].status = 'downloading'
|
||||||
status: 'idle',
|
}))
|
||||||
task: task
|
})
|
||||||
};
|
.progress(percent => {
|
||||||
draft.downloads.push(id);
|
this.setState(produce(draft => {
|
||||||
draft.url = '';
|
draft.downloadsData[task.id].percent = percent
|
||||||
}));
|
}))
|
||||||
|
})
|
||||||
|
.done(async () => {
|
||||||
|
try {
|
||||||
|
console.log(`Finished downloading: ${task.id}, deleting it...`)
|
||||||
|
await RNFS.unlink(filePath)
|
||||||
|
console.log(`Deleted ${task.id}`)
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
delete this.idsToData[task.id]
|
||||||
|
this.saveDownloads()
|
||||||
|
this.setState(produce(draft => {
|
||||||
|
delete draft.downloadsData[task.id]
|
||||||
|
draft.downloads.splice(draft.downloads.indexOf(task.id), 1)
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
.error(err => {
|
||||||
|
console.error(`Download ${task.id} has an error: ${err}`)
|
||||||
|
delete this.idsToData[task.id]
|
||||||
|
this.saveDownloads()
|
||||||
|
this.setState(produce(draft => {
|
||||||
|
delete draft.downloadsData[task.id]
|
||||||
|
draft.downloads.splice(draft.downloads.indexOf(task.id), 1)
|
||||||
|
}))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
addDownload () {
|
||||||
return (
|
const id = Math.random()
|
||||||
<SafeAreaView style={styles.container}>
|
.toString(36)
|
||||||
<TextInput
|
.substr(2, 6)
|
||||||
style={styles.textInput}
|
const filePath = `${RNBGD.directories.documents}/${id}`
|
||||||
textContentType="none"
|
const url = this.state.url || testURL
|
||||||
autoCorrect={false}
|
const task = RNBGD.download({
|
||||||
multiline={true}
|
id: id,
|
||||||
keyboardType="url"
|
url: url,
|
||||||
placeholder={testURL}
|
destination: filePath,
|
||||||
onChangeText={(text) => {
|
})
|
||||||
this.setState({ url: text.toLowerCase() });
|
this.attachToTask(task, filePath)
|
||||||
}}
|
this.idsToData[id] = {
|
||||||
value={this.state.url}
|
url,
|
||||||
/>
|
filePath,
|
||||||
<Button
|
}
|
||||||
title="Add Download"
|
this.saveDownloads()
|
||||||
onPress={this.addDownload.bind(this)}
|
|
||||||
disabled={this.state.url !== '' && !isValid(this.state.url)}
|
this.setState(produce(draft => {
|
||||||
/>
|
draft.downloadsData[id] = {
|
||||||
<FlatList
|
url: url,
|
||||||
style={styles.downloadingList}
|
status: 'idle',
|
||||||
data={this.state.downloads}
|
task: task,
|
||||||
renderItem={this.renderRow.bind(this)}
|
}
|
||||||
extraData={this.state.downloadsData}
|
draft.downloads.push(id)
|
||||||
/>
|
draft.url = ''
|
||||||
</SafeAreaView>
|
}))
|
||||||
);
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<SafeAreaView style={styles.container}>
|
||||||
|
<TextInput
|
||||||
|
style={styles.textInput}
|
||||||
|
textContentType="none"
|
||||||
|
autoCorrect={false}
|
||||||
|
multiline={true}
|
||||||
|
keyboardType="url"
|
||||||
|
placeholder={testURL}
|
||||||
|
onChangeText={(text) => {
|
||||||
|
this.setState({ url: text.toLowerCase() })
|
||||||
|
}}
|
||||||
|
value={this.state.url}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
title="Add Download"
|
||||||
|
onPress={this.addDownload.bind(this)}
|
||||||
|
disabled={this.state.url !== '' && !isValid(this.state.url)}
|
||||||
|
/>
|
||||||
|
<FlatList
|
||||||
|
style={styles.downloadingList}
|
||||||
|
data={this.state.downloads}
|
||||||
|
renderItem={this.renderRow.bind(this)}
|
||||||
|
extraData={this.state.downloadsData}
|
||||||
|
/>
|
||||||
|
</SafeAreaView>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,39 +1,39 @@
|
|||||||
import { StyleSheet } from 'react-native';
|
import { StyleSheet } from 'react-native'
|
||||||
|
|
||||||
export default StyleSheet.create({
|
export default StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
backgroundColor: '#F5FCFF',
|
backgroundColor: '#F5FCFF',
|
||||||
paddingHorizontal: 10,
|
paddingHorizontal: 10,
|
||||||
},
|
},
|
||||||
textInput: {
|
textInput: {
|
||||||
height: 70,
|
height: 70,
|
||||||
width: 300,
|
width: 300,
|
||||||
borderColor: 'grey',
|
borderColor: 'grey',
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
padding: 10,
|
padding: 10,
|
||||||
},
|
},
|
||||||
downloadingList: {
|
downloadingList: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
width: '100%'
|
width: '100%',
|
||||||
},
|
},
|
||||||
downloadItem: {
|
downloadItem: {
|
||||||
height: 100,
|
height: 100,
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
paddingHorizontal: 5,
|
paddingHorizontal: 5,
|
||||||
justifyContent: 'space-between'
|
justifyContent: 'space-between',
|
||||||
},
|
},
|
||||||
buttonsContainer: {
|
buttonsContainer: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
},
|
},
|
||||||
button: {
|
button: {
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
paddingHorizontal: 10
|
paddingHorizontal: 10,
|
||||||
}
|
},
|
||||||
});
|
})
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AppRegistry} from 'react-native';
|
import { AppRegistry } from 'react-native'
|
||||||
import App from './App';
|
import App from './App'
|
||||||
import {name as appName} from './app.json';
|
import { name as appName } from './app.json'
|
||||||
|
|
||||||
AppRegistry.registerComponent(appName, () => App);
|
AppRegistry.registerComponent(appName, () => App)
|
||||||
|
|||||||
Reference in New Issue
Block a user