format js code with eslint

This commit is contained in:
Kesha Antonov
2021-12-05 21:27:10 +03:00
parent 2ededc9694
commit d157726679
10 changed files with 4404 additions and 2164 deletions

82
.eslintrc.js Normal file
View 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',
},
}

View File

@@ -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,
}); });
}); });
}); });

View File

@@ -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
View File

@@ -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,
}; }

View File

@@ -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)
} }
} }

View File

@@ -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",

View File

@@ -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>
)
} }
} }

View File

@@ -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,
} },
}); })

View File

@@ -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)

5697
yarn.lock

File diff suppressed because it is too large Load Diff