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', () => {
|
||||
const mockedHeaders = { Etag: '123' }
|
||||
return new Promise(resolve => {
|
||||
const beginDT = RNBackgroundDownloader.download({
|
||||
id: 'testBegin',
|
||||
url: 'test',
|
||||
destination: 'test'
|
||||
}).begin((expectedBytes) => {
|
||||
}).begin(({ expectedBytes, headers }) => {
|
||||
expect(expectedBytes).toBe(9001);
|
||||
expect(headers).toBe(mockedHeaders);
|
||||
expect(beginDT.state).toBe('DOWNLOADING');
|
||||
resolve();
|
||||
});
|
||||
NativeEventEmitter.listeners.downloadBegin({
|
||||
id: 'testBegin',
|
||||
expectedBytes: 9001
|
||||
expectedBytes: 9001,
|
||||
headers: mockedHeaders,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -172,4 +175,4 @@ test('wrong handler type', () => {
|
||||
expect(() => {
|
||||
dt.error('not function');
|
||||
}).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
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';
|
||||
const { RNBackgroundDownloader } = NativeModules;
|
||||
const RNBackgroundDownloaderEmitter = new NativeEventEmitter(RNBackgroundDownloader);
|
||||
import DownloadTask from './lib/downloadTask';
|
||||
import { NativeModules, NativeEventEmitter } from 'react-native'
|
||||
import DownloadTask from './lib/downloadTask'
|
||||
const { RNBackgroundDownloader } = NativeModules
|
||||
const RNBackgroundDownloaderEmitter = new NativeEventEmitter(RNBackgroundDownloader)
|
||||
|
||||
const tasksMap = new Map();
|
||||
let headers = {};
|
||||
const tasksMap = new Map()
|
||||
let headers = {}
|
||||
|
||||
RNBackgroundDownloaderEmitter.addListener('downloadProgress', events => {
|
||||
for (let event of events) {
|
||||
let task = tasksMap.get(event.id);
|
||||
if (task) {
|
||||
task._onProgress(event.percent, event.written, event.total);
|
||||
}
|
||||
}
|
||||
});
|
||||
for (const event of events) {
|
||||
const task = tasksMap.get(event.id)
|
||||
if (task)
|
||||
task._onProgress(event.percent, event.written, event.total)
|
||||
}
|
||||
})
|
||||
|
||||
RNBackgroundDownloaderEmitter.addListener('downloadComplete', ({ id, location }) => {
|
||||
let task = tasksMap.get(id);
|
||||
if (task) {
|
||||
task._onDone({ location });
|
||||
}
|
||||
tasksMap.delete(id);
|
||||
});
|
||||
const task = tasksMap.get(id)
|
||||
if (task)
|
||||
task._onDone({ location })
|
||||
|
||||
tasksMap.delete(id)
|
||||
})
|
||||
|
||||
RNBackgroundDownloaderEmitter.addListener('downloadFailed', event => {
|
||||
let task = tasksMap.get(event.id);
|
||||
if (task) {
|
||||
task._onError(event.error, event.errorcode);
|
||||
}
|
||||
tasksMap.delete(event.id);
|
||||
});
|
||||
const task = tasksMap.get(event.id)
|
||||
if (task)
|
||||
task._onError(event.error, event.errorcode)
|
||||
|
||||
tasksMap.delete(event.id)
|
||||
})
|
||||
|
||||
RNBackgroundDownloaderEmitter.addListener('downloadBegin', ({ id, expectedBytes, headers }) => {
|
||||
let task = tasksMap.get(id);
|
||||
if (task) {
|
||||
task._onBegin({ expectedBytes, headers });
|
||||
}
|
||||
});
|
||||
const task = tasksMap.get(id)
|
||||
if (task)
|
||||
task._onBegin({ expectedBytes, headers })
|
||||
})
|
||||
|
||||
export function setHeaders(h = {}) {
|
||||
if (typeof h !== 'object') {
|
||||
throw new Error('[RNBackgroundDownloader] headers must be an object');
|
||||
}
|
||||
headers = h;
|
||||
export function setHeaders (h = {}) {
|
||||
if (typeof h !== 'object')
|
||||
throw new Error('[RNBackgroundDownloader] headers must be an object')
|
||||
|
||||
headers = h
|
||||
}
|
||||
|
||||
export function checkForExistingDownloads() {
|
||||
return RNBackgroundDownloader.checkForExistingDownloads()
|
||||
.then(foundTasks => {
|
||||
return foundTasks.map(taskInfo => {
|
||||
let task = new DownloadTask(taskInfo,tasksMap.get(taskInfo.id));
|
||||
if (taskInfo.state === RNBackgroundDownloader.TaskRunning) {
|
||||
task.state = 'DOWNLOADING';
|
||||
} else if (taskInfo.state === RNBackgroundDownloader.TaskSuspended) {
|
||||
task.state = 'PAUSED';
|
||||
} else if (taskInfo.state === RNBackgroundDownloader.TaskCanceling) {
|
||||
task.stop();
|
||||
return null;
|
||||
} else if (taskInfo.state === RNBackgroundDownloader.TaskCompleted) {
|
||||
if (taskInfo.bytesWritten === taskInfo.totalBytes) {
|
||||
task.state = 'DONE';
|
||||
} else {
|
||||
// IOS completed the download but it was not done.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
tasksMap.set(taskInfo.id, task);
|
||||
return task;
|
||||
}).filter(task => task !== null);
|
||||
});
|
||||
export function checkForExistingDownloads () {
|
||||
return RNBackgroundDownloader.checkForExistingDownloads()
|
||||
.then(foundTasks => {
|
||||
return foundTasks.map(taskInfo => {
|
||||
const task = new DownloadTask(taskInfo, tasksMap.get(taskInfo.id))
|
||||
if (taskInfo.state === RNBackgroundDownloader.TaskRunning) {
|
||||
task.state = 'DOWNLOADING'
|
||||
} else if (taskInfo.state === RNBackgroundDownloader.TaskSuspended) {
|
||||
task.state = 'PAUSED'
|
||||
} else if (taskInfo.state === RNBackgroundDownloader.TaskCanceling) {
|
||||
task.stop()
|
||||
return null
|
||||
} else if (taskInfo.state === RNBackgroundDownloader.TaskCompleted) {
|
||||
if (taskInfo.bytesWritten === taskInfo.totalBytes)
|
||||
task.state = 'DONE'
|
||||
else
|
||||
// IOS completed the download but it was not done.
|
||||
return null
|
||||
}
|
||||
tasksMap.set(taskInfo.id, task)
|
||||
return task
|
||||
}).filter(task => task !== null)
|
||||
})
|
||||
}
|
||||
|
||||
export function completeHandler (jobId) {
|
||||
return RNBackgroundDownloader.completeHandler(jobId)
|
||||
}
|
||||
|
||||
export function download(options) {
|
||||
if (!options.id || !options.url || !options.destination) {
|
||||
throw new Error('[RNBackgroundDownloader] id, url and destination are required');
|
||||
export function download (options) {
|
||||
if (!options.id || !options.url || !options.destination)
|
||||
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') {
|
||||
options.headers = {
|
||||
...headers,
|
||||
...options.headers
|
||||
};
|
||||
} else {
|
||||
options.headers = headers;
|
||||
}
|
||||
RNBackgroundDownloader.download(options);
|
||||
let task = new DownloadTask(options.id);
|
||||
tasksMap.set(options.id, task);
|
||||
return task;
|
||||
else
|
||||
options.headers = headers
|
||||
|
||||
RNBackgroundDownloader.download(options)
|
||||
const task = new DownloadTask(options.id)
|
||||
tasksMap.set(options.id, task)
|
||||
return task
|
||||
}
|
||||
|
||||
export const directories = {
|
||||
documents: RNBackgroundDownloader.documents
|
||||
};
|
||||
documents: RNBackgroundDownloader.documents,
|
||||
}
|
||||
|
||||
export const Network = {
|
||||
WIFI_ONLY: RNBackgroundDownloader.OnlyWifi,
|
||||
ALL: RNBackgroundDownloader.AllNetworks
|
||||
};
|
||||
WIFI_ONLY: RNBackgroundDownloader.OnlyWifi,
|
||||
ALL: RNBackgroundDownloader.AllNetworks,
|
||||
}
|
||||
|
||||
export const Priority = {
|
||||
HIGH: RNBackgroundDownloader.PriorityHigh,
|
||||
MEDIUM: RNBackgroundDownloader.PriorityNormal,
|
||||
LOW: RNBackgroundDownloader.PriorityLow
|
||||
};
|
||||
HIGH: RNBackgroundDownloader.PriorityHigh,
|
||||
MEDIUM: RNBackgroundDownloader.PriorityNormal,
|
||||
LOW: RNBackgroundDownloader.PriorityLow,
|
||||
}
|
||||
|
||||
export default {
|
||||
download,
|
||||
checkForExistingDownloads,
|
||||
completeHandler,
|
||||
setHeaders,
|
||||
directories,
|
||||
Network,
|
||||
Priority
|
||||
};
|
||||
download,
|
||||
checkForExistingDownloads,
|
||||
completeHandler,
|
||||
setHeaders,
|
||||
directories,
|
||||
Network,
|
||||
Priority,
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { NativeModules } from 'react-native';
|
||||
const { RNBackgroundDownloader } = NativeModules;
|
||||
import { NativeModules } from 'react-native'
|
||||
const { RNBackgroundDownloader } = NativeModules
|
||||
|
||||
function validateHandler(handler) {
|
||||
if (!(typeof handler === 'function')) {
|
||||
throw new TypeError(`[RNBackgroundDownloader] expected argument to be a function, got: ${typeof handler}`);
|
||||
}
|
||||
function validateHandler (handler) {
|
||||
if (!(typeof handler === 'function'))
|
||||
throw new TypeError(`[RNBackgroundDownloader] expected argument to be a function, got: ${typeof handler}`)
|
||||
}
|
||||
export default class DownloadTask {
|
||||
state = 'PENDING'
|
||||
@@ -13,89 +12,85 @@ export default class DownloadTask {
|
||||
totalBytes = 0
|
||||
|
||||
constructor (taskInfo, originalTask) {
|
||||
if (typeof taskInfo === 'string') {
|
||||
this.id = taskInfo;
|
||||
} else {
|
||||
this.id = taskInfo.id;
|
||||
this.percent = taskInfo.percent;
|
||||
this.bytesWritten = taskInfo.bytesWritten;
|
||||
this.totalBytes = taskInfo.totalBytes;
|
||||
}
|
||||
if (typeof taskInfo === 'string') {
|
||||
this.id = taskInfo
|
||||
} else {
|
||||
this.id = taskInfo.id
|
||||
this.percent = taskInfo.percent
|
||||
this.bytesWritten = taskInfo.bytesWritten
|
||||
this.totalBytes = taskInfo.totalBytes
|
||||
}
|
||||
|
||||
if (originalTask) {
|
||||
this._beginHandler = originalTask._beginHandler
|
||||
this._progressHandler = originalTask._progressHandler
|
||||
this._doneHandler = originalTask._doneHandler
|
||||
this._errorHandler = originalTask._errorHandler
|
||||
}
|
||||
if (originalTask) {
|
||||
this._beginHandler = originalTask._beginHandler
|
||||
this._progressHandler = originalTask._progressHandler
|
||||
this._doneHandler = originalTask._doneHandler
|
||||
this._errorHandler = originalTask._errorHandler
|
||||
}
|
||||
}
|
||||
|
||||
begin (handler) {
|
||||
validateHandler(handler);
|
||||
this._beginHandler = handler;
|
||||
return this;
|
||||
validateHandler(handler)
|
||||
this._beginHandler = handler
|
||||
return this
|
||||
}
|
||||
|
||||
progress (handler) {
|
||||
validateHandler(handler);
|
||||
this._progressHandler = handler;
|
||||
return this;
|
||||
validateHandler(handler)
|
||||
this._progressHandler = handler
|
||||
return this
|
||||
}
|
||||
|
||||
done (handler) {
|
||||
validateHandler(handler);
|
||||
this._doneHandler = handler;
|
||||
return this;
|
||||
validateHandler(handler)
|
||||
this._doneHandler = handler
|
||||
return this
|
||||
}
|
||||
|
||||
error (handler) {
|
||||
validateHandler(handler);
|
||||
this._errorHandler = handler;
|
||||
return this;
|
||||
validateHandler(handler)
|
||||
this._errorHandler = handler
|
||||
return this
|
||||
}
|
||||
|
||||
_onBegin ({ expectedBytes, headers }) {
|
||||
this.state = 'DOWNLOADING';
|
||||
if (this._beginHandler) {
|
||||
this._beginHandler({ expectedBytes, headers });
|
||||
}
|
||||
this.state = 'DOWNLOADING'
|
||||
if (this._beginHandler)
|
||||
this._beginHandler({ expectedBytes, headers })
|
||||
}
|
||||
|
||||
_onProgress (percent, bytesWritten, totalBytes) {
|
||||
this.percent = percent;
|
||||
this.bytesWritten = bytesWritten;
|
||||
this.totalBytes = totalBytes;
|
||||
if (this._progressHandler) {
|
||||
this._progressHandler(percent, bytesWritten, totalBytes);
|
||||
}
|
||||
this.percent = percent
|
||||
this.bytesWritten = bytesWritten
|
||||
this.totalBytes = totalBytes
|
||||
if (this._progressHandler)
|
||||
this._progressHandler(percent, bytesWritten, totalBytes)
|
||||
}
|
||||
|
||||
_onDone ({ location }) {
|
||||
this.state = 'DONE';
|
||||
if (this._doneHandler) {
|
||||
this._doneHandler({ location });
|
||||
}
|
||||
this.state = 'DONE'
|
||||
if (this._doneHandler)
|
||||
this._doneHandler({ location })
|
||||
}
|
||||
|
||||
_onError (error, errorCode) {
|
||||
this.state = 'FAILED';
|
||||
if (this._errorHandler) {
|
||||
this._errorHandler(error, errorCode);
|
||||
}
|
||||
this.state = 'FAILED'
|
||||
if (this._errorHandler)
|
||||
this._errorHandler(error, errorCode)
|
||||
}
|
||||
|
||||
pause () {
|
||||
this.state = 'PAUSED';
|
||||
RNBackgroundDownloader.pauseTask(this.id);
|
||||
this.state = 'PAUSED'
|
||||
RNBackgroundDownloader.pauseTask(this.id)
|
||||
}
|
||||
|
||||
resume () {
|
||||
this.state = 'DOWNLOADING';
|
||||
RNBackgroundDownloader.resumeTask(this.id);
|
||||
this.state = 'DOWNLOADING'
|
||||
RNBackgroundDownloader.resumeTask(this.id)
|
||||
}
|
||||
|
||||
stop () {
|
||||
this.state = 'STOPPED';
|
||||
RNBackgroundDownloader.stopTask(this.id);
|
||||
this.state = 'STOPPED'
|
||||
RNBackgroundDownloader.stopTask(this.id)
|
||||
}
|
||||
}
|
||||
|
||||
21
package.json
21
package.json
@@ -6,7 +6,7 @@
|
||||
"scripts": {
|
||||
"test": "jest",
|
||||
"prepublish": "jest && npm run lint",
|
||||
"lint": "eslint index.js lib/**"
|
||||
"lint": "eslint ."
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -40,15 +40,32 @@
|
||||
}
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"lint-staged": {
|
||||
"*.js": "eslint --cache"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react-native": ">=0.57.0"
|
||||
},
|
||||
"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/runtime": "^7.4.2",
|
||||
"@react-native-community/eslint-config": "^0.0.3",
|
||||
"babel-jest": "^24.5.0",
|
||||
"eslint": "^5.16.0",
|
||||
"lint-staged": ">=12",
|
||||
"immer": "^3.2.0",
|
||||
"jest": "^24.5.0",
|
||||
"metro-react-native-babel-preset": "^0.53.1",
|
||||
|
||||
387
testApp/App.js
387
testApp/App.js
@@ -6,227 +6,226 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { Text, SafeAreaView, TextInput, Button, FlatList, View, AsyncStorage, TouchableOpacity, Slider } from 'react-native';
|
||||
import Icon from 'react-native-vector-icons/Ionicons';
|
||||
import RNFS from 'react-native-fs';
|
||||
import produce from 'immer';
|
||||
import RNBGD from '../index';
|
||||
import styles from './Style';
|
||||
import React, { Component } from 'react'
|
||||
import { Text, SafeAreaView, TextInput, Button, FlatList, View, AsyncStorage, TouchableOpacity, Slider } from 'react-native'
|
||||
import Icon from 'react-native-vector-icons/Ionicons'
|
||||
import RNFS from 'react-native-fs'
|
||||
import produce from 'immer'
|
||||
import RNBGD from '../index'
|
||||
import styles from './Style'
|
||||
|
||||
const testURL = 'https://speed.hetzner.de/100MB.bin';
|
||||
const urlRegex = /^(?:https?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$/;
|
||||
const testURL = 'https://speed.hetzner.de/100MB.bin'
|
||||
const urlRegex = /^(?:https?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$/
|
||||
|
||||
function isValid(url) {
|
||||
return urlRegex.test(url);
|
||||
function isValid (url) {
|
||||
return urlRegex.test(url)
|
||||
}
|
||||
|
||||
export default class App extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.idsToData = {};
|
||||
}
|
||||
constructor (props) {
|
||||
super(props)
|
||||
this.idsToData = {}
|
||||
}
|
||||
|
||||
state = {
|
||||
url: '',
|
||||
status: 'idle',
|
||||
percent: 0,
|
||||
downloads: [],
|
||||
downloadsData: {},
|
||||
url: '',
|
||||
status: 'idle',
|
||||
percent: 0,
|
||||
downloads: [],
|
||||
downloadsData: {},
|
||||
};
|
||||
|
||||
async componentDidMount() {
|
||||
const tasks = await RNBGD.checkForExistingDownloads();
|
||||
if (tasks && tasks.length) {
|
||||
await this.loadDownloads();
|
||||
const downloadsData = {};
|
||||
const downloads = [];
|
||||
for (let task of tasks) {
|
||||
downloads.push(task.id);
|
||||
downloadsData[task.id] = {
|
||||
url: this.idsToData[task.id].url,
|
||||
percent: task.percent,
|
||||
total: task.totalBytes,
|
||||
status: task.state === 'DOWNLOADING' ? 'downloading' : 'paused',
|
||||
task: task
|
||||
};
|
||||
this.attachToTask(task, this.idsToData[task.id].filePath);
|
||||
}
|
||||
this.setState({
|
||||
downloadsData,
|
||||
downloads
|
||||
});
|
||||
async componentDidMount () {
|
||||
const tasks = await RNBGD.checkForExistingDownloads()
|
||||
if (tasks && tasks.length) {
|
||||
await this.loadDownloads()
|
||||
const downloadsData = {}
|
||||
const downloads = []
|
||||
for (const task of tasks) {
|
||||
downloads.push(task.id)
|
||||
downloadsData[task.id] = {
|
||||
url: this.idsToData[task.id].url,
|
||||
percent: task.percent,
|
||||
total: task.totalBytes,
|
||||
status: task.state === 'DOWNLOADING' ? 'downloading' : 'paused',
|
||||
task: task,
|
||||
}
|
||||
this.attachToTask(task, this.idsToData[task.id].filePath)
|
||||
}
|
||||
this.setState({
|
||||
downloadsData,
|
||||
downloads,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
saveDownloads() {
|
||||
AsyncStorage.setItem('idsToData', JSON.stringify(this.idsToData));
|
||||
saveDownloads () {
|
||||
AsyncStorage.setItem('idsToData', JSON.stringify(this.idsToData))
|
||||
}
|
||||
|
||||
async loadDownloads() {
|
||||
const mapStr = await AsyncStorage.getItem('idsToData');
|
||||
try {
|
||||
this.idsToData = JSON.parse(mapStr) || {};
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
async loadDownloads () {
|
||||
const mapStr = await AsyncStorage.getItem('idsToData')
|
||||
try {
|
||||
this.idsToData = JSON.parse(mapStr) || {}
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
pauseOrResume(id) {
|
||||
let newStatus;
|
||||
const download = this.state.downloadsData[id];
|
||||
if (download.status === 'downloading') {
|
||||
download.task.pause();
|
||||
newStatus = 'paused';
|
||||
} else if (download.status === 'paused') {
|
||||
download.task.resume();
|
||||
newStatus = 'downloading';
|
||||
} else {
|
||||
console.error(`Unknown status for play or pause: ${download.status}`);
|
||||
return;
|
||||
}
|
||||
pauseOrResume (id) {
|
||||
let newStatus
|
||||
const download = this.state.downloadsData[id]
|
||||
if (download.status === 'downloading') {
|
||||
download.task.pause()
|
||||
newStatus = 'paused'
|
||||
} else if (download.status === 'paused') {
|
||||
download.task.resume()
|
||||
newStatus = 'downloading'
|
||||
} else {
|
||||
console.error(`Unknown status for play or pause: ${download.status}`)
|
||||
return
|
||||
}
|
||||
|
||||
this.setState(produce(draft => {
|
||||
draft.downloadsData[id].status = newStatus;
|
||||
}));
|
||||
this.setState(produce(draft => {
|
||||
draft.downloadsData[id].status = newStatus
|
||||
}))
|
||||
}
|
||||
|
||||
cancel(id) {
|
||||
const download = this.state.downloadsData[id];
|
||||
download.task.stop();
|
||||
delete this.idsToData[id];
|
||||
this.saveDownloads();
|
||||
this.setState(produce(draft => {
|
||||
delete draft.downloadsData[id];
|
||||
draft.downloads.splice(draft.downloads.indexOf(id), 1);
|
||||
}));
|
||||
cancel (id) {
|
||||
const download = this.state.downloadsData[id]
|
||||
download.task.stop()
|
||||
delete this.idsToData[id]
|
||||
this.saveDownloads()
|
||||
this.setState(produce(draft => {
|
||||
delete draft.downloadsData[id]
|
||||
draft.downloads.splice(draft.downloads.indexOf(id), 1)
|
||||
}))
|
||||
}
|
||||
|
||||
renderRow({ item: downloadId }) {
|
||||
const download = this.state.downloadsData[downloadId];
|
||||
let iconName = 'ios-pause';
|
||||
if (download.status === 'paused') {
|
||||
iconName = 'ios-play';
|
||||
}
|
||||
renderRow ({ item: downloadId }) {
|
||||
const download = this.state.downloadsData[downloadId]
|
||||
let iconName = 'ios-pause'
|
||||
if (download.status === 'paused')
|
||||
iconName = 'ios-play'
|
||||
|
||||
return (
|
||||
<View key={downloadId} style={styles.downloadItem}>
|
||||
<View style={{flex: 1}}>
|
||||
<View>
|
||||
<Text>{downloadId}</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>
|
||||
return (
|
||||
<View key={downloadId} style={styles.downloadItem}>
|
||||
<View style={{ flex: 1 }}>
|
||||
<View>
|
||||
<Text>{downloadId}</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>
|
||||
)
|
||||
}
|
||||
|
||||
attachToTask(task, filePath) {
|
||||
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();
|
||||
|
||||
attachToTask (task, filePath) {
|
||||
task.begin(expectedBytes => {
|
||||
this.setState(produce(draft => {
|
||||
draft.downloadsData[id] = {
|
||||
url: url,
|
||||
status: 'idle',
|
||||
task: task
|
||||
};
|
||||
draft.downloads.push(id);
|
||||
draft.url = '';
|
||||
}));
|
||||
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)
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
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>
|
||||
);
|
||||
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 => {
|
||||
draft.downloadsData[id] = {
|
||||
url: url,
|
||||
status: 'idle',
|
||||
task: task,
|
||||
}
|
||||
draft.downloads.push(id)
|
||||
draft.url = ''
|
||||
}))
|
||||
}
|
||||
|
||||
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({
|
||||
container: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#F5FCFF',
|
||||
paddingHorizontal: 10,
|
||||
},
|
||||
textInput: {
|
||||
height: 70,
|
||||
width: 300,
|
||||
borderColor: 'grey',
|
||||
borderWidth: 1,
|
||||
padding: 10,
|
||||
},
|
||||
downloadingList: {
|
||||
flex: 1,
|
||||
width: '100%'
|
||||
},
|
||||
downloadItem: {
|
||||
height: 100,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingHorizontal: 5,
|
||||
justifyContent: 'space-between'
|
||||
},
|
||||
buttonsContainer: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
button: {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
paddingHorizontal: 10
|
||||
}
|
||||
});
|
||||
container: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#F5FCFF',
|
||||
paddingHorizontal: 10,
|
||||
},
|
||||
textInput: {
|
||||
height: 70,
|
||||
width: 300,
|
||||
borderColor: 'grey',
|
||||
borderWidth: 1,
|
||||
padding: 10,
|
||||
},
|
||||
downloadingList: {
|
||||
flex: 1,
|
||||
width: '100%',
|
||||
},
|
||||
downloadItem: {
|
||||
height: 100,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingHorizontal: 5,
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
buttonsContainer: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
button: {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
paddingHorizontal: 10,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import {AppRegistry} from 'react-native';
|
||||
import App from './App';
|
||||
import {name as appName} from './app.json';
|
||||
import { AppRegistry } from 'react-native'
|
||||
import App from './App'
|
||||
import { name as appName } from './app.json'
|
||||
|
||||
AppRegistry.registerComponent(appName, () => App);
|
||||
AppRegistry.registerComponent(appName, () => App)
|
||||
|
||||
Reference in New Issue
Block a user