react-native-cloud-storage 1.5.0 → 1.5.1
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 +5 -6
- package/ios/CloudStorage.swift +262 -52
- package/ios/CloudStorage.xcodeproj/project.pbxproj +0 -28
- package/ios/CloudStorage.xcodeproj/project.xcworkspace/contents.xcworkspacedata +4 -0
- package/ios/CloudStorage.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/ios/CloudStorage.xcodeproj/project.xcworkspace/xcuserdata/max.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/ios/CloudStorage.xcodeproj/xcuserdata/max.xcuserdatad/xcschemes/xcschememanagement.plist +22 -0
- package/ios/CloudStorageEventEmitter.swift +4 -4
- package/lib/commonjs/RNCloudStorage.js +66 -361
- package/lib/commonjs/RNCloudStorage.js.map +1 -1
- package/lib/commonjs/createRNCloudStorage.js +48 -0
- package/lib/commonjs/createRNCloudStorage.js.map +1 -0
- package/lib/commonjs/expo-plugin/types/index.js.map +1 -1
- package/lib/commonjs/expo-plugin/withRNCloudStorage.js +3 -2
- package/lib/commonjs/expo-plugin/withRNCloudStorage.js.map +1 -1
- package/lib/commonjs/expo-plugin/withRNCloudStorageIos.js +7 -4
- package/lib/commonjs/expo-plugin/withRNCloudStorageIos.js.map +1 -1
- package/lib/commonjs/google-drive/client.js +20 -16
- package/lib/commonjs/google-drive/client.js.map +1 -1
- package/lib/commonjs/google-drive/index.js +64 -42
- package/lib/commonjs/google-drive/index.js.map +1 -1
- package/lib/commonjs/google-drive/types.js +2 -1
- package/lib/commonjs/google-drive/types.js.map +1 -1
- package/lib/commonjs/hooks/useCloudFile.js +17 -14
- package/lib/commonjs/hooks/useCloudFile.js.map +1 -1
- package/lib/commonjs/hooks/useIsCloudAvailable.js +21 -11
- package/lib/commonjs/hooks/useIsCloudAvailable.js.map +1 -1
- package/lib/commonjs/index.js +7 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/types/main.js +3 -8
- 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 +2 -1
- package/lib/commonjs/utils/CloudStorageError.js.map +1 -1
- package/lib/commonjs/utils/helpers.js +15 -8
- package/lib/commonjs/utils/helpers.js.map +1 -1
- package/lib/module/RNCloudStorage.js +65 -362
- package/lib/module/RNCloudStorage.js.map +1 -1
- package/lib/module/createRNCloudStorage.js +41 -0
- package/lib/module/createRNCloudStorage.js.map +1 -0
- 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 +0 -2
- 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 +20 -18
- package/lib/module/google-drive/client.js.map +1 -1
- package/lib/module/google-drive/index.js +62 -41
- package/lib/module/google-drive/index.js.map +1 -1
- package/lib/module/google-drive/types.js +0 -2
- package/lib/module/google-drive/types.js.map +1 -1
- package/lib/module/hooks/useCloudFile.js +16 -15
- package/lib/module/hooks/useCloudFile.js.map +1 -1
- package/lib/module/hooks/useIsCloudAvailable.js +21 -13
- package/lib/module/hooks/useIsCloudAvailable.js.map +1 -1
- package/lib/module/index.js +5 -2
- package/lib/module/index.js.map +1 -1
- package/lib/module/types/main.js +0 -9
- package/lib/module/types/main.js.map +1 -1
- package/lib/module/types/native.js +1 -4
- package/lib/module/types/native.js.map +1 -1
- package/lib/module/utils/CloudStorageError.js +0 -2
- package/lib/module/utils/CloudStorageError.js.map +1 -1
- package/lib/module/utils/helpers.js +13 -8
- package/lib/module/utils/helpers.js.map +1 -1
- package/lib/typescript/RNCloudStorage.d.ts +39 -159
- package/lib/typescript/RNCloudStorage.d.ts.map +1 -1
- package/lib/typescript/createRNCloudStorage.d.ts +3 -0
- package/lib/typescript/createRNCloudStorage.d.ts.map +1 -0
- 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 +18 -6
- package/lib/typescript/google-drive/index.d.ts.map +1 -1
- package/lib/typescript/hooks/useCloudFile.d.ts +7 -4
- package/lib/typescript/hooks/useCloudFile.d.ts.map +1 -1
- package/lib/typescript/hooks/useIsCloudAvailable.d.ts +2 -3
- package/lib/typescript/hooks/useIsCloudAvailable.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +4 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/types/main.d.ts +0 -33
- package/lib/typescript/types/main.d.ts.map +1 -1
- package/lib/typescript/types/native.d.ts +1 -2
- package/lib/typescript/types/native.d.ts.map +1 -1
- package/lib/typescript/utils/helpers.d.ts +9 -2
- package/lib/typescript/utils/helpers.d.ts.map +1 -1
- package/package.json +11 -9
- package/src/RNCloudStorage.ts +68 -387
- package/src/createRNCloudStorage.ts +53 -0
- package/src/google-drive/client.ts +7 -8
- package/src/google-drive/index.ts +63 -38
- package/src/hooks/useCloudFile.ts +16 -13
- package/src/hooks/useIsCloudAvailable.ts +25 -12
- package/src/index.ts +5 -0
- package/src/types/main.ts +0 -38
- package/src/types/native.ts +1 -2
- package/src/utils/helpers.ts +15 -8
- package/ios/Utils/CloudKitUtils.swift +0 -112
- package/ios/Utils/CloudStorageError.swift +0 -78
- package/ios/Utils/FileUtils.swift +0 -132
- package/ios/Utils/Promise.swift +0 -58
- package/ios/Utils/Types.swift +0 -36
- package/lib/commonjs/package.json +0 -1
- package/lib/module/package.json +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import type NativeRNCloudStorage from '../types/native';
|
|
2
2
|
import {
|
|
3
3
|
CloudStorageErrorCode,
|
|
4
4
|
type NativeRNCloudCloudStorageFileStat,
|
|
@@ -6,30 +6,28 @@ 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';
|
|
9
10
|
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 drive: GoogleDriveApiClient;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
constructor(options: DeepRequired<CloudStorageProviderOptions['googledrive']>) {
|
|
20
|
-
this.options = options;
|
|
21
|
-
this.drive = new GoogleDriveApiClient(options);
|
|
15
|
+
export default class GoogleDrive implements NativeRNCloudStorage {
|
|
16
|
+
private static drive: GoogleDriveApiClient = new GoogleDriveApiClient();
|
|
17
|
+
public static throwOnFilesWithSameName = false;
|
|
18
|
+
public filesWithSameNameSubscribers: (({ path, fileIds }: { path: string; fileIds: string[] }) => void)[];
|
|
22
19
|
|
|
20
|
+
constructor() {
|
|
21
|
+
this.filesWithSameNameSubscribers = [];
|
|
23
22
|
return new Proxy(this, {
|
|
24
23
|
// before calling any function, check if the access token is set
|
|
25
24
|
get(target: GoogleDrive, prop: keyof GoogleDrive) {
|
|
26
|
-
const allowedFunctions = ['isCloudAvailable'];
|
|
25
|
+
const allowedFunctions = ['isCloudAvailable', 'subscribeToFilesWithSameName'];
|
|
27
26
|
if (typeof target[prop] === 'function' && !allowedFunctions.includes(prop.toString())) {
|
|
28
|
-
|
|
29
|
-
if (!accessToken?.length) {
|
|
27
|
+
if (!GoogleDrive.drive.accessToken.length) {
|
|
30
28
|
throw new CloudStorageError(
|
|
31
29
|
`Google Drive access token is not set, cannot call function ${prop.toString()}`,
|
|
32
|
-
CloudStorageErrorCode.
|
|
30
|
+
CloudStorageErrorCode.GOOGLE_DRIVE_ACCESS_TOKEN_MISSING
|
|
33
31
|
);
|
|
34
32
|
}
|
|
35
33
|
}
|
|
@@ -39,10 +37,37 @@ export default class GoogleDrive implements NativeproviderService {
|
|
|
39
37
|
});
|
|
40
38
|
}
|
|
41
39
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
40
|
+
// when setting accessToken, set it on the GDrive instance
|
|
41
|
+
public static set accessToken(accessToken: string | null) {
|
|
42
|
+
GoogleDrive.drive.accessToken = accessToken ?? '';
|
|
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;
|
|
46
71
|
|
|
47
72
|
private getRootDirectory(scope: NativeRNCloudCloudStorageScope): GoogleDriveFileSpace {
|
|
48
73
|
switch (scope) {
|
|
@@ -116,7 +141,7 @@ export default class GoogleDrive implements NativeproviderService {
|
|
|
116
141
|
* @returns A promise that resolves to the ID of the root directory or null if it could not be found.
|
|
117
142
|
*/
|
|
118
143
|
private async getRootDirectoryId(scope: NativeRNCloudCloudStorageScope): Promise<string | null> {
|
|
119
|
-
const files = await
|
|
144
|
+
const files = await GoogleDrive.drive.listFiles(this.getRootDirectory(scope));
|
|
120
145
|
for (const file of files) {
|
|
121
146
|
if (!files.find((f) => f.id === file.parents![0])) return file.parents![0] ?? null;
|
|
122
147
|
}
|
|
@@ -130,8 +155,6 @@ export default class GoogleDrive implements NativeproviderService {
|
|
|
130
155
|
filename: string,
|
|
131
156
|
parentDirectoryId: string | null
|
|
132
157
|
) {
|
|
133
|
-
const { strictFilenames } = this.options;
|
|
134
|
-
|
|
135
158
|
let possibleFiles: GoogleDriveFile[];
|
|
136
159
|
if (parentDirectoryId) {
|
|
137
160
|
possibleFiles = files.filter((f) => f.name === filename && f.parents![0] === parentDirectoryId);
|
|
@@ -141,11 +164,13 @@ export default class GoogleDrive implements NativeproviderService {
|
|
|
141
164
|
|
|
142
165
|
if (possibleFiles.length <= 1) return;
|
|
143
166
|
|
|
144
|
-
if (
|
|
167
|
+
if (GoogleDrive.throwOnFilesWithSameName) {
|
|
145
168
|
throw new CloudStorageError(
|
|
146
169
|
`Multiple files with the same name found at path ${path}: ${possibleFiles.map((f) => f.id).join(', ')}`,
|
|
147
170
|
CloudStorageErrorCode.MULTIPLE_FILES_SAME_NAME
|
|
148
171
|
);
|
|
172
|
+
} else {
|
|
173
|
+
this.filesWithSameNameSubscribers.forEach((s) => s({ path, fileIds: possibleFiles.map((f) => f.id) }));
|
|
149
174
|
}
|
|
150
175
|
}
|
|
151
176
|
|
|
@@ -155,7 +180,7 @@ export default class GoogleDrive implements NativeproviderService {
|
|
|
155
180
|
throwIf: 'directory' | 'file' | false = false
|
|
156
181
|
): Promise<string> {
|
|
157
182
|
try {
|
|
158
|
-
const files = await
|
|
183
|
+
const files = await GoogleDrive.drive.listFiles(this.getRootDirectory(scope));
|
|
159
184
|
|
|
160
185
|
if (path === '' || path === '/') {
|
|
161
186
|
const rootDirectoryId = await this.getRootDirectoryId(scope);
|
|
@@ -215,7 +240,7 @@ export default class GoogleDrive implements NativeproviderService {
|
|
|
215
240
|
let prevContent = '';
|
|
216
241
|
try {
|
|
217
242
|
fileId = await this.getFileId(path, scope);
|
|
218
|
-
prevContent = await
|
|
243
|
+
prevContent = await GoogleDrive.drive.getFileText(fileId);
|
|
219
244
|
} catch (e: any) {
|
|
220
245
|
if (e instanceof CloudStorageError && e.code === CloudStorageErrorCode.FILE_NOT_FOUND) {
|
|
221
246
|
/* do nothing, simply create the file */
|
|
@@ -225,15 +250,15 @@ export default class GoogleDrive implements NativeproviderService {
|
|
|
225
250
|
}
|
|
226
251
|
|
|
227
252
|
if (fileId) {
|
|
228
|
-
await
|
|
253
|
+
await GoogleDrive.drive.updateFile(fileId, {
|
|
229
254
|
body: prevContent + data,
|
|
230
255
|
mimeType: MimeTypes.TEXT,
|
|
231
256
|
});
|
|
232
257
|
} else {
|
|
233
|
-
const files = await
|
|
258
|
+
const files = await GoogleDrive.drive.listFiles(this.getRootDirectory(scope));
|
|
234
259
|
const { directories, filename } = this.resolvePathToDirectories(path);
|
|
235
260
|
const parentDirectoryId = this.findParentDirectoryId(files, directories);
|
|
236
|
-
await
|
|
261
|
+
await GoogleDrive.drive.createFile(
|
|
237
262
|
{
|
|
238
263
|
name: filename,
|
|
239
264
|
parents: parentDirectoryId
|
|
@@ -281,15 +306,15 @@ export default class GoogleDrive implements NativeproviderService {
|
|
|
281
306
|
}
|
|
282
307
|
|
|
283
308
|
if (fileId) {
|
|
284
|
-
await
|
|
309
|
+
await GoogleDrive.drive.updateFile(fileId, {
|
|
285
310
|
body: data,
|
|
286
311
|
mimeType: MimeTypes.TEXT,
|
|
287
312
|
});
|
|
288
313
|
} else {
|
|
289
|
-
const files = await
|
|
314
|
+
const files = await GoogleDrive.drive.listFiles(this.getRootDirectory(scope));
|
|
290
315
|
const { directories, filename } = this.resolvePathToDirectories(path);
|
|
291
316
|
const parentDirectoryId = this.findParentDirectoryId(files, directories);
|
|
292
|
-
await
|
|
317
|
+
await GoogleDrive.drive.createFile(
|
|
293
318
|
{
|
|
294
319
|
name: filename,
|
|
295
320
|
parents: parentDirectoryId
|
|
@@ -307,7 +332,7 @@ export default class GoogleDrive implements NativeproviderService {
|
|
|
307
332
|
}
|
|
308
333
|
|
|
309
334
|
async listFiles(path: string, scope: NativeRNCloudCloudStorageScope): Promise<string[]> {
|
|
310
|
-
const allFiles = await
|
|
335
|
+
const allFiles = await GoogleDrive.drive.listFiles(this.getRootDirectory(scope));
|
|
311
336
|
if (path !== '') {
|
|
312
337
|
const fileId = await this.getFileId(path, scope);
|
|
313
338
|
const files = allFiles.filter((f) => (f.parents ?? [])[0] === fileId);
|
|
@@ -333,11 +358,11 @@ export default class GoogleDrive implements NativeproviderService {
|
|
|
333
358
|
}
|
|
334
359
|
}
|
|
335
360
|
|
|
336
|
-
const files = await
|
|
361
|
+
const files = await GoogleDrive.drive.listFiles(this.getRootDirectory(scope));
|
|
337
362
|
const { directories, filename } = this.resolvePathToDirectories(path);
|
|
338
363
|
const parentDirectoryId = this.findParentDirectoryId(files, directories);
|
|
339
364
|
|
|
340
|
-
await
|
|
365
|
+
await GoogleDrive.drive.createDirectory({
|
|
341
366
|
name: filename,
|
|
342
367
|
parents: parentDirectoryId
|
|
343
368
|
? [parentDirectoryId]
|
|
@@ -349,19 +374,19 @@ export default class GoogleDrive implements NativeproviderService {
|
|
|
349
374
|
|
|
350
375
|
async readFile(path: string, scope: NativeRNCloudCloudStorageScope): Promise<string> {
|
|
351
376
|
const fileId = await this.getFileId(path, scope);
|
|
352
|
-
const content = await
|
|
377
|
+
const content = await GoogleDrive.drive.getFileText(fileId);
|
|
353
378
|
return content;
|
|
354
379
|
}
|
|
355
380
|
|
|
356
381
|
async downloadFile(_path: string, _scope: NativeRNCloudCloudStorageScope): Promise<void> {
|
|
357
|
-
//
|
|
382
|
+
// Not doing anything here, just a placeholder to conform to the interface so it doesn't fail on Android
|
|
358
383
|
return;
|
|
359
384
|
}
|
|
360
385
|
|
|
361
386
|
async deleteFile(path: string, scope: NativeRNCloudCloudStorageScope): Promise<void> {
|
|
362
387
|
// if trying to pass a directory, throw an error
|
|
363
388
|
const fileId = await this.getFileId(path, scope, 'directory');
|
|
364
|
-
await
|
|
389
|
+
await GoogleDrive.drive.deleteFile(fileId);
|
|
365
390
|
}
|
|
366
391
|
|
|
367
392
|
async deleteDirectory(path: string, recursive: boolean, scope: NativeRNCloudCloudStorageScope): Promise<void> {
|
|
@@ -370,7 +395,7 @@ export default class GoogleDrive implements NativeproviderService {
|
|
|
370
395
|
|
|
371
396
|
if (!recursive) {
|
|
372
397
|
// check if the directory is empty
|
|
373
|
-
const files = await
|
|
398
|
+
const files = await GoogleDrive.drive.listFiles(this.getRootDirectory(scope));
|
|
374
399
|
const filesInDirectory = files.filter((f) => (f.parents ?? [])[0] === fileId);
|
|
375
400
|
if (filesInDirectory.length > 0) {
|
|
376
401
|
throw new CloudStorageError(
|
|
@@ -381,12 +406,12 @@ export default class GoogleDrive implements NativeproviderService {
|
|
|
381
406
|
}
|
|
382
407
|
}
|
|
383
408
|
|
|
384
|
-
await
|
|
409
|
+
await GoogleDrive.drive.deleteFile(fileId);
|
|
385
410
|
}
|
|
386
411
|
|
|
387
412
|
async statFile(path: string, scope: NativeRNCloudCloudStorageScope): Promise<NativeRNCloudCloudStorageFileStat> {
|
|
388
413
|
const fileId = await this.getFileId(path, scope, false);
|
|
389
|
-
const file = await
|
|
414
|
+
const file = await GoogleDrive.drive.getFile(fileId!);
|
|
390
415
|
|
|
391
416
|
return {
|
|
392
417
|
size: file.size ?? 0,
|
|
@@ -5,22 +5,20 @@ 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.
|
|
9
|
-
* @param cloudStorageInstance An optional instance of RNCloudStorage to use instead of the default instance.
|
|
8
|
+
* @param scope The directory scope the path is in. If not provided, defaults to the default scope set in the library.
|
|
10
9
|
* @returns An object containing the file's contents and functions for downloading, reading, writing, and removing the file.
|
|
11
10
|
*/
|
|
12
|
-
export const useCloudFile = (path: string, scope?: CloudStorageScope
|
|
11
|
+
export const useCloudFile = (path: string, scope?: CloudStorageScope) => {
|
|
13
12
|
const [content, setContent] = useState<string | null>(null);
|
|
14
|
-
const instance = cloudStorageInstance ?? RNCloudStorage;
|
|
15
13
|
|
|
16
14
|
const read = useCallback(async () => {
|
|
17
|
-
const exists = await
|
|
15
|
+
const exists = await RNCloudStorage.exists(path, scope);
|
|
18
16
|
if (!exists) {
|
|
19
17
|
setContent(null);
|
|
20
18
|
return;
|
|
21
19
|
}
|
|
22
|
-
|
|
23
|
-
}, [path, scope
|
|
20
|
+
RNCloudStorage.readFile(path, scope).then(setContent);
|
|
21
|
+
}, [path, scope]);
|
|
24
22
|
|
|
25
23
|
useEffect(() => {
|
|
26
24
|
read();
|
|
@@ -28,26 +26,31 @@ export const useCloudFile = (path: string, scope?: CloudStorageScope, cloudStora
|
|
|
28
26
|
|
|
29
27
|
const write = useCallback(
|
|
30
28
|
async (newContent: string) => {
|
|
31
|
-
await
|
|
29
|
+
await RNCloudStorage.writeFile(path, newContent, scope);
|
|
32
30
|
read();
|
|
33
31
|
},
|
|
34
|
-
[path, scope, read
|
|
32
|
+
[path, scope, read]
|
|
35
33
|
);
|
|
36
34
|
|
|
37
35
|
const remove = useCallback(async () => {
|
|
38
|
-
await
|
|
36
|
+
await RNCloudStorage.unlink(path, scope);
|
|
39
37
|
setContent(null);
|
|
40
|
-
}, [path, scope
|
|
38
|
+
}, [path, scope]);
|
|
41
39
|
|
|
42
40
|
const download = useCallback(async () => {
|
|
43
|
-
await
|
|
44
|
-
}, [path, scope
|
|
41
|
+
await RNCloudStorage.downloadFile(path, scope);
|
|
42
|
+
}, [path, scope]);
|
|
45
43
|
|
|
46
44
|
return {
|
|
47
45
|
content,
|
|
48
46
|
read,
|
|
49
47
|
write,
|
|
50
48
|
remove,
|
|
49
|
+
/**
|
|
50
|
+
* @deprecated Use `write` instead.
|
|
51
|
+
* @alias write
|
|
52
|
+
*/
|
|
53
|
+
update: write,
|
|
51
54
|
/**
|
|
52
55
|
* Downloads the file from iCloud to the device. Needed if the file hasn't been synced yet. Has no effect on
|
|
53
56
|
* Google Drive.
|
|
@@ -1,30 +1,43 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { NativeEventEmitter, NativeModules, Platform, DeviceEventEmitter } from 'react-native';
|
|
2
3
|
import RNCloudStorage from '../RNCloudStorage';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* A hook that tests whether or not the cloud storage is available.
|
|
6
|
-
* @param
|
|
7
|
+
* @param _iCloudTimeout DEPRECATED: This parameter is deprecated and has no effect. It will be removed in a future version.
|
|
7
8
|
* @returns A boolean indicating whether or not the cloud storage is available.
|
|
8
9
|
*/
|
|
9
|
-
export const useIsCloudAvailable = (
|
|
10
|
+
export const useIsCloudAvailable = (_iCloudTimeout?: number) => {
|
|
10
11
|
const [isAvailable, setIsAvailable] = useState(false);
|
|
11
|
-
const instance = cloudStorageInstance ?? RNCloudStorage.getDefaultInstance();
|
|
12
|
-
|
|
13
|
-
const handleAvailabilityChange = useCallback((available: boolean) => {
|
|
14
|
-
setIsAvailable(available);
|
|
15
|
-
}, []);
|
|
16
12
|
|
|
17
13
|
useEffect(() => {
|
|
18
14
|
// Set the initial availability state
|
|
19
|
-
|
|
15
|
+
RNCloudStorage.isCloudAvailable().then(setIsAvailable);
|
|
20
16
|
|
|
21
17
|
// Listen for changes to the cloud availability using the native event emitter
|
|
22
|
-
|
|
18
|
+
let eventEmitter: NativeEventEmitter | typeof DeviceEventEmitter;
|
|
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
|
+
});
|
|
23
28
|
|
|
24
29
|
return () => {
|
|
25
|
-
|
|
30
|
+
eventEmitter.removeAllListeners('RNCloudStorage.cloud.availability-changed');
|
|
26
31
|
};
|
|
27
|
-
}, [
|
|
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]);
|
|
28
41
|
|
|
29
42
|
return isAvailable;
|
|
30
43
|
};
|
package/src/index.ts
CHANGED
|
@@ -6,3 +6,8 @@ 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,8 +1,3 @@
|
|
|
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
|
-
|
|
6
1
|
export enum CloudStorageScope {
|
|
7
2
|
Documents = 'documents',
|
|
8
3
|
AppData = 'app_data',
|
|
@@ -17,36 +12,3 @@ export interface CloudStorageFileStat {
|
|
|
17
12
|
isDirectory: () => boolean;
|
|
18
13
|
isFile: () => boolean;
|
|
19
14
|
}
|
|
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,7 +9,6 @@ export interface NativeRNCloudCloudStorageFileStat {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export enum CloudStorageErrorCode {
|
|
12
|
-
INVALID_SCOPE = 'ERR_INVALID_SCOPE',
|
|
13
12
|
FILE_NOT_FOUND = 'ERR_FILE_NOT_FOUND',
|
|
14
13
|
PATH_IS_FILE = 'ERR_PATH_IS_FILE',
|
|
15
14
|
PATH_IS_DIRECTORY = 'ERR_PATH_IS_DIRECTORY',
|
|
@@ -24,7 +23,7 @@ export enum CloudStorageErrorCode {
|
|
|
24
23
|
STAT_ERROR = 'ERR_STAT_ERROR',
|
|
25
24
|
UNKNOWN = 'ERR_UNKNOWN',
|
|
26
25
|
FILE_NOT_DOWNLOADABLE = 'ERR_FILE_NOT_DOWNLOADABLE',
|
|
27
|
-
|
|
26
|
+
GOOGLE_DRIVE_ACCESS_TOKEN_MISSING = 'ERR_GOOGLE_DRIVE_ACCESS_TOKEN_MISSING',
|
|
28
27
|
}
|
|
29
28
|
|
|
30
29
|
export default interface NativeRNCloudStorage {
|
package/src/utils/helpers.ts
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Checks if the path starts with a leading slash and adds one if it doesn't to maintain backwards compatibility.
|
|
3
|
+
* Will log a warning to the console if it had to add a leading slash. Will throw an error in the future.
|
|
4
|
+
*
|
|
5
|
+
* @param path The path to check.
|
|
6
|
+
* @returns The path with a leading slash, if it didn't have one already.
|
|
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}`;
|
|
7
15
|
}
|
|
8
|
-
|
|
9
|
-
return true;
|
|
16
|
+
return path;
|
|
10
17
|
};
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// CloudKitUtils.swift
|
|
3
|
-
// CloudStorage
|
|
4
|
-
//
|
|
5
|
-
// Created by Maximilian Krause on 27.09.24.
|
|
6
|
-
// Copyright © 2024 Kuatsu App Agency. All rights reserved.
|
|
7
|
-
//
|
|
8
|
-
|
|
9
|
-
import Foundation
|
|
10
|
-
|
|
11
|
-
enum CloudKitUtils {
|
|
12
|
-
private static let fileManager = FileManager.default
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
Checks if the CloudKit service is available.
|
|
16
|
-
|
|
17
|
-
- Returns: True if the CloudKit service is available, false otherwise.
|
|
18
|
-
*/
|
|
19
|
-
static func isCloudKitAvailable() -> Bool {
|
|
20
|
-
fileManager.ubiquityIdentityToken != nil
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
Downloads a file from iCloud.
|
|
25
|
-
|
|
26
|
-
- Parameter fileUrl: The URL of the file to download.
|
|
27
|
-
- Throws: An NSError if the file is not downloadable or the download failed.
|
|
28
|
-
*/
|
|
29
|
-
static func downloadFile(fileUrl: URL) throws {
|
|
30
|
-
let isDownloadable = fileManager.isUbiquitousItem(at: fileUrl)
|
|
31
|
-
|
|
32
|
-
if !isDownloadable {
|
|
33
|
-
throw CloudStorageError.fileNotDownloadable(path: fileUrl.path)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
do {
|
|
37
|
-
// trigger download of file
|
|
38
|
-
try fileManager.startDownloadingUbiquitousItem(at: fileUrl)
|
|
39
|
-
} catch {
|
|
40
|
-
throw CloudStorageError.fileNotDownloadable(path: fileUrl.path)
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
Returns the iCloud directory URL for the given scope.
|
|
46
|
-
|
|
47
|
-
- Parameter scope: The scope of the directory.
|
|
48
|
-
- Returns: The URL of the iCloud directory, or nil if no directory is found.
|
|
49
|
-
*/
|
|
50
|
-
private static func getScopeDirectory(scope: DirectoryScope) -> URL? {
|
|
51
|
-
switch scope {
|
|
52
|
-
case .appData:
|
|
53
|
-
appDataDirectory
|
|
54
|
-
case .documents:
|
|
55
|
-
documentsDirectory
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
Parses a given path and directory scope to a full file URL.
|
|
61
|
-
|
|
62
|
-
- Parameter path: The path of the file.
|
|
63
|
-
- Parameter scope: The scope of the directory.
|
|
64
|
-
- Parameter shouldExist: Whether the file should exist. If true, throws an error if the file does not exist. If false, throws an error if the file exists. If nil, does not check if the file exists.
|
|
65
|
-
- Returns: The full URL of the file.
|
|
66
|
-
- Throws: An NSError if the scope directory couldn't be found or the file should exist but doesn't or vice versa.
|
|
67
|
-
*/
|
|
68
|
-
static func getFileURL(path: String, scope: DirectoryScope, _ shouldExist: Bool? = nil) throws -> URL {
|
|
69
|
-
guard let directory = getScopeDirectory(scope: scope) else {
|
|
70
|
-
throw CloudStorageError.directoryNotFound(path: path)
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// append path to scope directory
|
|
74
|
-
let fileUrl = directory.appendingPathComponent(FileUtils.sanitizePath(path: path))
|
|
75
|
-
|
|
76
|
-
if shouldExist != nil {
|
|
77
|
-
let fileExists = try FileUtils.checkFileExists(fileUrl: fileUrl)
|
|
78
|
-
if shouldExist! && !fileExists {
|
|
79
|
-
throw CloudStorageError.fileNotFound(path: path)
|
|
80
|
-
} else if !shouldExist! && fileExists {
|
|
81
|
-
throw CloudStorageError.fileAlreadyExists(path: path)
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return fileUrl
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
Parses a given path and unchecked directory scope to a full file URL.
|
|
90
|
-
|
|
91
|
-
- Parameter path: The path of the file.
|
|
92
|
-
- Parameter scope: The scope of the directory. Will be checked for validity.
|
|
93
|
-
- Parameter shouldExist: Whether the file should exist. If true, throws an error if the file does not exist. If false, throws an error if the file exists. If nil, does not check if the file exists.
|
|
94
|
-
- Returns: The full URL of the file.
|
|
95
|
-
- Throws: An NSError if the scope directory couldn't be found or the file should exist but doesn't or vice versa.
|
|
96
|
-
*/
|
|
97
|
-
static func getFileURL(path: String, scope: String, _ shouldExist: Bool? = nil) throws -> URL {
|
|
98
|
-
guard let directoryScope = DirectoryScope(rawValue: scope) else {
|
|
99
|
-
throw CloudStorageError.invalidScope(scope: scope)
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
return try getFileURL(path: path, scope: directoryScope, shouldExist)
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
static var appDataDirectory: URL? {
|
|
106
|
-
fileManager.url(forUbiquityContainerIdentifier: nil)
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
static var documentsDirectory: URL? {
|
|
110
|
-
fileManager.urls(for: .documentDirectory, in: .userDomainMask).first
|
|
111
|
-
}
|
|
112
|
-
}
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// CloudStorageError.swift
|
|
3
|
-
// CloudStorage
|
|
4
|
-
//
|
|
5
|
-
// Created by Maximilian Krause on 27.09.24.
|
|
6
|
-
// Copyright © 2024 Kuatsu App Agency. All rights reserved.
|
|
7
|
-
//
|
|
8
|
-
|
|
9
|
-
import Foundation
|
|
10
|
-
|
|
11
|
-
enum CloudStorageError: Error {
|
|
12
|
-
case invalidScope(scope: String)
|
|
13
|
-
case fileNotFound(path: String)
|
|
14
|
-
case pathIsDirectory(path: String)
|
|
15
|
-
case pathIsFile(path: String)
|
|
16
|
-
case directoryNotFound(path: String)
|
|
17
|
-
case directoryNotEmpty(path: String)
|
|
18
|
-
case fileAlreadyExists(path: String)
|
|
19
|
-
case authenticationFailed
|
|
20
|
-
case writeError(path: String)
|
|
21
|
-
case readError(path: String)
|
|
22
|
-
case deleteError(path: String)
|
|
23
|
-
case statError(path: String)
|
|
24
|
-
case unknown(message: String = "An unknown error occurred")
|
|
25
|
-
case fileNotDownloadable(path: String)
|
|
26
|
-
|
|
27
|
-
var code: String {
|
|
28
|
-
switch self {
|
|
29
|
-
case .invalidScope: "ERR_INVALID_SCOPE"
|
|
30
|
-
case .fileNotFound: "ERR_FILE_NOT_FOUND"
|
|
31
|
-
case .pathIsDirectory: "ERR_PATH_IS_DIRECTORY"
|
|
32
|
-
case .pathIsFile: "ERR_PATH_IS_FILE"
|
|
33
|
-
case .directoryNotFound: "ERR_DIRECTORY_NOT_FOUND"
|
|
34
|
-
case .directoryNotEmpty: "ERR_DIRECTORY_NOT_EMPTY"
|
|
35
|
-
case .fileAlreadyExists: "ERR_FILE_EXISTS"
|
|
36
|
-
case .authenticationFailed: "ERR_AUTHENTICATION_FAILED"
|
|
37
|
-
case .writeError: "ERR_WRITE_ERROR"
|
|
38
|
-
case .readError: "ERR_READ_ERROR"
|
|
39
|
-
case .deleteError: "ERR_DELETE_ERROR"
|
|
40
|
-
case .statError: "ERR_STAT_ERROR"
|
|
41
|
-
case .unknown: "ERR_UNKNOWN"
|
|
42
|
-
case .fileNotDownloadable: "ERR_FILE_NOT_DOWNLOADABLE"
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
var message: String {
|
|
47
|
-
switch self {
|
|
48
|
-
case let .invalidScope(scope):
|
|
49
|
-
"Invalid scope \(scope) provided"
|
|
50
|
-
case let .fileNotFound(path):
|
|
51
|
-
"File not found at path \(path)"
|
|
52
|
-
case let .pathIsDirectory(path):
|
|
53
|
-
"Path is a directory at path \(path)"
|
|
54
|
-
case let .pathIsFile(path):
|
|
55
|
-
"Path is a file at path \(path)"
|
|
56
|
-
case let .directoryNotFound(path):
|
|
57
|
-
"Directory not found at path \(path)"
|
|
58
|
-
case let .directoryNotEmpty(path):
|
|
59
|
-
"Directory not empty at path \(path)"
|
|
60
|
-
case let .fileAlreadyExists(path):
|
|
61
|
-
"File already exists at path \(path)"
|
|
62
|
-
case .authenticationFailed:
|
|
63
|
-
"Authentication failed"
|
|
64
|
-
case let .writeError(path):
|
|
65
|
-
"Write error for path \(path)"
|
|
66
|
-
case let .readError(path):
|
|
67
|
-
"Read error for path \(path)"
|
|
68
|
-
case let .deleteError(path):
|
|
69
|
-
"Delete error for path \(path)"
|
|
70
|
-
case let .statError(path):
|
|
71
|
-
"Stat error for path \(path)"
|
|
72
|
-
case let .unknown(message):
|
|
73
|
-
message
|
|
74
|
-
case let .fileNotDownloadable(path):
|
|
75
|
-
"File not downloadable at path \(path)"
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|