1import AsyncStorage from '@react-native-community/async-storage';
2import { Asset } from 'expo-asset';
3import * as FileSystem from 'expo-file-system';
4import * as Progress from 'expo-progress';
5import React from 'react';
6import { Alert, ScrollView, StyleSheet } from 'react-native';
7
8import ListButton from '../components/ListButton';
9
10interface State {
11  downloadProgress: number;
12}
13
14// See: https://github.com/expo/expo/pull/10229#discussion_r490961694
15// eslint-disable-next-line @typescript-eslint/ban-types
16export default class FileSystemScreen extends React.Component<{}, State> {
17  static navigationOptions = {
18    title: 'FileSystem',
19  };
20
21  readonly state: State = {
22    downloadProgress: 0,
23  };
24
25  download?: FileSystem.DownloadResumable;
26
27  _download = async () => {
28    const url = 'http://ipv4.download.thinkbroadband.com/256KB.zip';
29    await FileSystem.downloadAsync(url, FileSystem.documentDirectory + '256KB.zip');
30    alert('Download complete!');
31  };
32
33  _startDownloading = async () => {
34    const url = 'http://ipv4.download.thinkbroadband.com/5MB.zip';
35    const fileUri = FileSystem.documentDirectory + '5MB.zip';
36    const callback: FileSystem.DownloadProgressCallback = downloadProgress => {
37      const progress =
38        downloadProgress.totalBytesWritten / downloadProgress.totalBytesExpectedToWrite;
39      this.setState({
40        downloadProgress: progress,
41      });
42    };
43    const options = { md5: true };
44    this.download = FileSystem.createDownloadResumable(url, fileUri, options, callback);
45
46    try {
47      const result = await this.download.downloadAsync();
48      if (result) {
49        this._downloadComplete();
50      }
51    } catch (e) {
52      console.log(e);
53    }
54  };
55
56  _pause = async () => {
57    if (!this.download) {
58      alert('Initiate a download first!');
59      return;
60    }
61    try {
62      const downloadSnapshot = await this.download.pauseAsync();
63      await AsyncStorage.setItem('pausedDownload', JSON.stringify(downloadSnapshot));
64      alert('Download paused...');
65    } catch (e) {
66      console.log(e);
67    }
68  };
69
70  _resume = async () => {
71    try {
72      if (this.download) {
73        const result = await this.download.resumeAsync();
74        if (result) {
75          this._downloadComplete();
76        }
77      } else {
78        this._fetchDownload();
79      }
80    } catch (e) {
81      console.log(e);
82    }
83  };
84
85  _downloadComplete = () => {
86    if (this.state.downloadProgress !== 1) {
87      this.setState({
88        downloadProgress: 1,
89      });
90    }
91    alert('Download complete!');
92  };
93
94  _fetchDownload = async () => {
95    try {
96      const downloadJson = await AsyncStorage.getItem('pausedDownload');
97      if (downloadJson !== null) {
98        const downloadFromStore = JSON.parse(downloadJson);
99        const callback: FileSystem.DownloadProgressCallback = downloadProgress => {
100          const progress =
101            downloadProgress.totalBytesWritten / downloadProgress.totalBytesExpectedToWrite;
102          this.setState({
103            downloadProgress: progress,
104          });
105        };
106        this.download = new FileSystem.DownloadResumable(
107          downloadFromStore.url,
108          downloadFromStore.fileUri,
109          downloadFromStore.options,
110          callback,
111          downloadFromStore.resumeData
112        );
113        await this.download.resumeAsync();
114        if (this.state.downloadProgress === 1) {
115          alert('Download complete!');
116        }
117      } else {
118        alert('Initiate a download first!');
119        return;
120      }
121    } catch (e) {
122      console.log(e);
123    }
124  };
125
126  _getInfo = async () => {
127    if (!this.download) {
128      alert('Initiate a download first!');
129      return;
130    }
131    try {
132      const info = await FileSystem.getInfoAsync(this.download._fileUri);
133      Alert.alert('File Info:', JSON.stringify(info), [{ text: 'OK', onPress: () => {} }]);
134    } catch (e) {
135      console.log(e);
136    }
137  };
138
139  _readAsset = async () => {
140    const asset = Asset.fromModule(require('../../assets/index.html'));
141    await asset.downloadAsync();
142    try {
143      const result = await FileSystem.readAsStringAsync(asset.localUri!);
144      Alert.alert('Result', result);
145    } catch (e) {
146      Alert.alert('Error', e.message);
147    }
148  };
149
150  _getInfoAsset = async () => {
151    const asset = Asset.fromModule(require('../../assets/index.html'));
152    await asset.downloadAsync();
153    try {
154      const result = await FileSystem.getInfoAsync(asset.localUri!);
155      Alert.alert('Result', JSON.stringify(result, null, 2));
156    } catch (e) {
157      Alert.alert('Error', e.message);
158    }
159  };
160
161  _copyAndReadAsset = async () => {
162    const asset = Asset.fromModule(require('../../assets/index.html'));
163    await asset.downloadAsync();
164    const tmpFile = FileSystem.cacheDirectory + 'test.html';
165    try {
166      await FileSystem.copyAsync({ from: asset.localUri!, to: tmpFile });
167      const result = await FileSystem.readAsStringAsync(tmpFile);
168      Alert.alert('Result', result);
169    } catch (e) {
170      Alert.alert('Error', e.message);
171    }
172  };
173
174  _alertFreeSpace = async () => {
175    const freeBytes = await FileSystem.getFreeDiskStorageAsync();
176    alert(`${Math.round(freeBytes / 1024 / 1024)} MB available`);
177  };
178
179  render() {
180    return (
181      <ScrollView style={{ padding: 10 }}>
182        <ListButton onPress={this._download} title="Download file (512KB)" />
183        <ListButton onPress={this._startDownloading} title="Start Downloading file (5MB)" />
184        <ListButton onPress={this._pause} title="Pause Download" />
185        <ListButton onPress={this._resume} title="Resume Download" />
186        <ListButton onPress={this._getInfo} title="Get Info" />
187        <Progress.Bar style={styles.progress} isAnimated progress={this.state.downloadProgress} />
188        <ListButton onPress={this._readAsset} title="Read Asset" />
189        <ListButton onPress={this._getInfoAsset} title="Get Info Asset" />
190        <ListButton onPress={this._copyAndReadAsset} title="Copy and Read Asset" />
191        <ListButton onPress={this._alertFreeSpace} title="Alert free space" />
192      </ScrollView>
193    );
194  }
195}
196
197const styles = StyleSheet.create({
198  progress: {
199    marginHorizontal: 10,
200    marginVertical: 32,
201  },
202});
203