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.
Files changed (106) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +5 -6
  3. package/ios/CloudStorage.swift +262 -52
  4. package/ios/CloudStorage.xcodeproj/project.pbxproj +0 -28
  5. package/ios/CloudStorage.xcodeproj/project.xcworkspace/contents.xcworkspacedata +4 -0
  6. package/ios/CloudStorage.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  7. package/ios/CloudStorage.xcodeproj/project.xcworkspace/xcuserdata/max.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  8. package/ios/CloudStorage.xcodeproj/xcuserdata/max.xcuserdatad/xcschemes/xcschememanagement.plist +22 -0
  9. package/ios/CloudStorageEventEmitter.swift +4 -4
  10. package/lib/commonjs/RNCloudStorage.js +66 -361
  11. package/lib/commonjs/RNCloudStorage.js.map +1 -1
  12. package/lib/commonjs/createRNCloudStorage.js +48 -0
  13. package/lib/commonjs/createRNCloudStorage.js.map +1 -0
  14. package/lib/commonjs/expo-plugin/types/index.js.map +1 -1
  15. package/lib/commonjs/expo-plugin/withRNCloudStorage.js +3 -2
  16. package/lib/commonjs/expo-plugin/withRNCloudStorage.js.map +1 -1
  17. package/lib/commonjs/expo-plugin/withRNCloudStorageIos.js +7 -4
  18. package/lib/commonjs/expo-plugin/withRNCloudStorageIos.js.map +1 -1
  19. package/lib/commonjs/google-drive/client.js +20 -16
  20. package/lib/commonjs/google-drive/client.js.map +1 -1
  21. package/lib/commonjs/google-drive/index.js +64 -42
  22. package/lib/commonjs/google-drive/index.js.map +1 -1
  23. package/lib/commonjs/google-drive/types.js +2 -1
  24. package/lib/commonjs/google-drive/types.js.map +1 -1
  25. package/lib/commonjs/hooks/useCloudFile.js +17 -14
  26. package/lib/commonjs/hooks/useCloudFile.js.map +1 -1
  27. package/lib/commonjs/hooks/useIsCloudAvailable.js +21 -11
  28. package/lib/commonjs/hooks/useIsCloudAvailable.js.map +1 -1
  29. package/lib/commonjs/index.js +7 -1
  30. package/lib/commonjs/index.js.map +1 -1
  31. package/lib/commonjs/types/main.js +3 -8
  32. package/lib/commonjs/types/main.js.map +1 -1
  33. package/lib/commonjs/types/native.js +3 -3
  34. package/lib/commonjs/types/native.js.map +1 -1
  35. package/lib/commonjs/utils/CloudStorageError.js +2 -1
  36. package/lib/commonjs/utils/CloudStorageError.js.map +1 -1
  37. package/lib/commonjs/utils/helpers.js +15 -8
  38. package/lib/commonjs/utils/helpers.js.map +1 -1
  39. package/lib/module/RNCloudStorage.js +65 -362
  40. package/lib/module/RNCloudStorage.js.map +1 -1
  41. package/lib/module/createRNCloudStorage.js +41 -0
  42. package/lib/module/createRNCloudStorage.js.map +1 -0
  43. package/lib/module/expo-plugin/types/index.js +1 -1
  44. package/lib/module/expo-plugin/types/index.js.map +1 -1
  45. package/lib/module/expo-plugin/withRNCloudStorage.js +0 -2
  46. package/lib/module/expo-plugin/withRNCloudStorage.js.map +1 -1
  47. package/lib/module/expo-plugin/withRNCloudStorageIos.js +5 -5
  48. package/lib/module/expo-plugin/withRNCloudStorageIos.js.map +1 -1
  49. package/lib/module/google-drive/client.js +20 -18
  50. package/lib/module/google-drive/client.js.map +1 -1
  51. package/lib/module/google-drive/index.js +62 -41
  52. package/lib/module/google-drive/index.js.map +1 -1
  53. package/lib/module/google-drive/types.js +0 -2
  54. package/lib/module/google-drive/types.js.map +1 -1
  55. package/lib/module/hooks/useCloudFile.js +16 -15
  56. package/lib/module/hooks/useCloudFile.js.map +1 -1
  57. package/lib/module/hooks/useIsCloudAvailable.js +21 -13
  58. package/lib/module/hooks/useIsCloudAvailable.js.map +1 -1
  59. package/lib/module/index.js +5 -2
  60. package/lib/module/index.js.map +1 -1
  61. package/lib/module/types/main.js +0 -9
  62. package/lib/module/types/main.js.map +1 -1
  63. package/lib/module/types/native.js +1 -4
  64. package/lib/module/types/native.js.map +1 -1
  65. package/lib/module/utils/CloudStorageError.js +0 -2
  66. package/lib/module/utils/CloudStorageError.js.map +1 -1
  67. package/lib/module/utils/helpers.js +13 -8
  68. package/lib/module/utils/helpers.js.map +1 -1
  69. package/lib/typescript/RNCloudStorage.d.ts +39 -159
  70. package/lib/typescript/RNCloudStorage.d.ts.map +1 -1
  71. package/lib/typescript/createRNCloudStorage.d.ts +3 -0
  72. package/lib/typescript/createRNCloudStorage.d.ts.map +1 -0
  73. package/lib/typescript/google-drive/client.d.ts +3 -3
  74. package/lib/typescript/google-drive/client.d.ts.map +1 -1
  75. package/lib/typescript/google-drive/index.d.ts +18 -6
  76. package/lib/typescript/google-drive/index.d.ts.map +1 -1
  77. package/lib/typescript/hooks/useCloudFile.d.ts +7 -4
  78. package/lib/typescript/hooks/useCloudFile.d.ts.map +1 -1
  79. package/lib/typescript/hooks/useIsCloudAvailable.d.ts +2 -3
  80. package/lib/typescript/hooks/useIsCloudAvailable.d.ts.map +1 -1
  81. package/lib/typescript/index.d.ts +4 -0
  82. package/lib/typescript/index.d.ts.map +1 -1
  83. package/lib/typescript/types/main.d.ts +0 -33
  84. package/lib/typescript/types/main.d.ts.map +1 -1
  85. package/lib/typescript/types/native.d.ts +1 -2
  86. package/lib/typescript/types/native.d.ts.map +1 -1
  87. package/lib/typescript/utils/helpers.d.ts +9 -2
  88. package/lib/typescript/utils/helpers.d.ts.map +1 -1
  89. package/package.json +11 -9
  90. package/src/RNCloudStorage.ts +68 -387
  91. package/src/createRNCloudStorage.ts +53 -0
  92. package/src/google-drive/client.ts +7 -8
  93. package/src/google-drive/index.ts +63 -38
  94. package/src/hooks/useCloudFile.ts +16 -13
  95. package/src/hooks/useIsCloudAvailable.ts +25 -12
  96. package/src/index.ts +5 -0
  97. package/src/types/main.ts +0 -38
  98. package/src/types/native.ts +1 -2
  99. package/src/utils/helpers.ts +15 -8
  100. package/ios/Utils/CloudKitUtils.swift +0 -112
  101. package/ios/Utils/CloudStorageError.swift +0 -78
  102. package/ios/Utils/FileUtils.swift +0 -132
  103. package/ios/Utils/Promise.swift +0 -58
  104. package/ios/Utils/Types.swift +0 -36
  105. package/lib/commonjs/package.json +0 -1
  106. package/lib/module/package.json +0 -1
