UNPKG

8.36 kBPlain TextView Raw
1import UUID from 'uuid-js';
2import { Platform } from 'react-native';
3import { EventEmitter, Subscription, UnavailabilityError } from '@unimodules/core';
4
5import ExponentFileSystem from './ExponentFileSystem';
6
7import {
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
20if (!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// Prevent webpack from pruning this.
26const _unused = new EventEmitter(ExponentFileSystem); // eslint-disable-line
27
28export {
29 DownloadOptions,
30 DownloadResult,
31 DownloadProgressCallback,
32 DownloadProgressData,
33 DownloadPauseState,
34 FileInfo,
35 EncodingType,
36 ReadingOptions,
37 WritingOptions,
38 ProgressEvent,
39};
40
41function normalizeEndingSlash(p: string | null): string | null {
42 if (p != null) {
43 return p.replace(/\/*$/, '') + '/';
44 }
45 return null;
46}
47
48export const documentDirectory = normalizeEndingSlash(ExponentFileSystem.documentDirectory);
49export const cacheDirectory = normalizeEndingSlash(ExponentFileSystem.cacheDirectory);
50
51export const { bundledAssets, bundleDirectory } = ExponentFileSystem;
52
53export 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
63export 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
73export 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
86export 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
97export 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
107export async function deleteLegacyDocumentDirectoryAndroid(): Promise<void> {
108 if (Platform.OS !== 'android' || documentDirectory == null) {
109 return;
110 }
111 const legacyDocumentDirectory = `${documentDirectory}ExperienceData/`;
112 return await deleteAsync(legacyDocumentDirectory, { idempotent: true });
113}
114
115export async function moveAsync(options: { from: string; to: string }): Promise<void> {
116 if (!ExponentFileSystem.moveAsync) {
117 throw new UnavailabilityError('expo-file-system', 'moveAsync');
118 }
119 return await ExponentFileSystem.moveAsync(options);
120}
121
122export async function copyAsync(options: { from: string; to: string }): Promise<void> {
123 if (!ExponentFileSystem.copyAsync) {
124 throw new UnavailabilityError('expo-file-system', 'copyAsync');
125 }
126 return await ExponentFileSystem.copyAsync(options);
127}
128
129export async function makeDirectoryAsync(
130 fileUri: string,
131 options: { intermediates?: boolean } = {}
132): Promise<void> {
133 if (!ExponentFileSystem.makeDirectoryAsync) {
134 throw new UnavailabilityError('expo-file-system', 'makeDirectoryAsync');
135 }
136 return await ExponentFileSystem.makeDirectoryAsync(fileUri, options);
137}
138
139export async function readDirectoryAsync(fileUri: string): Promise<string[]> {
140 if (!ExponentFileSystem.readDirectoryAsync) {
141 throw new UnavailabilityError('expo-file-system', 'readDirectoryAsync');
142 }
143 return await ExponentFileSystem.readDirectoryAsync(fileUri, {});
144}
145
146export async function getFreeDiskStorageAsync(): Promise<number> {
147 if (!ExponentFileSystem.getFreeDiskStorageAsync) {
148 throw new UnavailabilityError('expo-file-system', 'getFreeDiskStorageAsync');
149 }
150 return await ExponentFileSystem.getFreeDiskStorageAsync();
151}
152
153export async function getTotalDiskCapacityAsync(): Promise<number> {
154 if (!ExponentFileSystem.getTotalDiskCapacityAsync) {
155 throw new UnavailabilityError('expo-file-system', 'getTotalDiskCapacityAsync');
156 }
157 return await ExponentFileSystem.getTotalDiskCapacityAsync();
158}
159
160export async function downloadAsync(
161 uri: string,
162 fileUri: string,
163 options: DownloadOptions = {}
164): Promise<DownloadResult> {
165 if (!ExponentFileSystem.downloadAsync) {
166 throw new UnavailabilityError('expo-file-system', 'downloadAsync');
167 }
168 return await ExponentFileSystem.downloadAsync(uri, fileUri, options);
169}
170
171export function createDownloadResumable(
172 uri: string,
173 fileUri: string,
174 options?: DownloadOptions,
175 callback?: DownloadProgressCallback,
176 resumeData?: string
177): DownloadResumable {
178 return new DownloadResumable(uri, fileUri, options, callback, resumeData);
179}
180
181export class DownloadResumable {
182 _uuid: string;
183 _url: string;
184 _fileUri: string;
185 _options: DownloadOptions;
186 _resumeData?: string;
187 _callback?: DownloadProgressCallback;
188 _subscription?: Subscription | null;
189 _emitter: EventEmitter;
190
191 constructor(
192 url: string,
193 fileUri: string,
194 options: DownloadOptions = {},
195 callback?: DownloadProgressCallback,
196 resumeData?: string
197 ) {
198 this._uuid = UUID.create(4).toString();
199 this._url = url;
200 this._fileUri = fileUri;
201 this._options = options;
202 this._resumeData = resumeData;
203 this._callback = callback;
204 this._subscription = null;
205 this._emitter = new EventEmitter(ExponentFileSystem);
206 }
207
208 async downloadAsync(): Promise<DownloadResult | undefined> {
209 if (!ExponentFileSystem.downloadResumableStartAsync) {
210 throw new UnavailabilityError('expo-file-system', 'downloadResumableStartAsync');
211 }
212 this._addSubscription();
213 return await ExponentFileSystem.downloadResumableStartAsync(
214 this._url,
215 this._fileUri,
216 this._uuid,
217 this._options,
218 this._resumeData
219 );
220 }
221
222 async pauseAsync(): Promise<DownloadPauseState> {
223 if (!ExponentFileSystem.downloadResumablePauseAsync) {
224 throw new UnavailabilityError('expo-file-system', 'downloadResumablePauseAsync');
225 }
226 const pauseResult = await ExponentFileSystem.downloadResumablePauseAsync(this._uuid);
227 this._removeSubscription();
228 if (pauseResult) {
229 this._resumeData = pauseResult.resumeData;
230 return this.savable();
231 } else {
232 throw new Error('Unable to generate a savable pause state');
233 }
234 }
235
236 async resumeAsync(): Promise<DownloadResult | undefined> {
237 if (!ExponentFileSystem.downloadResumableStartAsync) {
238 throw new UnavailabilityError('expo-file-system', 'downloadResumableStartAsync');
239 }
240 this._addSubscription();
241 return await ExponentFileSystem.downloadResumableStartAsync(
242 this._url,
243 this._fileUri,
244 this._uuid,
245 this._options,
246 this._resumeData
247 );
248 }
249
250 savable(): DownloadPauseState {
251 return {
252 url: this._url,
253 fileUri: this._fileUri,
254 options: this._options,
255 resumeData: this._resumeData,
256 };
257 }
258
259 _addSubscription(): void {
260 if (this._subscription) {
261 return;
262 }
263 this._subscription = this._emitter.addListener(
264 'Exponent.downloadProgress',
265 (event: ProgressEvent) => {
266 if (event.uuid === this._uuid) {
267 const callback = this._callback;
268 if (callback) {
269 callback(event.data);
270 }
271 }
272 }
273 );
274 }
275
276 _removeSubscription(): void {
277 if (!this._subscription) {
278 return;
279 }
280 this._emitter.removeSubscription(this._subscription);
281 this._subscription = null;
282 }
283}