react-native-cloud-storage 1.5.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +6 -5
- package/ios/CloudStorage.swift +52 -262
- package/ios/CloudStorage.xcodeproj/project.pbxproj +28 -0
- package/ios/CloudStorageEventEmitter.swift +4 -4
- package/ios/Utils/CloudKitUtils.swift +112 -0
- package/ios/Utils/CloudStorageError.swift +78 -0
- package/ios/Utils/FileUtils.swift +132 -0
- package/ios/Utils/Promise.swift +58 -0
- package/ios/Utils/Types.swift +36 -0
- package/lib/commonjs/RNCloudStorage.js +361 -66
- package/lib/commonjs/RNCloudStorage.js.map +1 -1
- package/lib/commonjs/expo-plugin/types/index.js.map +1 -1
- package/lib/commonjs/expo-plugin/withRNCloudStorage.js +2 -3
- package/lib/commonjs/expo-plugin/withRNCloudStorage.js.map +1 -1
- package/lib/commonjs/expo-plugin/withRNCloudStorageIos.js +4 -7
- package/lib/commonjs/expo-plugin/withRNCloudStorageIos.js.map +1 -1
- package/lib/commonjs/google-drive/client.js +16 -20
- package/lib/commonjs/google-drive/client.js.map +1 -1
- package/lib/commonjs/google-drive/index.js +42 -64
- package/lib/commonjs/google-drive/index.js.map +1 -1
- package/lib/commonjs/google-drive/types.js +1 -2
- package/lib/commonjs/google-drive/types.js.map +1 -1
- package/lib/commonjs/hooks/useCloudFile.js +14 -17
- package/lib/commonjs/hooks/useCloudFile.js.map +1 -1
- package/lib/commonjs/hooks/useIsCloudAvailable.js +11 -21
- package/lib/commonjs/hooks/useIsCloudAvailable.js.map +1 -1
- package/lib/commonjs/index.js +1 -7
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/types/main.js +8 -3
- package/lib/commonjs/types/main.js.map +1 -1
- package/lib/commonjs/types/native.js +3 -3
- package/lib/commonjs/types/native.js.map +1 -1
- package/lib/commonjs/utils/CloudStorageError.js +1 -2
- package/lib/commonjs/utils/CloudStorageError.js.map +1 -1
- package/lib/commonjs/utils/helpers.js +8 -15
- package/lib/commonjs/utils/helpers.js.map +1 -1
- package/lib/module/RNCloudStorage.js +362 -65
- package/lib/module/RNCloudStorage.js.map +1 -1
- package/lib/module/expo-plugin/types/index.js +1 -1
- package/lib/module/expo-plugin/types/index.js.map +1 -1
- package/lib/module/expo-plugin/withRNCloudStorage.js +2 -0
- package/lib/module/expo-plugin/withRNCloudStorage.js.map +1 -1
- package/lib/module/expo-plugin/withRNCloudStorageIos.js +5 -5
- package/lib/module/expo-plugin/withRNCloudStorageIos.js.map +1 -1
- package/lib/module/google-drive/client.js +18 -20
- package/lib/module/google-drive/client.js.map +1 -1
- package/lib/module/google-drive/index.js +41 -62
- package/lib/module/google-drive/index.js.map +1 -1
- package/lib/module/google-drive/types.js +2 -0
- package/lib/module/google-drive/types.js.map +1 -1
- package/lib/module/hooks/useCloudFile.js +15 -16
- package/lib/module/hooks/useCloudFile.js.map +1 -1
- package/lib/module/hooks/useIsCloudAvailable.js +13 -21
- package/lib/module/hooks/useIsCloudAvailable.js.map +1 -1
- package/lib/module/index.js +2 -5
- package/lib/module/index.js.map +1 -1
- package/lib/module/package.json +1 -0
- package/lib/module/types/main.js +9 -0
- package/lib/module/types/main.js.map +1 -1
- package/lib/module/types/native.js +4 -1
- package/lib/module/types/native.js.map +1 -1
- package/lib/module/utils/CloudStorageError.js +2 -0
- package/lib/module/utils/CloudStorageError.js.map +1 -1
- package/lib/module/utils/helpers.js +8 -13
- package/lib/module/utils/helpers.js.map +1 -1
- package/lib/typescript/RNCloudStorage.d.ts +159 -39
- package/lib/typescript/RNCloudStorage.d.ts.map +1 -1
- package/lib/typescript/google-drive/client.d.ts +3 -3
- package/lib/typescript/google-drive/client.d.ts.map +1 -1
- package/lib/typescript/google-drive/index.d.ts +6 -18
- package/lib/typescript/google-drive/index.d.ts.map +1 -1
- package/lib/typescript/hooks/useCloudFile.d.ts +4 -7
- package/lib/typescript/hooks/useCloudFile.d.ts.map +1 -1
- package/lib/typescript/hooks/useIsCloudAvailable.d.ts +3 -2
- package/lib/typescript/hooks/useIsCloudAvailable.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +0 -4
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/types/main.d.ts +33 -0
- package/lib/typescript/types/main.d.ts.map +1 -1
- package/lib/typescript/types/native.d.ts +2 -1
- package/lib/typescript/types/native.d.ts.map +1 -1
- package/lib/typescript/utils/helpers.d.ts +2 -9
- package/lib/typescript/utils/helpers.d.ts.map +1 -1
- package/package.json +9 -11
- package/src/RNCloudStorage.ts +387 -68
- package/src/google-drive/client.ts +8 -7
- package/src/google-drive/index.ts +38 -63
- package/src/hooks/useCloudFile.ts +13 -16
- package/src/hooks/useIsCloudAvailable.ts +12 -25
- package/src/index.ts +0 -5
- package/src/types/main.ts +38 -0
- package/src/types/native.ts +2 -1
- package/src/utils/helpers.ts +8 -15
- package/lib/commonjs/createRNCloudStorage.js +0 -48
- package/lib/commonjs/createRNCloudStorage.js.map +0 -1
- package/lib/module/createRNCloudStorage.js +0 -41
- package/lib/module/createRNCloudStorage.js.map +0 -1
- package/lib/typescript/createRNCloudStorage.d.ts +0 -3
- package/lib/typescript/createRNCloudStorage.d.ts.map +0 -1
- package/src/createRNCloudStorage.ts +0 -53
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import type NativeproviderService from '../types/native';
|
|
2
2
|
import {
|
|
3
3
|
CloudStorageErrorCode,
|
|
4
4
|
type NativeRNCloudCloudStorageFileStat,
|
|
@@ -6,28 +6,30 @@ import {
|
|
|
6
6
|
} from '../types/native';
|
|
7
7
|
import CloudStorageError from '../utils/CloudStorageError';
|
|
8
8
|
import { MimeTypes, type GoogleDriveFile, type GoogleDriveFileSpace } from './types';
|
|
9
|
-
import { DeviceEventEmitter } from 'react-native';
|
|
10
9
|
import GoogleDriveApiClient, { GoogleDriveHttpError } from './client';
|
|
10
|
+
import { type CloudStorageProviderOptions, type DeepRequired } from '../types/main';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* A proxy class that wraps the Google Drive API client implementation to match the native iOS interface.
|
|
14
14
|
*/
|
|
15
|
-
export default class GoogleDrive implements
|
|
16
|
-
private
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
export default class GoogleDrive implements NativeproviderService {
|
|
16
|
+
private drive: GoogleDriveApiClient;
|
|
17
|
+
private options: DeepRequired<CloudStorageProviderOptions['googledrive']>;
|
|
18
|
+
|
|
19
|
+
constructor(options: DeepRequired<CloudStorageProviderOptions['googledrive']>) {
|
|
20
|
+
this.options = options;
|
|
21
|
+
this.drive = new GoogleDriveApiClient(options);
|
|
19
22
|
|
|
20
|
-
constructor() {
|
|
21
|
-
this.filesWithSameNameSubscribers = [];
|
|
22
23
|
return new Proxy(this, {
|
|
23
24
|
// before calling any function, check if the access token is set
|
|
24
25
|
get(target: GoogleDrive, prop: keyof GoogleDrive) {
|
|
25
|
-
const allowedFunctions = ['isCloudAvailable'
|
|
26
|
+
const allowedFunctions = ['isCloudAvailable'];
|
|
26
27
|
if (typeof target[prop] === 'function' && !allowedFunctions.includes(prop.toString())) {
|
|
27
|
-
|
|
28
|
+
const { accessToken } = options;
|
|
29
|
+
if (!accessToken?.length) {
|
|
28
30
|
throw new CloudStorageError(
|
|
29
31
|
`Google Drive access token is not set, cannot call function ${prop.toString()}`,
|
|
30
|
-
CloudStorageErrorCode.
|
|
32
|
+
CloudStorageErrorCode.ACCESS_TOKEN_MISSING
|
|
31
33
|
);
|
|
32
34
|
}
|
|
33
35
|
}
|
|
@@ -37,37 +39,10 @@ export default class GoogleDrive implements NativeRNCloudStorage {
|
|
|
37
39
|
});
|
|
38
40
|
}
|
|
39
41
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
// emit an event for the useIsCloudAvailable hook
|
|
45
|
-
DeviceEventEmitter.emit('RNCloudStorage.cloud.availability-changed', {
|
|
46
|
-
available: !!accessToken?.length,
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
public static set timeout(timeout: number) {
|
|
51
|
-
GoogleDrive.drive.timeout = timeout;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
public static get accessToken(): string | null {
|
|
55
|
-
return GoogleDrive.drive.accessToken.length ? GoogleDrive.drive.accessToken : null;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
public subscribeToFilesWithSameName(subscriber: ({ path, fileIds }: { path: string; fileIds: string[] }) => void): {
|
|
59
|
-
remove: () => void;
|
|
60
|
-
} {
|
|
61
|
-
this.filesWithSameNameSubscribers.push(subscriber);
|
|
62
|
-
|
|
63
|
-
return {
|
|
64
|
-
remove: () => {
|
|
65
|
-
this.filesWithSameNameSubscribers = this.filesWithSameNameSubscribers.filter((s) => s !== subscriber);
|
|
66
|
-
},
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
public isCloudAvailable: () => Promise<boolean> = async () => !!GoogleDrive.drive.accessToken.length;
|
|
42
|
+
public isCloudAvailable: () => Promise<boolean> = async () => {
|
|
43
|
+
const { accessToken } = this.options;
|
|
44
|
+
return !!accessToken?.length;
|
|
45
|
+
};
|
|
71
46
|
|
|
72
47
|
private getRootDirectory(scope: NativeRNCloudCloudStorageScope): GoogleDriveFileSpace {
|
|
73
48
|
switch (scope) {
|
|
@@ -141,7 +116,7 @@ export default class GoogleDrive implements NativeRNCloudStorage {
|
|
|
141
116
|
* @returns A promise that resolves to the ID of the root directory or null if it could not be found.
|
|
142
117
|
*/
|
|
143
118
|
private async getRootDirectoryId(scope: NativeRNCloudCloudStorageScope): Promise<string | null> {
|
|
144
|
-
const files = await
|
|
119
|
+
const files = await this.drive.listFiles(this.getRootDirectory(scope));
|
|
145
120
|
for (const file of files) {
|
|
146
121
|
if (!files.find((f) => f.id === file.parents![0])) return file.parents![0] ?? null;
|
|
147
122
|
}
|
|
@@ -155,6 +130,8 @@ export default class GoogleDrive implements NativeRNCloudStorage {
|
|
|
155
130
|
filename: string,
|
|
156
131
|
parentDirectoryId: string | null
|
|
157
132
|
) {
|
|
133
|
+
const { strictFilenames } = this.options;
|
|
134
|
+
|
|
158
135
|
let possibleFiles: GoogleDriveFile[];
|
|
159
136
|
if (parentDirectoryId) {
|
|
160
137
|
possibleFiles = files.filter((f) => f.name === filename && f.parents![0] === parentDirectoryId);
|
|
@@ -164,13 +141,11 @@ export default class GoogleDrive implements NativeRNCloudStorage {
|
|
|
164
141
|
|
|
165
142
|
if (possibleFiles.length <= 1) return;
|
|
166
143
|
|
|
167
|
-
if (
|
|
144
|
+
if (strictFilenames) {
|
|
168
145
|
throw new CloudStorageError(
|
|
169
146
|
`Multiple files with the same name found at path ${path}: ${possibleFiles.map((f) => f.id).join(', ')}`,
|
|
170
147
|
CloudStorageErrorCode.MULTIPLE_FILES_SAME_NAME
|
|
171
148
|
);
|
|
172
|
-
} else {
|
|
173
|
-
this.filesWithSameNameSubscribers.forEach((s) => s({ path, fileIds: possibleFiles.map((f) => f.id) }));
|
|
174
149
|
}
|
|
175
150
|
}
|
|
176
151
|
|
|
@@ -180,7 +155,7 @@ export default class GoogleDrive implements NativeRNCloudStorage {
|
|
|
180
155
|
throwIf: 'directory' | 'file' | false = false
|
|
181
156
|
): Promise<string> {
|
|
182
157
|
try {
|
|
183
|
-
const files = await
|
|
158
|
+
const files = await this.drive.listFiles(this.getRootDirectory(scope));
|
|
184
159
|
|
|
185
160
|
if (path === '' || path === '/') {
|
|
186
161
|
const rootDirectoryId = await this.getRootDirectoryId(scope);
|
|
@@ -240,7 +215,7 @@ export default class GoogleDrive implements NativeRNCloudStorage {
|
|
|
240
215
|
let prevContent = '';
|
|
241
216
|
try {
|
|
242
217
|
fileId = await this.getFileId(path, scope);
|
|
243
|
-
prevContent = await
|
|
218
|
+
prevContent = await this.drive.getFileText(fileId);
|
|
244
219
|
} catch (e: any) {
|
|
245
220
|
if (e instanceof CloudStorageError && e.code === CloudStorageErrorCode.FILE_NOT_FOUND) {
|
|
246
221
|
/* do nothing, simply create the file */
|
|
@@ -250,15 +225,15 @@ export default class GoogleDrive implements NativeRNCloudStorage {
|
|
|
250
225
|
}
|
|
251
226
|
|
|
252
227
|
if (fileId) {
|
|
253
|
-
await
|
|
228
|
+
await this.drive.updateFile(fileId, {
|
|
254
229
|
body: prevContent + data,
|
|
255
230
|
mimeType: MimeTypes.TEXT,
|
|
256
231
|
});
|
|
257
232
|
} else {
|
|
258
|
-
const files = await
|
|
233
|
+
const files = await this.drive.listFiles(this.getRootDirectory(scope));
|
|
259
234
|
const { directories, filename } = this.resolvePathToDirectories(path);
|
|
260
235
|
const parentDirectoryId = this.findParentDirectoryId(files, directories);
|
|
261
|
-
await
|
|
236
|
+
await this.drive.createFile(
|
|
262
237
|
{
|
|
263
238
|
name: filename,
|
|
264
239
|
parents: parentDirectoryId
|
|
@@ -306,15 +281,15 @@ export default class GoogleDrive implements NativeRNCloudStorage {
|
|
|
306
281
|
}
|
|
307
282
|
|
|
308
283
|
if (fileId) {
|
|
309
|
-
await
|
|
284
|
+
await this.drive.updateFile(fileId, {
|
|
310
285
|
body: data,
|
|
311
286
|
mimeType: MimeTypes.TEXT,
|
|
312
287
|
});
|
|
313
288
|
} else {
|
|
314
|
-
const files = await
|
|
289
|
+
const files = await this.drive.listFiles(this.getRootDirectory(scope));
|
|
315
290
|
const { directories, filename } = this.resolvePathToDirectories(path);
|
|
316
291
|
const parentDirectoryId = this.findParentDirectoryId(files, directories);
|
|
317
|
-
await
|
|
292
|
+
await this.drive.createFile(
|
|
318
293
|
{
|
|
319
294
|
name: filename,
|
|
320
295
|
parents: parentDirectoryId
|
|
@@ -332,7 +307,7 @@ export default class GoogleDrive implements NativeRNCloudStorage {
|
|
|
332
307
|
}
|
|
333
308
|
|
|
334
309
|
async listFiles(path: string, scope: NativeRNCloudCloudStorageScope): Promise<string[]> {
|
|
335
|
-
const allFiles = await
|
|
310
|
+
const allFiles = await this.drive.listFiles(this.getRootDirectory(scope));
|
|
336
311
|
if (path !== '') {
|
|
337
312
|
const fileId = await this.getFileId(path, scope);
|
|
338
313
|
const files = allFiles.filter((f) => (f.parents ?? [])[0] === fileId);
|
|
@@ -358,11 +333,11 @@ export default class GoogleDrive implements NativeRNCloudStorage {
|
|
|
358
333
|
}
|
|
359
334
|
}
|
|
360
335
|
|
|
361
|
-
const files = await
|
|
336
|
+
const files = await this.drive.listFiles(this.getRootDirectory(scope));
|
|
362
337
|
const { directories, filename } = this.resolvePathToDirectories(path);
|
|
363
338
|
const parentDirectoryId = this.findParentDirectoryId(files, directories);
|
|
364
339
|
|
|
365
|
-
await
|
|
340
|
+
await this.drive.createDirectory({
|
|
366
341
|
name: filename,
|
|
367
342
|
parents: parentDirectoryId
|
|
368
343
|
? [parentDirectoryId]
|
|
@@ -374,19 +349,19 @@ export default class GoogleDrive implements NativeRNCloudStorage {
|
|
|
374
349
|
|
|
375
350
|
async readFile(path: string, scope: NativeRNCloudCloudStorageScope): Promise<string> {
|
|
376
351
|
const fileId = await this.getFileId(path, scope);
|
|
377
|
-
const content = await
|
|
352
|
+
const content = await this.drive.getFileText(fileId);
|
|
378
353
|
return content;
|
|
379
354
|
}
|
|
380
355
|
|
|
381
356
|
async downloadFile(_path: string, _scope: NativeRNCloudCloudStorageScope): Promise<void> {
|
|
382
|
-
//
|
|
357
|
+
// Downloading files from Google Drive is not necessary / possible, as they need to be downloaded on every read operation via the API anyway
|
|
383
358
|
return;
|
|
384
359
|
}
|
|
385
360
|
|
|
386
361
|
async deleteFile(path: string, scope: NativeRNCloudCloudStorageScope): Promise<void> {
|
|
387
362
|
// if trying to pass a directory, throw an error
|
|
388
363
|
const fileId = await this.getFileId(path, scope, 'directory');
|
|
389
|
-
await
|
|
364
|
+
await this.drive.deleteFile(fileId);
|
|
390
365
|
}
|
|
391
366
|
|
|
392
367
|
async deleteDirectory(path: string, recursive: boolean, scope: NativeRNCloudCloudStorageScope): Promise<void> {
|
|
@@ -395,7 +370,7 @@ export default class GoogleDrive implements NativeRNCloudStorage {
|
|
|
395
370
|
|
|
396
371
|
if (!recursive) {
|
|
397
372
|
// check if the directory is empty
|
|
398
|
-
const files = await
|
|
373
|
+
const files = await this.drive.listFiles(this.getRootDirectory(scope));
|
|
399
374
|
const filesInDirectory = files.filter((f) => (f.parents ?? [])[0] === fileId);
|
|
400
375
|
if (filesInDirectory.length > 0) {
|
|
401
376
|
throw new CloudStorageError(
|
|
@@ -406,12 +381,12 @@ export default class GoogleDrive implements NativeRNCloudStorage {
|
|
|
406
381
|
}
|
|
407
382
|
}
|
|
408
383
|
|
|
409
|
-
await
|
|
384
|
+
await this.drive.deleteFile(fileId);
|
|
410
385
|
}
|
|
411
386
|
|
|
412
387
|
async statFile(path: string, scope: NativeRNCloudCloudStorageScope): Promise<NativeRNCloudCloudStorageFileStat> {
|
|
413
388
|
const fileId = await this.getFileId(path, scope, false);
|
|
414
|
-
const file = await
|
|
389
|
+
const file = await this.drive.getFile(fileId!);
|
|
415
390
|
|
|
416
391
|
return {
|
|
417
392
|
size: file.size ?? 0,
|
|
@@ -5,20 +5,22 @@ import { useCallback, useEffect, useState } from 'react';
|
|
|
5
5
|
/**
|
|
6
6
|
* A utility hook for reading and writing to a single file in the cloud.
|
|
7
7
|
* @param path The path to the file.
|
|
8
|
-
* @param scope The directory scope the path is in.
|
|
8
|
+
* @param scope The directory scope the path is in. Defaults to the default scope set for the current provider.
|
|
9
|
+
* @param cloudStorageInstance An optional instance of RNCloudStorage to use instead of the default instance.
|
|
9
10
|
* @returns An object containing the file's contents and functions for downloading, reading, writing, and removing the file.
|
|
10
11
|
*/
|
|
11
|
-
export const useCloudFile = (path: string, scope?: CloudStorageScope) => {
|
|
12
|
+
export const useCloudFile = (path: string, scope?: CloudStorageScope, cloudStorageInstance?: RNCloudStorage) => {
|
|
12
13
|
const [content, setContent] = useState<string | null>(null);
|
|
14
|
+
const instance = cloudStorageInstance ?? RNCloudStorage;
|
|
13
15
|
|
|
14
16
|
const read = useCallback(async () => {
|
|
15
|
-
const exists = await
|
|
17
|
+
const exists = await instance.exists(path, scope);
|
|
16
18
|
if (!exists) {
|
|
17
19
|
setContent(null);
|
|
18
20
|
return;
|
|
19
21
|
}
|
|
20
|
-
|
|
21
|
-
}, [path, scope]);
|
|
22
|
+
instance.readFile(path, scope).then(setContent);
|
|
23
|
+
}, [path, scope, instance]);
|
|
22
24
|
|
|
23
25
|
useEffect(() => {
|
|
24
26
|
read();
|
|
@@ -26,31 +28,26 @@ export const useCloudFile = (path: string, scope?: CloudStorageScope) => {
|
|
|
26
28
|
|
|
27
29
|
const write = useCallback(
|
|
28
30
|
async (newContent: string) => {
|
|
29
|
-
await
|
|
31
|
+
await instance.writeFile(path, newContent, scope);
|
|
30
32
|
read();
|
|
31
33
|
},
|
|
32
|
-
[path, scope, read]
|
|
34
|
+
[path, scope, read, instance]
|
|
33
35
|
);
|
|
34
36
|
|
|
35
37
|
const remove = useCallback(async () => {
|
|
36
|
-
await
|
|
38
|
+
await instance.unlink(path, scope);
|
|
37
39
|
setContent(null);
|
|
38
|
-
}, [path, scope]);
|
|
40
|
+
}, [path, scope, instance]);
|
|
39
41
|
|
|
40
42
|
const download = useCallback(async () => {
|
|
41
|
-
await
|
|
42
|
-
}, [path, scope]);
|
|
43
|
+
await instance.downloadFile(path, scope);
|
|
44
|
+
}, [path, scope, instance]);
|
|
43
45
|
|
|
44
46
|
return {
|
|
45
47
|
content,
|
|
46
48
|
read,
|
|
47
49
|
write,
|
|
48
50
|
remove,
|
|
49
|
-
/**
|
|
50
|
-
* @deprecated Use `write` instead.
|
|
51
|
-
* @alias write
|
|
52
|
-
*/
|
|
53
|
-
update: write,
|
|
54
51
|
/**
|
|
55
52
|
* Downloads the file from iCloud to the device. Needed if the file hasn't been synced yet. Has no effect on
|
|
56
53
|
* Google Drive.
|
|
@@ -1,43 +1,30 @@
|
|
|
1
|
-
import { useEffect, useState } from 'react';
|
|
2
|
-
import { NativeEventEmitter, NativeModules, Platform, DeviceEventEmitter } from 'react-native';
|
|
1
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
3
2
|
import RNCloudStorage from '../RNCloudStorage';
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* A hook that tests whether or not the cloud storage is available.
|
|
7
|
-
* @param
|
|
6
|
+
* @param cloudStorageInstance - An optional instance of RNCloudStorage to use instead of the default instance.
|
|
8
7
|
* @returns A boolean indicating whether or not the cloud storage is available.
|
|
9
8
|
*/
|
|
10
|
-
export const useIsCloudAvailable = (
|
|
9
|
+
export const useIsCloudAvailable = (cloudStorageInstance?: RNCloudStorage) => {
|
|
11
10
|
const [isAvailable, setIsAvailable] = useState(false);
|
|
11
|
+
const instance = cloudStorageInstance ?? RNCloudStorage.getDefaultInstance();
|
|
12
|
+
|
|
13
|
+
const handleAvailabilityChange = useCallback((available: boolean) => {
|
|
14
|
+
setIsAvailable(available);
|
|
15
|
+
}, []);
|
|
12
16
|
|
|
13
17
|
useEffect(() => {
|
|
14
18
|
// Set the initial availability state
|
|
15
|
-
|
|
19
|
+
instance.isCloudAvailable().then(setIsAvailable);
|
|
16
20
|
|
|
17
21
|
// Listen for changes to the cloud availability using the native event emitter
|
|
18
|
-
|
|
19
|
-
if (Platform.OS === 'ios') {
|
|
20
|
-
eventEmitter = new NativeEventEmitter(NativeModules.CloudStorageEventEmitter);
|
|
21
|
-
} else {
|
|
22
|
-
eventEmitter = DeviceEventEmitter;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
eventEmitter.addListener('RNCloudStorage.cloud.availability-changed', (event: { available: boolean }) => {
|
|
26
|
-
setIsAvailable(event.available);
|
|
27
|
-
});
|
|
22
|
+
instance.subscribeToCloudAvailability(handleAvailabilityChange);
|
|
28
23
|
|
|
29
24
|
return () => {
|
|
30
|
-
|
|
25
|
+
instance.unsubscribeFromCloudAvailability(handleAvailabilityChange);
|
|
31
26
|
};
|
|
32
|
-
}, []);
|
|
33
|
-
|
|
34
|
-
useEffect(() => {
|
|
35
|
-
if (_iCloudTimeout !== undefined) {
|
|
36
|
-
console.warn(
|
|
37
|
-
'The iCloudTimeout parameter for useIsCloudFile is deprecated and has no effect. It will be removed in a future version. Please remove it from your code.'
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
}, [_iCloudTimeout]);
|
|
27
|
+
}, [instance, handleAvailabilityChange]);
|
|
41
28
|
|
|
42
29
|
return isAvailable;
|
|
43
30
|
};
|
package/src/index.ts
CHANGED
|
@@ -6,8 +6,3 @@ export * from './hooks/useIsCloudAvailable';
|
|
|
6
6
|
import CloudStorageError from './utils/CloudStorageError';
|
|
7
7
|
|
|
8
8
|
export { RNCloudStorage as CloudStorage, CloudStorageError, CloudStorageErrorCode };
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* @deprecated Use the named export `CloudStorage` instead.
|
|
12
|
-
*/
|
|
13
|
-
export default RNCloudStorage;
|
package/src/types/main.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/* Custom utility type to make properties required, but still allow null if defined */
|
|
2
|
+
export type DeepRequired<T> = {
|
|
3
|
+
[P in keyof T]-?: T[P] extends object ? DeepRequired<T[P]> : T[P];
|
|
4
|
+
};
|
|
5
|
+
|
|
1
6
|
export enum CloudStorageScope {
|
|
2
7
|
Documents = 'documents',
|
|
3
8
|
AppData = 'app_data',
|
|
@@ -12,3 +17,36 @@ export interface CloudStorageFileStat {
|
|
|
12
17
|
isDirectory: () => boolean;
|
|
13
18
|
isFile: () => boolean;
|
|
14
19
|
}
|
|
20
|
+
|
|
21
|
+
export enum CloudStorageProvider {
|
|
22
|
+
ICloud = 'icloud',
|
|
23
|
+
GoogleDrive = 'googledrive',
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface CloudStorageProviderOptions {
|
|
27
|
+
[CloudStorageProvider.ICloud]: {
|
|
28
|
+
/**
|
|
29
|
+
* The directory scope to use for iCloud operations. Defaults to 'app_data'.
|
|
30
|
+
*/
|
|
31
|
+
scope?: CloudStorageScope;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
[CloudStorageProvider.GoogleDrive]: {
|
|
35
|
+
/**
|
|
36
|
+
* The directory scope to use for Google Drive operations. Defaults to 'app_data'.
|
|
37
|
+
*/
|
|
38
|
+
scope?: CloudStorageScope;
|
|
39
|
+
/**
|
|
40
|
+
* The access token to use for Google Drive operations.
|
|
41
|
+
*/
|
|
42
|
+
accessToken?: string | null;
|
|
43
|
+
/**
|
|
44
|
+
* Whether or not to throw an error if multiple files with the same filename are found. Defaults to false.
|
|
45
|
+
*/
|
|
46
|
+
strictFilenames?: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* The timeout in milliseconds after which to cancel an API request. Defaults to 3000.
|
|
49
|
+
*/
|
|
50
|
+
timeout?: number;
|
|
51
|
+
};
|
|
52
|
+
}
|
package/src/types/native.ts
CHANGED
|
@@ -9,6 +9,7 @@ export interface NativeRNCloudCloudStorageFileStat {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export enum CloudStorageErrorCode {
|
|
12
|
+
INVALID_SCOPE = 'ERR_INVALID_SCOPE',
|
|
12
13
|
FILE_NOT_FOUND = 'ERR_FILE_NOT_FOUND',
|
|
13
14
|
PATH_IS_FILE = 'ERR_PATH_IS_FILE',
|
|
14
15
|
PATH_IS_DIRECTORY = 'ERR_PATH_IS_DIRECTORY',
|
|
@@ -23,7 +24,7 @@ export enum CloudStorageErrorCode {
|
|
|
23
24
|
STAT_ERROR = 'ERR_STAT_ERROR',
|
|
24
25
|
UNKNOWN = 'ERR_UNKNOWN',
|
|
25
26
|
FILE_NOT_DOWNLOADABLE = 'ERR_FILE_NOT_DOWNLOADABLE',
|
|
26
|
-
|
|
27
|
+
ACCESS_TOKEN_MISSING = 'ERR_ACCESS_TOKEN_MISSING',
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
export default interface NativeRNCloudStorage {
|
package/src/utils/helpers.ts
CHANGED
|
@@ -1,17 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
* @private
|
|
8
|
-
*/
|
|
9
|
-
export const verifyLeadingSlash = (path: string) => {
|
|
10
|
-
if (!path.startsWith('/')) {
|
|
11
|
-
console.warn(
|
|
12
|
-
`[react-native-cloud-storage] Path "${path}" did not start with a leading slash. This is deprecated and will be an error in the future.`
|
|
13
|
-
);
|
|
14
|
-
return `/${path}`;
|
|
1
|
+
import { CloudStorageProvider } from '../types/main';
|
|
2
|
+
import { Platform } from 'react-native';
|
|
3
|
+
|
|
4
|
+
export const isProviderSupported = (provider: CloudStorageProvider): boolean => {
|
|
5
|
+
if (Platform.OS !== 'ios' && provider === CloudStorageProvider.ICloud) {
|
|
6
|
+
return false;
|
|
15
7
|
}
|
|
16
|
-
|
|
8
|
+
|
|
9
|
+
return true;
|
|
17
10
|
};
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.default = createRNCloudStorage;
|
|
7
|
-
var _reactNative = require("react-native");
|
|
8
|
-
var _googleDrive = _interopRequireDefault(require("./google-drive"));
|
|
9
|
-
var _native = require("./types/native");
|
|
10
|
-
var _CloudStorageError = _interopRequireDefault(require("./utils/CloudStorageError"));
|
|
11
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
12
|
-
const LINKING_ERROR = `The package 'react-native-cloud-storage' doesn't seem to be linked. Make sure: \n\n` + _reactNative.Platform.select({
|
|
13
|
-
ios: "- You have run 'pod install'\n",
|
|
14
|
-
default: ''
|
|
15
|
-
}) + '- You rebuilt the app after installing the package\n' + '- You are not using Expo Go\n';
|
|
16
|
-
|
|
17
|
-
// proxy NativeModules.CloudStorage to catch any errors thrown by the native module and wrap them in a CloudStorageError
|
|
18
|
-
const nativeIosInstance = _reactNative.NativeModules.CloudStorage ? new Proxy(_reactNative.NativeModules.CloudStorage, {
|
|
19
|
-
get(target, prop) {
|
|
20
|
-
const originalFunction = target[prop];
|
|
21
|
-
if (typeof originalFunction === 'function') {
|
|
22
|
-
return async function () {
|
|
23
|
-
try {
|
|
24
|
-
// @ts-expect-error - we can't know the types of the functions and their arguments
|
|
25
|
-
return await originalFunction(...arguments);
|
|
26
|
-
} catch (error) {
|
|
27
|
-
if (error !== null && error !== void 0 && error.code && Object.values(_native.CloudStorageErrorCode).includes(error.code)) {
|
|
28
|
-
throw new _CloudStorageError.default((error === null || error === void 0 ? void 0 : error.message) || '', error.code);
|
|
29
|
-
} else {
|
|
30
|
-
throw new _CloudStorageError.default('Unknown error', _native.CloudStorageErrorCode.UNKNOWN, error);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
return originalFunction;
|
|
36
|
-
}
|
|
37
|
-
}) : null;
|
|
38
|
-
function createRNCloudStorage() {
|
|
39
|
-
if (_reactNative.Platform.OS === 'ios') {
|
|
40
|
-
return nativeIosInstance ?? new Proxy({}, {
|
|
41
|
-
get() {
|
|
42
|
-
throw new Error(LINKING_ERROR);
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
return new _googleDrive.default();
|
|
47
|
-
}
|
|
48
|
-
//# sourceMappingURL=createRNCloudStorage.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"names":["_reactNative","require","_googleDrive","_interopRequireDefault","_native","_CloudStorageError","obj","__esModule","default","LINKING_ERROR","Platform","select","ios","nativeIosInstance","NativeModules","CloudStorage","Proxy","get","target","prop","originalFunction","arguments","error","code","Object","values","CloudStorageErrorCode","includes","CloudStorageError","message","UNKNOWN","createRNCloudStorage","OS","Error","GoogleDrive"],"sourceRoot":"../../src","sources":["createRNCloudStorage.ts"],"mappings":";;;;;;AAAA,IAAAA,YAAA,GAAAC,OAAA;AAEA,IAAAC,YAAA,GAAAC,sBAAA,CAAAF,OAAA;AACA,IAAAG,OAAA,GAAAH,OAAA;AACA,IAAAI,kBAAA,GAAAF,sBAAA,CAAAF,OAAA;AAA0D,SAAAE,uBAAAG,GAAA,WAAAA,GAAA,IAAAA,GAAA,CAAAC,UAAA,GAAAD,GAAA,KAAAE,OAAA,EAAAF,GAAA;AAE1D,MAAMG,aAAa,GAChB,qFAAoF,GACrFC,qBAAQ,CAACC,MAAM,CAAC;EAAEC,GAAG,EAAE,gCAAgC;EAAEJ,OAAO,EAAE;AAAG,CAAC,CAAC,GACvE,sDAAsD,GACtD,+BAA+B;;AAEjC;AACA,MAAMK,iBAAiB,GAAGC,0BAAa,CAACC,YAAY,GAChD,IAAIC,KAAK,CAACF,0BAAa,CAACC,YAAY,EAAE;EACpCE,GAAGA,CAACC,MAA4B,EAAEC,IAAgC,EAAE;IAClE,MAAMC,gBAAgB,GAAGF,MAAM,CAACC,IAAI,CAAC;IACrC,IAAI,OAAOC,gBAAgB,KAAK,UAAU,EAAE;MAC1C,OAAO,kBAA0B;QAC/B,IAAI;UACF;UACA,OAAO,MAAMA,gBAAgB,CAAC,GAAAC,SAAO,CAAC;QACxC,CAAC,CAAC,OAAOC,KAAU,EAAE;UACnB,IAAIA,KAAK,aAALA,KAAK,eAALA,KAAK,CAAEC,IAAI,IAAIC,MAAM,CAACC,MAAM,CAACC,6BAAqB,CAAC,CAACC,QAAQ,CAACL,KAAK,CAACC,IAAI,CAAC,EAAE;YAC5E,MAAM,IAAIK,0BAAiB,CAAC,CAAAN,KAAK,aAALA,KAAK,uBAALA,KAAK,CAAEO,OAAO,KAAI,EAAE,EAAEP,KAAK,CAACC,IAAI,CAA0B;UACxF,CAAC,MAAM;YACL,MAAM,IAAIK,0BAAiB,CAAC,eAAe,EAAEF,6BAAqB,CAACI,OAAO,EAAER,KAAK,CAAC;UACpF;QACF;MACF,CAAC;IACH;IACA,OAAOF,gBAAgB;EACzB;AACF,CAAC,CAAC,GACF,IAAI;AAEO,SAASW,oBAAoBA,CAAA,EAAyB;EACnE,IAAIrB,qBAAQ,CAACsB,EAAE,KAAK,KAAK,EAAE;IACzB,OACEnB,iBAAiB,IACjB,IAAIG,KAAK,CACP,CAAC,CAAC,EACF;MACEC,GAAGA,CAAA,EAAG;QACJ,MAAM,IAAIgB,KAAK,CAACxB,aAAa,CAAC;MAChC;IACF,CAAC,CACF;EAEL;EAEA,OAAO,IAAIyB,oBAAW,EAAE;AAC1B"}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { NativeModules, Platform } from 'react-native';
|
|
2
|
-
import GoogleDrive from './google-drive';
|
|
3
|
-
import { CloudStorageErrorCode } from './types/native';
|
|
4
|
-
import CloudStorageError from './utils/CloudStorageError';
|
|
5
|
-
const LINKING_ERROR = `The package 'react-native-cloud-storage' doesn't seem to be linked. Make sure: \n\n` + Platform.select({
|
|
6
|
-
ios: "- You have run 'pod install'\n",
|
|
7
|
-
default: ''
|
|
8
|
-
}) + '- You rebuilt the app after installing the package\n' + '- You are not using Expo Go\n';
|
|
9
|
-
|
|
10
|
-
// proxy NativeModules.CloudStorage to catch any errors thrown by the native module and wrap them in a CloudStorageError
|
|
11
|
-
const nativeIosInstance = NativeModules.CloudStorage ? new Proxy(NativeModules.CloudStorage, {
|
|
12
|
-
get(target, prop) {
|
|
13
|
-
const originalFunction = target[prop];
|
|
14
|
-
if (typeof originalFunction === 'function') {
|
|
15
|
-
return async function () {
|
|
16
|
-
try {
|
|
17
|
-
// @ts-expect-error - we can't know the types of the functions and their arguments
|
|
18
|
-
return await originalFunction(...arguments);
|
|
19
|
-
} catch (error) {
|
|
20
|
-
if (error !== null && error !== void 0 && error.code && Object.values(CloudStorageErrorCode).includes(error.code)) {
|
|
21
|
-
throw new CloudStorageError((error === null || error === void 0 ? void 0 : error.message) || '', error.code);
|
|
22
|
-
} else {
|
|
23
|
-
throw new CloudStorageError('Unknown error', CloudStorageErrorCode.UNKNOWN, error);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
return originalFunction;
|
|
29
|
-
}
|
|
30
|
-
}) : null;
|
|
31
|
-
export default function createRNCloudStorage() {
|
|
32
|
-
if (Platform.OS === 'ios') {
|
|
33
|
-
return nativeIosInstance ?? new Proxy({}, {
|
|
34
|
-
get() {
|
|
35
|
-
throw new Error(LINKING_ERROR);
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
return new GoogleDrive();
|
|
40
|
-
}
|
|
41
|
-
//# sourceMappingURL=createRNCloudStorage.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"names":["NativeModules","Platform","GoogleDrive","CloudStorageErrorCode","CloudStorageError","LINKING_ERROR","select","ios","default","nativeIosInstance","CloudStorage","Proxy","get","target","prop","originalFunction","arguments","error","code","Object","values","includes","message","UNKNOWN","createRNCloudStorage","OS","Error"],"sourceRoot":"../../src","sources":["createRNCloudStorage.ts"],"mappings":"AAAA,SAASA,aAAa,EAAEC,QAAQ,QAAQ,cAAc;AAEtD,OAAOC,WAAW,MAAM,gBAAgB;AACxC,SAASC,qBAAqB,QAAQ,gBAAgB;AACtD,OAAOC,iBAAiB,MAAM,2BAA2B;AAEzD,MAAMC,aAAa,GAChB,qFAAoF,GACrFJ,QAAQ,CAACK,MAAM,CAAC;EAAEC,GAAG,EAAE,gCAAgC;EAAEC,OAAO,EAAE;AAAG,CAAC,CAAC,GACvE,sDAAsD,GACtD,+BAA+B;;AAEjC;AACA,MAAMC,iBAAiB,GAAGT,aAAa,CAACU,YAAY,GAChD,IAAIC,KAAK,CAACX,aAAa,CAACU,YAAY,EAAE;EACpCE,GAAGA,CAACC,MAA4B,EAAEC,IAAgC,EAAE;IAClE,MAAMC,gBAAgB,GAAGF,MAAM,CAACC,IAAI,CAAC;IACrC,IAAI,OAAOC,gBAAgB,KAAK,UAAU,EAAE;MAC1C,OAAO,kBAA0B;QAC/B,IAAI;UACF;UACA,OAAO,MAAMA,gBAAgB,CAAC,GAAAC,SAAO,CAAC;QACxC,CAAC,CAAC,OAAOC,KAAU,EAAE;UACnB,IAAIA,KAAK,aAALA,KAAK,eAALA,KAAK,CAAEC,IAAI,IAAIC,MAAM,CAACC,MAAM,CAACjB,qBAAqB,CAAC,CAACkB,QAAQ,CAACJ,KAAK,CAACC,IAAI,CAAC,EAAE;YAC5E,MAAM,IAAId,iBAAiB,CAAC,CAAAa,KAAK,aAALA,KAAK,uBAALA,KAAK,CAAEK,OAAO,KAAI,EAAE,EAAEL,KAAK,CAACC,IAAI,CAA0B;UACxF,CAAC,MAAM;YACL,MAAM,IAAId,iBAAiB,CAAC,eAAe,EAAED,qBAAqB,CAACoB,OAAO,EAAEN,KAAK,CAAC;UACpF;QACF;MACF,CAAC;IACH;IACA,OAAOF,gBAAgB;EACzB;AACF,CAAC,CAAC,GACF,IAAI;AAER,eAAe,SAASS,oBAAoBA,CAAA,EAAyB;EACnE,IAAIvB,QAAQ,CAACwB,EAAE,KAAK,KAAK,EAAE;IACzB,OACEhB,iBAAiB,IACjB,IAAIE,KAAK,CACP,CAAC,CAAC,EACF;MACEC,GAAGA,CAAA,EAAG;QACJ,MAAM,IAAIc,KAAK,CAACrB,aAAa,CAAC;MAChC;IACF,CAAC,CACF;EAEL;EAEA,OAAO,IAAIH,WAAW,EAAE;AAC1B"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"createRNCloudStorage.d.ts","sourceRoot":"","sources":["../../src/createRNCloudStorage.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,oBAAoB,MAAM,gBAAgB,CAAC;AAmCvD,MAAM,CAAC,OAAO,UAAU,oBAAoB,IAAI,oBAAoB,CAgBnE"}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { NativeModules, Platform } from 'react-native';
|
|
2
|
-
import type NativeRNCloudStorage from './types/native';
|
|
3
|
-
import GoogleDrive from './google-drive';
|
|
4
|
-
import { CloudStorageErrorCode } from './types/native';
|
|
5
|
-
import CloudStorageError from './utils/CloudStorageError';
|
|
6
|
-
|
|
7
|
-
const LINKING_ERROR =
|
|
8
|
-
`The package 'react-native-cloud-storage' doesn't seem to be linked. Make sure: \n\n` +
|
|
9
|
-
Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) +
|
|
10
|
-
'- You rebuilt the app after installing the package\n' +
|
|
11
|
-
'- You are not using Expo Go\n';
|
|
12
|
-
|
|
13
|
-
// proxy NativeModules.CloudStorage to catch any errors thrown by the native module and wrap them in a CloudStorageError
|
|
14
|
-
const nativeIosInstance = NativeModules.CloudStorage
|
|
15
|
-
? new Proxy(NativeModules.CloudStorage, {
|
|
16
|
-
get(target: NativeRNCloudStorage, prop: keyof NativeRNCloudStorage) {
|
|
17
|
-
const originalFunction = target[prop];
|
|
18
|
-
if (typeof originalFunction === 'function') {
|
|
19
|
-
return async (...args: any[]) => {
|
|
20
|
-
try {
|
|
21
|
-
// @ts-expect-error - we can't know the types of the functions and their arguments
|
|
22
|
-
return await originalFunction(...args);
|
|
23
|
-
} catch (error: any) {
|
|
24
|
-
if (error?.code && Object.values(CloudStorageErrorCode).includes(error.code)) {
|
|
25
|
-
throw new CloudStorageError(error?.message || '', error.code as CloudStorageErrorCode);
|
|
26
|
-
} else {
|
|
27
|
-
throw new CloudStorageError('Unknown error', CloudStorageErrorCode.UNKNOWN, error);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
return originalFunction;
|
|
33
|
-
},
|
|
34
|
-
})
|
|
35
|
-
: null;
|
|
36
|
-
|
|
37
|
-
export default function createRNCloudStorage(): NativeRNCloudStorage {
|
|
38
|
-
if (Platform.OS === 'ios') {
|
|
39
|
-
return (
|
|
40
|
-
nativeIosInstance ??
|
|
41
|
-
new Proxy(
|
|
42
|
-
{},
|
|
43
|
-
{
|
|
44
|
-
get() {
|
|
45
|
-
throw new Error(LINKING_ERROR);
|
|
46
|
-
},
|
|
47
|
-
}
|
|
48
|
-
)
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return new GoogleDrive();
|
|
53
|
-
}
|