1 | import UUID from 'uuid-js';
|
2 | import { Platform } from 'react-native';
|
3 | import { EventEmitter, Subscription, UnavailabilityError } from '@unimodules/core';
|
4 |
|
5 | import ExponentFileSystem from './ExponentFileSystem';
|
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 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 |
|
115 | export 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 |
|
122 | export 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 |
|
129 | export 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 |
|
139 | export 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 |
|
146 | export 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 |
|
153 | export 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 |
|
160 | export 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 |
|
171 | export 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 |
|
181 | export 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 | }
|