@@ -1,4 +1,4 @@
1
- import type NativeproviderService from '../types/native';
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 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);
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
- const { accessToken } = options;
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.ACCESS_TOKEN_MISSING
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
- public isCloudAvailable: () => Promise<boolean> = async () => {
43
- const { accessToken } = this.options;
44
- return !!accessToken?.length;
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 this.drive.listFiles(this.getRootDirectory(scope));
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 (strictFilenames) {
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 this.drive.listFiles(this.getRootDirectory(scope));
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 this.drive.getFileText(fileId);
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 this.drive.updateFile(fileId, {
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 this.drive.listFiles(this.getRootDirectory(scope));
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 this.drive.createFile(
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 this.drive.updateFile(fileId, {
309
+ await GoogleDrive.drive.updateFile(fileId, {
285
310
  body: data,
286
311
  mimeType: MimeTypes.TEXT,
287
312
  });
288
313
  } else {
289
- const files = await this.drive.listFiles(this.getRootDirectory(scope));
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 this.drive.createFile(
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 this.drive.listFiles(this.getRootDirectory(scope));
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 this.drive.listFiles(this.getRootDirectory(scope));
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 this.drive.createDirectory({
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 this.drive.getFileText(fileId);
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
- // Downloading files from Google Drive is not necessary / possible, as they need to be downloaded on every read operation via the API anyway
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 this.drive.deleteFile(fileId);
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 this.drive.listFiles(this.getRootDirectory(scope));
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 this.drive.deleteFile(fileId);
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 this.drive.getFile(fileId!);
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. 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.
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, cloudStorageInstance?: RNCloudStorage) => {
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 instance.exists(path, scope);
15
+ const exists = await RNCloudStorage.exists(path, scope);
18
16
  if (!exists) {
19
17
  setContent(null);
20
18
  return;
21
19
  }
22
- instance.readFile(path, scope).then(setContent);
23
- }, [path, scope, instance]);
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 instance.writeFile(path, newContent, scope);
29
+ await RNCloudStorage.writeFile(path, newContent, scope);
32
30
  read();
33
31
  },
34
- [path, scope, read, instance]
32
+ [path, scope, read]
35
33
  );
36
34
 
37
35
  const remove = useCallback(async () => {
38
- await instance.unlink(path, scope);
36
+ await RNCloudStorage.unlink(path, scope);
39
37
  setContent(null);
40
- }, [path, scope, instance]);
38
+ }, [path, scope]);
41
39
 
42
40
  const download = useCallback(async () => {
43
- await instance.downloadFile(path, scope);
44
- }, [path, scope, instance]);
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 { useCallback, useEffect, useState } from 'react';
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 cloudStorageInstance - An optional instance of RNCloudStorage to use instead of the default instance.
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 = (cloudStorageInstance?: RNCloudStorage) => {
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
- instance.isCloudAvailable().then(setIsAvailable);
15
+ RNCloudStorage.isCloudAvailable().then(setIsAvailable);
20
16
 
21
17
  // Listen for changes to the cloud availability using the native event emitter
22
- instance.subscribeToCloudAvailability(handleAvailabilityChange);
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
- instance.unsubscribeFromCloudAvailability(handleAvailabilityChange);
30
+ eventEmitter.removeAllListeners('RNCloudStorage.cloud.availability-changed');
26
31
  };
27
- }, [instance, handleAvailabilityChange]);
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
- }
@@ -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
- ACCESS_TOKEN_MISSING = 'ERR_ACCESS_TOKEN_MISSING',
26
+ GOOGLE_DRIVE_ACCESS_TOKEN_MISSING = 'ERR_GOOGLE_DRIVE_ACCESS_TOKEN_MISSING',
28
27
  }
29
28
 
30
29
  export default interface NativeRNCloudStorage {
@@ -1,10 +1,17 @@
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;
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
- }