1 | import { UnavailabilityError } from '@unimodules/core';
|
2 | import { EventEmitter, Subscription } from '@unimodules/core';
|
3 | import UUID from 'uuid-js';
|
4 | import ExponentFileSystem from './ExponentFileSystem';
|
5 | import { Platform } from 'react-native';
|
6 |
|
7 | import {
|
8 | DownloadOptions,
|
9 | DownloadResult,
|
10 | DownloadProgressCallback,
|
11 | DownloadProgressData,
|
12 | DownloadPauseState,
|
13 | FileInfo,
|
14 | EncodingType,
|
15 | ReadingOptions,
|
16 | WritingOptions,
|
17 | ProgressEvent,
|
18 | } from './FileSystem.types';
|
19 |
|
20 | if (!ExponentFileSystem) {
|
21 | console.warn(
|
22 | "No native ExponentFileSystem module found, are you sure the expo-file-system's module is linked properly?"
|
23 | );
|
24 | }
|
25 |
|
26 | const _unused = new EventEmitter(ExponentFileSystem);
|
27 |
|
28 | export {
|
29 | DownloadOptions,
|
30 | DownloadResult,
|
31 | DownloadProgressCallback,
|
32 | DownloadProgressData,
|
33 | DownloadPauseState,
|
34 | FileInfo,
|
35 | EncodingType,
|
36 | ReadingOptions,
|
37 | WritingOptions,
|
38 | ProgressEvent,
|
39 | };
|
40 |
|
41 | function normalizeEndingSlash(p: string | null): string | null {
|
42 | if (p != null) {
|
43 | return p.replace(/\/*$/, '') + '/';
|
44 | }
|
45 | return null;
|
46 | }
|
47 |
|
48 | export const documentDirectory = normalizeEndingSlash(ExponentFileSystem.documentDirectory);
|
49 | export const cacheDirectory = normalizeEndingSlash(ExponentFileSystem.cacheDirectory);
|
50 |
|
51 | export const { bundledAssets, bundleDirectory } = ExponentFileSystem;
|
52 |
|
53 | export async function getInfoAsync(
|
54 | fileUri: string,
|
55 | options: { md5?: boolean; size?: boolean } = {}
|
56 | ): Promise<FileInfo> {
|
57 | if (!ExponentFileSystem.getInfoAsync) {
|
58 | throw new UnavailabilityError('expo-file-system', 'getInfoAsync');
|
59 | }
|
60 | return await ExponentFileSystem.getInfoAsync(fileUri, options);
|
61 | }
|
62 |
|
63 | export async function readAsStringAsync(
|
64 | fileUri: string,
|
65 | options?: ReadingOptions
|
66 | ): Promise<string> {
|
67 | if (!ExponentFileSystem.readAsStringAsync) {
|
68 | throw new UnavailabilityError('expo-file-system', 'readAsStringAsync');
|
69 | }
|
70 | return await ExponentFileSystem.readAsStringAsync(fileUri, options || {});
|
71 | }
|
72 |
|
73 | export async function getContentUriAsync(fileUri: string): Promise<string> {
|
74 | if (Platform.OS === 'android') {
|
75 | if (!ExponentFileSystem.getContentUriAsync) {
|
76 | throw new UnavailabilityError('expo-file-system', 'getContentUriAsync');
|
77 | }
|
78 | return await ExponentFileSystem.getContentUriAsync(fileUri);
|
79 | } else {
|
80 | return new Promise(function(resolve, reject) {
|
81 | resolve(fileUri);
|
82 | });
|
83 | }
|
84 | }
|
85 |
|
86 | export async function writeAsStringAsync(
|
87 | fileUri: string,
|
88 | contents: string,
|
89 | options: WritingOptions = {}
|
90 | ): Promise<void> {
|
91 | if (!ExponentFileSystem.writeAsStringAsync) {
|
92 | throw new UnavailabilityError('expo-file-system', 'writeAsStringAsync');
|
93 | }
|
94 | return await ExponentFileSystem.writeAsStringAsync(fileUri, contents, options);
|
95 | }
|
96 |
|
97 | export async function deleteAsync(
|
98 | fileUri: string,
|
99 | options: { idempotent?: boolean } = {}
|
100 | ): Promise<void> {
|
101 | if (!ExponentFileSystem.deleteAsync) {
|
102 | throw new UnavailabilityError('expo-file-system', 'deleteAsync');
|
103 | }
|
104 | return await ExponentFileSystem.deleteAsync(fileUri, options);
|
105 | }
|
106 |
|
107 | export async function moveAsync(options: { from: string; to: string }): Promise<void> {
|
108 | if (!ExponentFileSystem.moveAsync) {
|
109 | throw new UnavailabilityError('expo-file-system', 'moveAsync');
|
110 | }
|
111 | return await ExponentFileSystem.moveAsync(options);
|
112 | }
|
113 |
|
114 | export async function copyAsync(options: { from: string; to: string }): Promise<void> {
|
115 | if (!ExponentFileSystem.copyAsync) {
|
116 | throw new UnavailabilityError('expo-file-system', 'copyAsync');
|
117 | }
|
118 | return await ExponentFileSystem.copyAsync(options);
|
119 | }
|
120 |
|
121 | export async function makeDirectoryAsync(
|
122 | fileUri: string,
|
123 | options: { intermediates?: boolean } = {}
|
124 | ): Promise<void> {
|
125 | if (!ExponentFileSystem.makeDirectoryAsync) {
|
126 | throw new UnavailabilityError('expo-file-system', 'makeDirectoryAsync');
|
127 | }
|
128 | return await ExponentFileSystem.makeDirectoryAsync(fileUri, options);
|
129 | }
|
130 |
|
131 | export async function readDirectoryAsync(fileUri: string): Promise<string[]> {
|
132 | if (!ExponentFileSystem.readDirectoryAsync) {
|
133 | throw new UnavailabilityError('expo-file-system', 'readDirectoryAsync');
|
134 | }
|
135 | return await ExponentFileSystem.readDirectoryAsync(fileUri, {});
|
136 | }
|
137 |
|
138 | export async function getFreeDiskStorageAsync(): Promise<number> {
|
139 | if (!ExponentFileSystem.getFreeDiskStorageAsync) {
|
140 | throw new UnavailabilityError('expo-file-system', 'getFreeDiskStorageAsync');
|
141 | }
|
142 | return await ExponentFileSystem.getFreeDiskStorageAsync();
|
143 | }
|
144 |
|
145 | export async function getTotalDiskCapacityAsync(): Promise<number> {
|
146 | if (!ExponentFileSystem.getTotalDiskCapacityAsync) {
|
147 | throw new UnavailabilityError('expo-file-system', 'getTotalDiskCapacityAsync');
|
148 | }
|
149 | return await ExponentFileSystem.getTotalDiskCapacityAsync();
|
150 | }
|
151 |
|
152 | export async function downloadAsync(
|
153 | uri: string,
|
154 | fileUri: string,
|
155 | options: DownloadOptions = {}
|
156 | ): Promise<DownloadResult> {
|
157 | if (!ExponentFileSystem.downloadAsync) {
|
158 | throw new UnavailabilityError('expo-file-system', 'downloadAsync');
|
159 | }
|
160 | return await ExponentFileSystem.downloadAsync(uri, fileUri, options);
|
161 | }
|
162 |
|
163 | export function createDownloadResumable(
|
164 | uri: string,
|
165 | fileUri: string,
|
166 | options?: DownloadOptions,
|
167 | callback?: DownloadProgressCallback,
|
168 | resumeData?: string
|
169 | ): DownloadResumable {
|
170 | return new DownloadResumable(uri, fileUri, options, callback, resumeData);
|
171 | }
|
172 |
|
173 | export class DownloadResumable {
|
174 | _uuid: string;
|
175 | _url: string;
|
176 | _fileUri: string;
|
177 | _options: DownloadOptions;
|
178 | _resumeData?: string;
|
179 | _callback?: DownloadProgressCallback;
|
180 | _subscription?: Subscription | null;
|
181 | _emitter: EventEmitter;
|
182 |
|
183 | constructor(
|
184 | url: string,
|
185 | fileUri: string,
|
186 | options: DownloadOptions = {},
|
187 | callback?: DownloadProgressCallback,
|
188 | resumeData?: string
|
189 | ) {
|
190 | this._uuid = UUID.create(4).toString();
|
191 | this._url = url;
|
192 | this._fileUri = fileUri;
|
193 | this._options = options;
|
194 | this._resumeData = resumeData;
|
195 | this._callback = callback;
|
196 | this._subscription = null;
|
197 | this._emitter = new EventEmitter(ExponentFileSystem);
|
198 | }
|
199 |
|
200 | async downloadAsync(): Promise<DownloadResult | undefined> {
|
201 | if (!ExponentFileSystem.downloadResumableStartAsync) {
|
202 | throw new UnavailabilityError('expo-file-system', 'downloadResumableStartAsync');
|
203 | }
|
204 | this._addSubscription();
|
205 | return await ExponentFileSystem.downloadResumableStartAsync(
|
206 | this._url,
|
207 | this._fileUri,
|
208 | this._uuid,
|
209 | this._options,
|
210 | this._resumeData
|
211 | );
|
212 | }
|
213 |
|
214 | async pauseAsync(): Promise<DownloadPauseState> {
|
215 | if (!ExponentFileSystem.downloadResumablePauseAsync) {
|
216 | throw new UnavailabilityError('expo-file-system', 'downloadResumablePauseAsync');
|
217 | }
|
218 | const pauseResult = await ExponentFileSystem.downloadResumablePauseAsync(this._uuid);
|
219 | this._removeSubscription();
|
220 | if (pauseResult) {
|
221 | this._resumeData = pauseResult.resumeData;
|
222 | return this.savable();
|
223 | } else {
|
224 | throw new Error('Unable to generate a savable pause state');
|
225 | }
|
226 | }
|
227 |
|
228 | async resumeAsync(): Promise<DownloadResult | undefined> {
|
229 | if (!ExponentFileSystem.downloadResumableStartAsync) {
|
230 | throw new UnavailabilityError('expo-file-system', 'downloadResumableStartAsync');
|
231 | }
|
232 | this._addSubscription();
|
233 | return await ExponentFileSystem.downloadResumableStartAsync(
|
234 | this._url,
|
235 | this._fileUri,
|
236 | this._uuid,
|
237 | this._options,
|
238 | this._resumeData
|
239 | );
|
240 | }
|
241 |
|
242 | savable(): DownloadPauseState {
|
243 | return {
|
244 | url: this._url,
|
245 | fileUri: this._fileUri,
|
246 | options: this._options,
|
247 | resumeData: this._resumeData,
|
248 | };
|
249 | }
|
250 |
|
251 | _addSubscription(): void {
|
252 | if (this._subscription) {
|
253 | return;
|
254 | }
|
255 | this._subscription = this._emitter.addListener(
|
256 | 'Exponent.downloadProgress',
|
257 | (event: ProgressEvent) => {
|
258 | if (event.uuid === this._uuid) {
|
259 | const callback = this._callback;
|
260 | if (callback) {
|
261 | callback(event.data);
|
262 | }
|
263 | }
|
264 | }
|
265 | );
|
266 | }
|
267 |
|
268 | _removeSubscription(): void {
|
269 | if (!this._subscription) {
|
270 | return;
|
271 | }
|
272 | this._emitter.removeSubscription(this._subscription);
|
273 | this._subscription = null;
|
274 | }
|
275 | }
|