react-native-cloud-storage 2.2.1 → 2.3.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.
Files changed (188) hide show
  1. package/LICENSE +1 -1
  2. package/android/build.gradle +101 -0
  3. package/android/gradle.properties +5 -0
  4. package/android/src/main/AndroidManifest.xml +3 -0
  5. package/android/src/main/AndroidManifestNew.xml +2 -0
  6. package/android/src/main/java/com/voicekit/CloudStorageError.kt +86 -0
  7. package/android/src/main/java/com/voicekit/CloudStorageLocalFileSystemModule.kt +236 -0
  8. package/android/src/main/java/com/voicekit/CloudStoragePackage.kt +17 -0
  9. package/android/src/main/java/com/voicekit/FileUtils.kt +41 -0
  10. package/android/src/main/java/com/voicekit/Types.kt +6 -0
  11. package/app.plugin.js +1 -1
  12. package/{lib/commonjs/RNCloudStorage.js → dist/commonjs/cloud-storage.js} +130 -80
  13. package/dist/commonjs/cloud-storage.js.map +1 -0
  14. package/dist/commonjs/expo-plugin/index.js +13 -0
  15. package/dist/commonjs/expo-plugin/index.js.map +1 -0
  16. package/{lib/commonjs/expo-plugin/withRNCloudStorageIos.js → dist/commonjs/expo-plugin/ios.js} +1 -1
  17. package/dist/commonjs/expo-plugin/ios.js.map +1 -0
  18. package/{lib/commonjs/hooks/useCloudFile.js → dist/commonjs/hooks/use-cloud-file.js} +26 -8
  19. package/dist/commonjs/hooks/use-cloud-file.js.map +1 -0
  20. package/{lib/commonjs/hooks/useIsCloudAvailable.js → dist/commonjs/hooks/use-is-cloud-available.js} +3 -3
  21. package/dist/commonjs/hooks/use-is-cloud-available.js.map +1 -0
  22. package/{lib → dist}/commonjs/index.js +10 -10
  23. package/dist/commonjs/index.js.map +1 -0
  24. package/dist/commonjs/storages/cloudkit.js +10 -0
  25. package/dist/commonjs/storages/cloudkit.js.map +1 -0
  26. package/{lib/commonjs → dist/commonjs/storages}/google-drive/client.js +82 -39
  27. package/dist/commonjs/storages/google-drive/client.js.map +1 -0
  28. package/{lib/commonjs → dist/commonjs/storages}/google-drive/index.js +120 -64
  29. package/dist/commonjs/storages/google-drive/index.js.map +1 -0
  30. package/dist/commonjs/storages/google-drive/types.js.map +1 -0
  31. package/dist/commonjs/types/native.js +28 -0
  32. package/dist/commonjs/types/native.js.map +1 -0
  33. package/{lib/commonjs/utils/CloudStorageError.js → dist/commonjs/utils/cloud-storage-error.js} +3 -1
  34. package/dist/commonjs/utils/cloud-storage-error.js.map +1 -0
  35. package/dist/commonjs/utils/constants.js +24 -0
  36. package/dist/commonjs/utils/constants.js.map +1 -0
  37. package/dist/commonjs/utils/local-fs.js +16 -0
  38. package/dist/commonjs/utils/local-fs.js.map +1 -0
  39. package/dist/commonjs/utils/native.js +35 -0
  40. package/dist/commonjs/utils/native.js.map +1 -0
  41. package/{lib/module/RNCloudStorage.js → dist/module/cloud-storage.js} +130 -80
  42. package/dist/module/cloud-storage.js.map +1 -0
  43. package/dist/module/expo-plugin/index.js +8 -0
  44. package/dist/module/expo-plugin/index.js.map +1 -0
  45. package/{lib/module/expo-plugin/withRNCloudStorageIos.js → dist/module/expo-plugin/ios.js} +1 -1
  46. package/dist/module/expo-plugin/ios.js.map +1 -0
  47. package/{lib/module/hooks/useCloudFile.js → dist/module/hooks/use-cloud-file.js} +25 -7
  48. package/dist/module/hooks/use-cloud-file.js.map +1 -0
  49. package/{lib/module/hooks/useIsCloudAvailable.js → dist/module/hooks/use-is-cloud-available.js} +2 -2
  50. package/dist/module/hooks/use-is-cloud-available.js.map +1 -0
  51. package/dist/module/index.js +9 -0
  52. package/dist/module/index.js.map +1 -0
  53. package/dist/module/storages/cloudkit.js +6 -0
  54. package/dist/module/storages/cloudkit.js.map +1 -0
  55. package/{lib/module → dist/module/storages}/google-drive/client.js +82 -39
  56. package/dist/module/storages/google-drive/client.js.map +1 -0
  57. package/{lib/module → dist/module/storages}/google-drive/index.js +119 -62
  58. package/dist/module/storages/google-drive/index.js.map +1 -0
  59. package/dist/module/storages/google-drive/types.js.map +1 -0
  60. package/dist/module/types/native.js +24 -0
  61. package/dist/module/types/native.js.map +1 -0
  62. package/{lib/module/utils/CloudStorageError.js → dist/module/utils/cloud-storage-error.js} +3 -1
  63. package/dist/module/utils/cloud-storage-error.js.map +1 -0
  64. package/dist/module/utils/constants.js +20 -0
  65. package/dist/module/utils/constants.js.map +1 -0
  66. package/dist/module/utils/local-fs.js +12 -0
  67. package/dist/module/utils/local-fs.js.map +1 -0
  68. package/dist/module/utils/native.js +30 -0
  69. package/dist/module/utils/native.js.map +1 -0
  70. package/{lib/typescript/RNCloudStorage.d.ts → dist/typescript/cloud-storage.d.ts} +60 -8
  71. package/dist/typescript/cloud-storage.d.ts.map +1 -0
  72. package/{lib/typescript/expo-plugin/withRNCloudStorage.d.ts → dist/typescript/expo-plugin/index.d.ts} +1 -1
  73. package/dist/typescript/expo-plugin/index.d.ts.map +1 -0
  74. package/{lib/typescript/expo-plugin/withRNCloudStorageIos.d.ts → dist/typescript/expo-plugin/ios.d.ts} +1 -1
  75. package/dist/typescript/expo-plugin/ios.d.ts.map +1 -0
  76. package/{lib/typescript/hooks/useCloudFile.d.ts → dist/typescript/hooks/use-cloud-file.d.ts} +22 -4
  77. package/dist/typescript/hooks/use-cloud-file.d.ts.map +1 -0
  78. package/{lib/typescript/hooks/useIsCloudAvailable.d.ts → dist/typescript/hooks/use-is-cloud-available.d.ts} +2 -2
  79. package/dist/typescript/hooks/use-is-cloud-available.d.ts.map +1 -0
  80. package/dist/typescript/index.d.ts +7 -0
  81. package/dist/typescript/index.d.ts.map +1 -0
  82. package/dist/typescript/storages/cloudkit.d.ts +3 -0
  83. package/dist/typescript/storages/cloudkit.d.ts.map +1 -0
  84. package/{lib/typescript → dist/typescript/storages}/google-drive/client.d.ts +9 -2
  85. package/dist/typescript/storages/google-drive/client.d.ts.map +1 -0
  86. package/dist/typescript/storages/google-drive/index.d.ts +35 -0
  87. package/dist/typescript/storages/google-drive/index.d.ts.map +1 -0
  88. package/dist/typescript/storages/google-drive/types.d.ts.map +1 -0
  89. package/dist/typescript/types/native.d.ts +64 -0
  90. package/dist/typescript/types/native.d.ts.map +1 -0
  91. package/dist/typescript/utils/cloud-storage-error.d.ts +8 -0
  92. package/dist/typescript/utils/cloud-storage-error.d.ts.map +1 -0
  93. package/dist/typescript/utils/constants.d.ts +4 -0
  94. package/dist/typescript/utils/constants.d.ts.map +1 -0
  95. package/{lib → dist}/typescript/utils/helpers.d.ts.map +1 -1
  96. package/dist/typescript/utils/local-fs.d.ts +3 -0
  97. package/dist/typescript/utils/local-fs.d.ts.map +1 -0
  98. package/dist/typescript/utils/native.d.ts +7 -0
  99. package/dist/typescript/utils/native.d.ts.map +1 -0
  100. package/ios/CloudStorage.xcodeproj/project.pbxproj +12 -6
  101. package/ios/{CloudStorage.m → CloudStorageCloudKit.m} +4 -2
  102. package/ios/{CloudStorage.swift → CloudStorageCloudKit.swift} +60 -5
  103. package/ios/CloudStorageLocalFileSystem.m +15 -0
  104. package/ios/CloudStorageLocalFileSystem.swift +215 -0
  105. package/ios/Utils/CloudKitUtils.swift +4 -4
  106. package/ios/Utils/CloudStorageError.swift +8 -0
  107. package/ios/Utils/FileUtils.swift +19 -0
  108. package/ios/Utils/Types.swift +7 -1
  109. package/package.json +44 -103
  110. package/src/{RNCloudStorage.ts → cloud-storage.ts} +147 -85
  111. package/src/expo-plugin/{withRNCloudStorage.ts → index.ts} +2 -2
  112. package/src/hooks/{useCloudFile.ts → use-cloud-file.ts} +24 -6
  113. package/src/hooks/{useIsCloudAvailable.ts → use-is-cloud-available.ts} +1 -1
  114. package/src/index.ts +5 -6
  115. package/src/storages/cloudkit.ts +5 -0
  116. package/src/{google-drive → storages/google-drive}/client.ts +99 -40
  117. package/src/{google-drive → storages/google-drive}/index.ts +172 -89
  118. package/src/types/native.ts +50 -14
  119. package/src/utils/cloud-storage-error.ts +15 -0
  120. package/src/utils/constants.ts +20 -0
  121. package/src/utils/local-fs.ts +19 -0
  122. package/src/utils/native.ts +40 -0
  123. package/lib/commonjs/RNCloudStorage.js.map +0 -1
  124. package/lib/commonjs/expo-plugin/withRNCloudStorage.js +0 -13
  125. package/lib/commonjs/expo-plugin/withRNCloudStorage.js.map +0 -1
  126. package/lib/commonjs/expo-plugin/withRNCloudStorageIos.js.map +0 -1
  127. package/lib/commonjs/google-drive/client.js.map +0 -1
  128. package/lib/commonjs/google-drive/index.js.map +0 -1
  129. package/lib/commonjs/google-drive/types.js.map +0 -1
  130. package/lib/commonjs/hooks/useCloudFile.js.map +0 -1
  131. package/lib/commonjs/hooks/useIsCloudAvailable.js.map +0 -1
  132. package/lib/commonjs/index.js.map +0 -1
  133. package/lib/commonjs/types/native.js +0 -26
  134. package/lib/commonjs/types/native.js.map +0 -1
  135. package/lib/commonjs/utils/CloudStorageError.js.map +0 -1
  136. package/lib/module/RNCloudStorage.js.map +0 -1
  137. package/lib/module/expo-plugin/withRNCloudStorage.js +0 -8
  138. package/lib/module/expo-plugin/withRNCloudStorage.js.map +0 -1
  139. package/lib/module/expo-plugin/withRNCloudStorageIos.js.map +0 -1
  140. package/lib/module/google-drive/client.js.map +0 -1
  141. package/lib/module/google-drive/index.js.map +0 -1
  142. package/lib/module/google-drive/types.js.map +0 -1
  143. package/lib/module/hooks/useCloudFile.js.map +0 -1
  144. package/lib/module/hooks/useIsCloudAvailable.js.map +0 -1
  145. package/lib/module/index.js +0 -10
  146. package/lib/module/index.js.map +0 -1
  147. package/lib/module/types/native.js +0 -22
  148. package/lib/module/types/native.js.map +0 -1
  149. package/lib/module/utils/CloudStorageError.js.map +0 -1
  150. package/lib/typescript/RNCloudStorage.d.ts.map +0 -1
  151. package/lib/typescript/expo-plugin/withRNCloudStorage.d.ts.map +0 -1
  152. package/lib/typescript/expo-plugin/withRNCloudStorageIos.d.ts.map +0 -1
  153. package/lib/typescript/google-drive/client.d.ts.map +0 -1
  154. package/lib/typescript/google-drive/index.d.ts +0 -34
  155. package/lib/typescript/google-drive/index.d.ts.map +0 -1
  156. package/lib/typescript/google-drive/types.d.ts.map +0 -1
  157. package/lib/typescript/hooks/useCloudFile.d.ts.map +0 -1
  158. package/lib/typescript/hooks/useIsCloudAvailable.d.ts.map +0 -1
  159. package/lib/typescript/index.d.ts +0 -8
  160. package/lib/typescript/index.d.ts.map +0 -1
  161. package/lib/typescript/types/native.d.ts +0 -40
  162. package/lib/typescript/types/native.d.ts.map +0 -1
  163. package/lib/typescript/utils/CloudStorageError.d.ts +0 -8
  164. package/lib/typescript/utils/CloudStorageError.d.ts.map +0 -1
  165. package/src/utils/CloudStorageError.ts +0 -14
  166. /package/{lib → dist}/commonjs/expo-plugin/types/index.js +0 -0
  167. /package/{lib → dist}/commonjs/expo-plugin/types/index.js.map +0 -0
  168. /package/{lib/commonjs → dist/commonjs/storages}/google-drive/types.js +0 -0
  169. /package/{lib → dist}/commonjs/types/main.js +0 -0
  170. /package/{lib → dist}/commonjs/types/main.js.map +0 -0
  171. /package/{lib → dist}/commonjs/utils/helpers.js +0 -0
  172. /package/{lib → dist}/commonjs/utils/helpers.js.map +0 -0
  173. /package/{lib → dist}/module/expo-plugin/types/index.js +0 -0
  174. /package/{lib → dist}/module/expo-plugin/types/index.js.map +0 -0
  175. /package/{lib → dist}/module/package.json +0 -0
  176. /package/{lib/module → dist/module/storages}/google-drive/types.js +0 -0
  177. /package/{lib → dist}/module/types/main.js +0 -0
  178. /package/{lib → dist}/module/types/main.js.map +0 -0
  179. /package/{lib → dist}/module/utils/helpers.js +0 -0
  180. /package/{lib → dist}/module/utils/helpers.js.map +0 -0
  181. /package/{lib → dist}/typescript/expo-plugin/types/index.d.ts +0 -0
  182. /package/{lib → dist}/typescript/expo-plugin/types/index.d.ts.map +0 -0
  183. /package/{lib/typescript → dist/typescript/storages}/google-drive/types.d.ts +0 -0
  184. /package/{lib → dist}/typescript/types/main.d.ts +0 -0
  185. /package/{lib → dist}/typescript/types/main.d.ts.map +0 -0
  186. /package/{lib → dist}/typescript/utils/helpers.d.ts +0 -0
  187. /package/src/expo-plugin/{withRNCloudStorageIos.ts → ios.ts} +0 -0
  188. /package/src/{google-drive → storages/google-drive}/types.ts +0 -0
@@ -1,18 +1,18 @@
1
- import type NativeproviderService from '../types/native';
2
1
  import {
3
- CloudStorageErrorCode,
4
- type NativeRNCloudCloudStorageFileStat,
5
- type NativeRNCloudCloudStorageScope,
6
- } from '../types/native';
7
- import CloudStorageError from '../utils/CloudStorageError';
2
+ NativeCloudStorageErrorCode,
3
+ NativeStorage,
4
+ type NativeStorageFileStat,
5
+ type NativeStorageScope,
6
+ } from '../../types/native';
7
+ import CloudStorageError from '../../utils/cloud-storage-error';
8
8
  import { MimeTypes, type GoogleDriveFile, type GoogleDriveFileSpace } from './types';
9
9
  import GoogleDriveApiClient, { GoogleDriveHttpError } from './client';
10
- import { type CloudStorageProviderOptions, type DeepRequired } from '../types/main';
10
+ import { type CloudStorageProviderOptions, type DeepRequired } from '../../types/main';
11
11
 
12
12
  /**
13
- * A proxy class that wraps the Google Drive API client implementation to match the native iOS interface.
13
+ * A JavaScript-based implementation of the Google Drive API that implements the cloud storage interface.
14
14
  */
15
- export default class GoogleDrive implements NativeproviderService {
15
+ export default class GoogleDrive implements NativeStorage {
16
16
  private drive: GoogleDriveApiClient;
17
17
  private options: DeepRequired<CloudStorageProviderOptions['googledrive']>;
18
18
 
@@ -22,19 +22,19 @@ export default class GoogleDrive implements NativeproviderService {
22
22
 
23
23
  return new Proxy(this, {
24
24
  // before calling any function, check if the access token is set
25
- get(target: GoogleDrive, prop: keyof GoogleDrive) {
25
+ get(target: GoogleDrive, property: keyof GoogleDrive) {
26
26
  const allowedFunctions = ['isCloudAvailable'];
27
- if (typeof target[prop] === 'function' && !allowedFunctions.includes(prop.toString())) {
27
+ if (typeof target[property] === 'function' && !allowedFunctions.includes(property.toString())) {
28
28
  const { accessToken } = options;
29
29
  if (!accessToken?.length) {
30
30
  throw new CloudStorageError(
31
- `Google Drive access token is not set, cannot call function ${prop.toString()}`,
32
- CloudStorageErrorCode.ACCESS_TOKEN_MISSING
31
+ `Google Drive access token is not set, cannot call function ${property.toString()}`,
32
+ NativeCloudStorageErrorCode.ACCESS_TOKEN_MISSING
33
33
  );
34
34
  }
35
35
  }
36
36
 
37
- return target[prop];
37
+ return target[property];
38
38
  },
39
39
  });
40
40
  }
@@ -44,12 +44,14 @@ export default class GoogleDrive implements NativeproviderService {
44
44
  return !!accessToken?.length;
45
45
  };
46
46
 
47
- private getRootDirectory(scope: NativeRNCloudCloudStorageScope): GoogleDriveFileSpace {
47
+ private getRootDirectory(scope: NativeStorageScope): GoogleDriveFileSpace {
48
48
  switch (scope) {
49
- case 'documents':
49
+ case 'documents': {
50
50
  return 'drive';
51
- case 'app_data':
51
+ }
52
+ case 'app_data': {
52
53
  return 'appDataFolder';
54
+ }
53
55
  }
54
56
  }
55
57
 
@@ -75,7 +77,7 @@ export default class GoogleDrive implements NativeproviderService {
75
77
  the files array - if it does not, it means that the directory is a child of the root directory and the one we're
76
78
  looking for */
77
79
  for (const possibleTopDirectory of possibleTopDirectories) {
78
- if (!files.find((f) => f.id === possibleTopDirectory!.parents![0] && f.mimeType === MimeTypes.FOLDER)) {
80
+ if (!files.some((f) => f.id === possibleTopDirectory!.parents![0] && f.mimeType === MimeTypes.FOLDER)) {
79
81
  topDirectoryId = possibleTopDirectory!.id;
80
82
  break;
81
83
  }
@@ -85,24 +87,24 @@ export default class GoogleDrive implements NativeproviderService {
85
87
  if (!topDirectoryId) {
86
88
  throw new CloudStorageError(
87
89
  `Could not find top directory with name ${directoryTree[0]}`,
88
- CloudStorageErrorCode.DIRECTORY_NOT_FOUND
90
+ NativeCloudStorageErrorCode.DIRECTORY_NOT_FOUND
89
91
  );
90
92
  }
91
93
 
92
94
  // now, we traverse the directories array and get the id of the last directory from the files array
93
95
  let currentDirectoryId = topDirectoryId;
94
- for (let i = 1; i < directoryTree.length; i++) {
96
+ for (let index = 1; index < directoryTree.length; index++) {
95
97
  const currentDirectory = files.find((f) => f.id === currentDirectoryId);
96
98
  if (!currentDirectory)
97
99
  throw new CloudStorageError(
98
100
  `Could not find directory with id ${currentDirectoryId}`,
99
- CloudStorageErrorCode.DIRECTORY_NOT_FOUND
101
+ NativeCloudStorageErrorCode.DIRECTORY_NOT_FOUND
100
102
  );
101
- const nextDirectory = files.find((f) => f.name === directoryTree[i] && f.parents![0] === currentDirectoryId);
103
+ const nextDirectory = files.find((f) => f.name === directoryTree[index] && f.parents![0] === currentDirectoryId);
102
104
  if (!nextDirectory)
103
105
  throw new CloudStorageError(
104
- `Could not find directory with name ${directoryTree[i]}`,
105
- CloudStorageErrorCode.DIRECTORY_NOT_FOUND
106
+ `Could not find directory with name ${directoryTree[index]}`,
107
+ NativeCloudStorageErrorCode.DIRECTORY_NOT_FOUND
106
108
  );
107
109
  currentDirectoryId = nextDirectory.id;
108
110
  }
@@ -115,10 +117,10 @@ export default class GoogleDrive implements NativeproviderService {
115
117
  * @param scope The scope to get the root directory for.
116
118
  * @returns A promise that resolves to the ID of the root directory or null if it could not be found.
117
119
  */
118
- private async getRootDirectoryId(scope: NativeRNCloudCloudStorageScope): Promise<string | null> {
120
+ private async getRootDirectoryId(scope: NativeStorageScope): Promise<string | null> {
119
121
  const files = await this.drive.listFiles(this.getRootDirectory(scope));
120
122
  for (const file of files) {
121
- if (!files.find((f) => f.id === file.parents![0])) return file.parents![0] ?? null;
123
+ if (!files.some((f) => f.id === file.parents![0])) return file.parents![0] ?? null;
122
124
  }
123
125
 
124
126
  return null;
@@ -132,26 +134,23 @@ export default class GoogleDrive implements NativeproviderService {
132
134
  ) {
133
135
  const { strictFilenames } = this.options;
134
136
 
135
- let possibleFiles: GoogleDriveFile[];
136
- if (parentDirectoryId) {
137
- possibleFiles = files.filter((f) => f.name === filename && f.parents![0] === parentDirectoryId);
138
- } else {
139
- possibleFiles = files.filter((f) => f.name === filename && !files.find((f2) => f2.id === f.parents![0]));
140
- }
137
+ const possibleFiles: GoogleDriveFile[] = parentDirectoryId
138
+ ? files.filter((f) => f.name === filename && f.parents![0] === parentDirectoryId)
139
+ : files.filter((f) => f.name === filename && !files.some((f2) => f2.id === f.parents![0]));
141
140
 
142
141
  if (possibleFiles.length <= 1) return;
143
142
 
144
143
  if (strictFilenames) {
145
144
  throw new CloudStorageError(
146
145
  `Multiple files with the same name found at path ${path}: ${possibleFiles.map((f) => f.id).join(', ')}`,
147
- CloudStorageErrorCode.MULTIPLE_FILES_SAME_NAME
146
+ NativeCloudStorageErrorCode.MULTIPLE_FILES_SAME_NAME
148
147
  );
149
148
  }
150
149
  }
151
150
 
152
151
  private async getFileId(
153
152
  path: string,
154
- scope: NativeRNCloudCloudStorageScope,
153
+ scope: NativeStorageScope,
155
154
  throwIf: 'directory' | 'file' | false = false
156
155
  ): Promise<string> {
157
156
  try {
@@ -162,7 +161,7 @@ export default class GoogleDrive implements NativeproviderService {
162
161
  if (!rootDirectoryId)
163
162
  throw new CloudStorageError(
164
163
  `Root directory in scope ${scope} not found`,
165
- CloudStorageErrorCode.DIRECTORY_NOT_FOUND
164
+ NativeCloudStorageErrorCode.DIRECTORY_NOT_FOUND
166
165
  );
167
166
  return rootDirectoryId;
168
167
  }
@@ -174,59 +173,63 @@ export default class GoogleDrive implements NativeproviderService {
174
173
  this.checkIfMultipleFilesWithSameName(path, files, filename, null);
175
174
  /* when the file is supposed to be in the root directory, we need to get the file where the name is the filename
176
175
  and the first parent has an id which does not exist in the files array */
177
- file = files.find((f) => f.name === filename && !files.find((f2) => f2.id === f.parents![0]));
176
+ file = files.find((f) => f.name === filename && !files.some((f2) => f2.id === f.parents![0]));
178
177
  } else {
179
178
  this.checkIfMultipleFilesWithSameName(path, files, filename, parentDirectoryId);
180
179
  file = files.find((f) => f.name === filename && f.parents![0] === parentDirectoryId);
181
180
  }
182
- if (!file) throw new CloudStorageError(`File not found`, CloudStorageErrorCode.FILE_NOT_FOUND);
181
+ if (!file) throw new CloudStorageError(`File not found`, NativeCloudStorageErrorCode.FILE_NOT_FOUND);
183
182
  if (file.mimeType === MimeTypes.FOLDER && throwIf === 'directory') {
184
- throw new CloudStorageError(`Path ${path} is a directory`, CloudStorageErrorCode.PATH_IS_DIRECTORY);
183
+ throw new CloudStorageError(`Path ${path} is a directory`, NativeCloudStorageErrorCode.PATH_IS_DIRECTORY);
185
184
  } else if (file.mimeType !== MimeTypes.FOLDER && throwIf === 'file') {
186
- throw new CloudStorageError(`Path ${path} is a file`, CloudStorageErrorCode.FILE_NOT_FOUND);
185
+ throw new CloudStorageError(`Path ${path} is a file`, NativeCloudStorageErrorCode.FILE_NOT_FOUND);
187
186
  }
188
187
  return file.id;
189
- } catch (e: unknown) {
190
- if (e instanceof GoogleDriveHttpError && e.json?.error?.status === 'UNAUTHENTICATED') {
188
+ } catch (error: unknown) {
189
+ if (error instanceof GoogleDriveHttpError && error.json?.error?.status === 'UNAUTHENTICATED') {
191
190
  throw new CloudStorageError(
192
191
  `Could not authenticate with Google Drive`,
193
- CloudStorageErrorCode.AUTHENTICATION_FAILED,
194
- e.json
192
+ NativeCloudStorageErrorCode.AUTHENTICATION_FAILED,
193
+ error.json
195
194
  );
196
195
  } else {
197
- if (e instanceof CloudStorageError) throw e;
198
- throw new CloudStorageError(`Could not get file id for path ${path}`, CloudStorageErrorCode.UNKNOWN, e);
196
+ if (error instanceof CloudStorageError) throw error;
197
+ throw new CloudStorageError(
198
+ `Could not get file id for path ${path}`,
199
+ NativeCloudStorageErrorCode.UNKNOWN,
200
+ error
201
+ );
199
202
  }
200
203
  }
201
204
  }
202
205
 
203
- async fileExists(path: string, scope: NativeRNCloudCloudStorageScope): Promise<boolean> {
206
+ async fileExists(path: string, scope: NativeStorageScope): Promise<boolean> {
204
207
  try {
205
208
  await this.getFileId(path, scope);
206
209
  return true;
207
- } catch (e: any) {
208
- if (e instanceof CloudStorageError && e.code === CloudStorageErrorCode.FILE_NOT_FOUND) return false;
209
- else throw e;
210
+ } catch (error: unknown) {
211
+ if (error instanceof CloudStorageError && error.code === NativeCloudStorageErrorCode.FILE_NOT_FOUND) return false;
212
+ else throw error;
210
213
  }
211
214
  }
212
215
 
213
- async appendToFile(path: string, data: string, scope: NativeRNCloudCloudStorageScope): Promise<void> {
216
+ async appendToFile(path: string, data: string, scope: NativeStorageScope): Promise<void> {
214
217
  let fileId: string | undefined;
215
- let prevContent = '';
218
+ let previousContent = '';
216
219
  try {
217
220
  fileId = await this.getFileId(path, scope);
218
- prevContent = await this.drive.getFileText(fileId);
219
- } catch (e: any) {
220
- if (e instanceof CloudStorageError && e.code === CloudStorageErrorCode.FILE_NOT_FOUND) {
221
+ previousContent = await this.drive.getFileText(fileId);
222
+ } catch (error: unknown) {
223
+ if (error instanceof CloudStorageError && error.code === NativeCloudStorageErrorCode.FILE_NOT_FOUND) {
221
224
  /* do nothing, simply create the file */
222
225
  } else {
223
- throw e;
226
+ throw error;
224
227
  }
225
228
  }
226
229
 
227
230
  if (fileId) {
228
231
  await this.drive.updateFile(fileId, {
229
- body: prevContent + data,
232
+ body: previousContent + data,
230
233
  mimeType: MimeTypes.TEXT,
231
234
  });
232
235
  } else {
@@ -250,32 +253,27 @@ export default class GoogleDrive implements NativeproviderService {
250
253
  }
251
254
  }
252
255
 
253
- async createFile(
254
- path: string,
255
- data: string,
256
- scope: NativeRNCloudCloudStorageScope,
257
- overwrite: boolean
258
- ): Promise<void> {
256
+ async createFile(path: string, data: string, scope: NativeStorageScope, overwrite: boolean): Promise<void> {
259
257
  let fileId: string | undefined;
260
258
  if (overwrite) {
261
259
  try {
262
260
  fileId = await this.getFileId(path, scope);
263
- } catch (e: any) {
264
- if (e instanceof CloudStorageError && e.code === CloudStorageErrorCode.FILE_NOT_FOUND) {
261
+ } catch (error: unknown) {
262
+ if (error instanceof CloudStorageError && error.code === NativeCloudStorageErrorCode.FILE_NOT_FOUND) {
265
263
  /* do nothing, simply create the file */
266
264
  } else {
267
- throw e;
265
+ throw error;
268
266
  }
269
267
  }
270
268
  } else {
271
269
  try {
272
270
  await this.getFileId(path, scope);
273
- throw new CloudStorageError(`File ${path} already exists`, CloudStorageErrorCode.FILE_ALREADY_EXISTS);
274
- } catch (e: any) {
275
- if (e instanceof CloudStorageError && e.code === CloudStorageErrorCode.FILE_NOT_FOUND) {
271
+ throw new CloudStorageError(`File ${path} already exists`, NativeCloudStorageErrorCode.FILE_ALREADY_EXISTS);
272
+ } catch (error: unknown) {
273
+ if (error instanceof CloudStorageError && error.code === NativeCloudStorageErrorCode.FILE_NOT_FOUND) {
276
274
  /* do nothing, simply create the file */
277
275
  } else {
278
- throw e;
276
+ throw error;
279
277
  }
280
278
  }
281
279
  }
@@ -306,30 +304,33 @@ export default class GoogleDrive implements NativeproviderService {
306
304
  }
307
305
  }
308
306
 
309
- async listFiles(path: string, scope: NativeRNCloudCloudStorageScope): Promise<string[]> {
307
+ async listFiles(path: string, scope: NativeStorageScope): Promise<string[]> {
310
308
  const allFiles = await this.drive.listFiles(this.getRootDirectory(scope));
311
- if (path !== '') {
309
+ if (path === '') {
310
+ const rootDirectoryId = await this.getRootDirectoryId(scope);
311
+ return [...new Set(allFiles.filter((f) => (f.parents ?? [])[0] === rootDirectoryId).map((f) => f.name))];
312
+ } else {
312
313
  const fileId = await this.getFileId(path, scope);
313
314
  const files = allFiles.filter((f) => (f.parents ?? [])[0] === fileId);
314
315
 
315
- return Array.from(new Set(files.map((f) => f.name)));
316
- } else {
317
- const rootDirectoryId = await this.getRootDirectoryId(scope);
318
- return Array.from(new Set(allFiles.filter((f) => (f.parents ?? [])[0] === rootDirectoryId).map((f) => f.name)));
316
+ return [...new Set(files.map((f) => f.name))];
319
317
  }
320
318
  }
321
319
 
322
- async createDirectory(path: string, scope: NativeRNCloudCloudStorageScope): Promise<void> {
320
+ async createDirectory(path: string, scope: NativeStorageScope): Promise<void> {
323
321
  try {
324
322
  await this.getFileId(path, scope);
325
- throw new CloudStorageError(`File ${path} already exists`, CloudStorageErrorCode.FILE_ALREADY_EXISTS);
326
- } catch (e: any) {
327
- if (e instanceof CloudStorageError && e.code === CloudStorageErrorCode.FILE_NOT_FOUND) {
323
+ throw new CloudStorageError(`File ${path} already exists`, NativeCloudStorageErrorCode.FILE_ALREADY_EXISTS);
324
+ } catch (error: unknown) {
325
+ if (error instanceof CloudStorageError && error.code === NativeCloudStorageErrorCode.FILE_NOT_FOUND) {
328
326
  /* do nothing, simply create the file */
329
- } else if (e instanceof CloudStorageError && e.code === CloudStorageErrorCode.PATH_IS_DIRECTORY) {
330
- throw new CloudStorageError(`Directory ${path} already exists`, CloudStorageErrorCode.FILE_ALREADY_EXISTS);
327
+ } else if (error instanceof CloudStorageError && error.code === NativeCloudStorageErrorCode.PATH_IS_DIRECTORY) {
328
+ throw new CloudStorageError(
329
+ `Directory ${path} already exists`,
330
+ NativeCloudStorageErrorCode.FILE_ALREADY_EXISTS
331
+ );
331
332
  } else {
332
- throw e;
333
+ throw error;
333
334
  }
334
335
  }
335
336
 
@@ -347,24 +348,24 @@ export default class GoogleDrive implements NativeproviderService {
347
348
  });
348
349
  }
349
350
 
350
- async readFile(path: string, scope: NativeRNCloudCloudStorageScope): Promise<string> {
351
+ async readFile(path: string, scope: NativeStorageScope): Promise<string> {
351
352
  const fileId = await this.getFileId(path, scope);
352
353
  const content = await this.drive.getFileText(fileId);
353
354
  return content;
354
355
  }
355
356
 
356
- 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
357
+ async triggerSync(_path: string, _scope: NativeStorageScope): Promise<void> {
358
+ // Triggering file synchronization in Google Drive is not necessary / possible, as they need to be downloaded on every read operation via the API anyway
358
359
  return;
359
360
  }
360
361
 
361
- async deleteFile(path: string, scope: NativeRNCloudCloudStorageScope): Promise<void> {
362
+ async deleteFile(path: string, scope: NativeStorageScope): Promise<void> {
362
363
  // if trying to pass a directory, throw an error
363
364
  const fileId = await this.getFileId(path, scope, 'directory');
364
365
  await this.drive.deleteFile(fileId);
365
366
  }
366
367
 
367
- async deleteDirectory(path: string, recursive: boolean, scope: NativeRNCloudCloudStorageScope): Promise<void> {
368
+ async deleteDirectory(path: string, recursive: boolean, scope: NativeStorageScope): Promise<void> {
368
369
  // if trying to pass a file, throw an error
369
370
  const fileId = await this.getFileId(path, scope, 'file');
370
371
 
@@ -375,7 +376,7 @@ export default class GoogleDrive implements NativeproviderService {
375
376
  if (filesInDirectory.length > 0) {
376
377
  throw new CloudStorageError(
377
378
  `Directory ${path} is not empty`,
378
- CloudStorageErrorCode.DELETE_ERROR,
379
+ NativeCloudStorageErrorCode.DELETE_ERROR,
379
380
  filesInDirectory
380
381
  );
381
382
  }
@@ -384,7 +385,7 @@ export default class GoogleDrive implements NativeproviderService {
384
385
  await this.drive.deleteFile(fileId);
385
386
  }
386
387
 
387
- async statFile(path: string, scope: NativeRNCloudCloudStorageScope): Promise<NativeRNCloudCloudStorageFileStat> {
388
+ async statFile(path: string, scope: NativeStorageScope): Promise<NativeStorageFileStat> {
388
389
  const fileId = await this.getFileId(path, scope, false);
389
390
  const file = await this.drive.getFile(fileId!);
390
391
 
@@ -396,4 +397,86 @@ export default class GoogleDrive implements NativeproviderService {
396
397
  isFile: file.mimeType !== MimeTypes.FOLDER,
397
398
  };
398
399
  }
400
+
401
+ async downloadFile(remotePath: string, localPath: string, scope: NativeStorageScope): Promise<void> {
402
+ const fileId = await this.getFileId(remotePath, scope, 'directory');
403
+
404
+ try {
405
+ await this.drive.downloadFile(fileId, localPath);
406
+ } catch (error: unknown) {
407
+ if (error instanceof CloudStorageError) throw error;
408
+
409
+ throw new CloudStorageError(
410
+ `Could not download file ${remotePath} to ${localPath}`,
411
+ NativeCloudStorageErrorCode.UNKNOWN,
412
+ error
413
+ );
414
+ }
415
+ }
416
+
417
+ async uploadFile(
418
+ remotePath: string,
419
+ localPath: string,
420
+ mimeType: string,
421
+ scope: NativeStorageScope,
422
+ overwrite: boolean
423
+ ): Promise<void> {
424
+ let fileId: string | undefined;
425
+
426
+ if (overwrite) {
427
+ try {
428
+ fileId = await this.getFileId(remotePath, scope);
429
+ } catch (error: unknown) {
430
+ if (error instanceof CloudStorageError && error.code === NativeCloudStorageErrorCode.FILE_NOT_FOUND) {
431
+ /* File doesn't exist -> we'll create it below */
432
+ } else {
433
+ throw error;
434
+ }
435
+ }
436
+ } else {
437
+ try {
438
+ await this.getFileId(remotePath, scope);
439
+ throw new CloudStorageError(
440
+ `File ${remotePath} already exists`,
441
+ NativeCloudStorageErrorCode.FILE_ALREADY_EXISTS
442
+ );
443
+ } catch (error: unknown) {
444
+ if (error instanceof CloudStorageError && error.code === NativeCloudStorageErrorCode.FILE_NOT_FOUND) {
445
+ /* not found -> ok, we'll create */
446
+ } else if (error instanceof CloudStorageError) {
447
+ throw error;
448
+ } else {
449
+ throw error;
450
+ }
451
+ }
452
+ }
453
+
454
+ if (fileId) {
455
+ // Overwrite existing file
456
+ await this.drive.updateFile(fileId, {
457
+ mimeType,
458
+ localPath,
459
+ });
460
+ } else {
461
+ // Need to create a new file first
462
+ const files = await this.drive.listFiles(this.getRootDirectory(scope));
463
+ const { directories, filename } = this.resolvePathToDirectories(remotePath);
464
+ const parentDirectoryId = this.findParentDirectoryId(files, directories);
465
+
466
+ await this.drive.createFile(
467
+ {
468
+ name: filename,
469
+ parents: parentDirectoryId
470
+ ? [parentDirectoryId]
471
+ : scope === 'app_data'
472
+ ? [this.getRootDirectory(scope)]
473
+ : undefined,
474
+ },
475
+ {
476
+ mimeType,
477
+ localPath,
478
+ }
479
+ );
480
+ }
481
+ }
399
482
  }
@@ -1,6 +1,6 @@
1
- export type NativeRNCloudCloudStorageScope = 'documents' | 'app_data';
1
+ export type NativeStorageScope = 'documents' | 'app_data';
2
2
 
3
- export interface NativeRNCloudCloudStorageFileStat {
3
+ export interface NativeStorageFileStat {
4
4
  size: number;
5
5
  birthtimeMs: number;
6
6
  mtimeMs: number;
@@ -8,7 +8,7 @@ export interface NativeRNCloudCloudStorageFileStat {
8
8
  isFile: boolean;
9
9
  }
10
10
 
11
- export enum CloudStorageErrorCode {
11
+ export enum NativeCloudStorageErrorCode {
12
12
  INVALID_SCOPE = 'ERR_INVALID_SCOPE',
13
13
  FILE_NOT_FOUND = 'ERR_FILE_NOT_FOUND',
14
14
  PATH_IS_FILE = 'ERR_PATH_IS_FILE',
@@ -25,18 +25,54 @@ export enum CloudStorageErrorCode {
25
25
  UNKNOWN = 'ERR_UNKNOWN',
26
26
  FILE_NOT_DOWNLOADABLE = 'ERR_FILE_NOT_DOWNLOADABLE',
27
27
  ACCESS_TOKEN_MISSING = 'ERR_ACCESS_TOKEN_MISSING',
28
+ INVALID_URL = 'ERR_INVALID_URL',
29
+ NETWORK_ERROR = 'ERR_NETWORK_ERROR',
28
30
  }
29
31
 
30
- export default interface NativeRNCloudStorage {
31
- fileExists: (path: string, scope: NativeRNCloudCloudStorageScope) => Promise<boolean>;
32
- appendToFile: (path: string, data: string, scope: NativeRNCloudCloudStorageScope) => Promise<void>;
33
- createFile: (path: string, data: string, scope: NativeRNCloudCloudStorageScope, overwrite: boolean) => Promise<void>;
34
- createDirectory: (path: string, scope: NativeRNCloudCloudStorageScope) => Promise<void>;
35
- listFiles: (path: string, scope: NativeRNCloudCloudStorageScope) => Promise<string[]>;
36
- readFile: (path: string, scope: NativeRNCloudCloudStorageScope) => Promise<string>;
37
- downloadFile: (path: string, scope: NativeRNCloudCloudStorageScope) => Promise<void>;
38
- deleteFile: (path: string, scope: NativeRNCloudCloudStorageScope) => Promise<void>;
39
- deleteDirectory: (path: string, recursively: boolean, scope: NativeRNCloudCloudStorageScope) => Promise<void>;
40
- statFile: (path: string, scope: NativeRNCloudCloudStorageScope) => Promise<NativeRNCloudCloudStorageFileStat>;
32
+ export interface NativeLocalFileSystem {
33
+ getConstants: () => {
34
+ temporaryDirectory: string;
35
+ };
36
+ createFile: (path: string, data: string) => Promise<string>;
37
+ readFile: (path: string) => Promise<string>;
38
+ downloadFile: (remoteUri: string, localPath: string, options?: { headers?: Record<string, string> }) => Promise<void>;
39
+ uploadFile: (
40
+ localPath: string,
41
+ remoteUri: string,
42
+ options?: {
43
+ headers?: Record<string, string>;
44
+ method?: 'PUT' | 'POST' | 'PATCH';
45
+ } & (
46
+ | {
47
+ uploadType?: 'binary';
48
+ }
49
+ | {
50
+ uploadType?: 'multipart';
51
+ fieldName?: string;
52
+ parameters?: Record<string, string>;
53
+ }
54
+ )
55
+ ) => Promise<void>;
56
+ }
57
+
58
+ export interface NativeStorage {
59
+ fileExists: (path: string, scope: NativeStorageScope) => Promise<boolean>;
60
+ appendToFile: (path: string, data: string, scope: NativeStorageScope) => Promise<void>;
61
+ createFile: (path: string, data: string, scope: NativeStorageScope, overwrite: boolean) => Promise<void>;
62
+ createDirectory: (path: string, scope: NativeStorageScope) => Promise<void>;
63
+ listFiles: (path: string, scope: NativeStorageScope) => Promise<string[]>;
64
+ readFile: (path: string, scope: NativeStorageScope) => Promise<string>;
65
+ deleteFile: (path: string, scope: NativeStorageScope) => Promise<void>;
66
+ deleteDirectory: (path: string, recursively: boolean, scope: NativeStorageScope) => Promise<void>;
67
+ statFile: (path: string, scope: NativeStorageScope) => Promise<NativeStorageFileStat>;
68
+ downloadFile: (remotePath: string, localPath: string, scope: NativeStorageScope) => Promise<void>;
69
+ uploadFile: (
70
+ remotePath: string,
71
+ localPath: string,
72
+ mimeType: string,
73
+ scope: NativeStorageScope,
74
+ overwrite: boolean
75
+ ) => Promise<void>;
41
76
  isCloudAvailable: () => Promise<boolean>;
77
+ triggerSync: (path: string, scope: NativeStorageScope) => Promise<void>;
42
78
  }
@@ -0,0 +1,15 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import type { NativeCloudStorageErrorCode } from '../types/native';
3
+
4
+ class CloudStorageError extends Error {
5
+ code: NativeCloudStorageErrorCode;
6
+ details?: any;
7
+
8
+ constructor(message: string, code: NativeCloudStorageErrorCode, details?: any) {
9
+ super(message);
10
+ this.code = code;
11
+ this.details = details;
12
+ }
13
+ }
14
+
15
+ export default CloudStorageError;
@@ -0,0 +1,20 @@
1
+ import { Platform } from 'react-native';
2
+ import { CloudStorageProvider, CloudStorageProviderOptions, CloudStorageScope, DeepRequired } from '../types/main';
3
+
4
+ export const LINKING_ERROR =
5
+ `The package 'react-native-cloud-storage' doesn't seem to be linked. Make sure: \n\n` +
6
+ Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) +
7
+ '- You rebuilt the app after installing the package\n' +
8
+ '- You are not using Expo Go\n';
9
+
10
+ export const DEFAULT_PROVIDER_OPTIONS: DeepRequired<CloudStorageProviderOptions> = {
11
+ [CloudStorageProvider.ICloud]: {
12
+ scope: CloudStorageScope.AppData,
13
+ },
14
+ [CloudStorageProvider.GoogleDrive]: {
15
+ scope: CloudStorageScope.AppData,
16
+ accessToken: null,
17
+ strictFilenames: false,
18
+ timeout: 3000,
19
+ },
20
+ };
@@ -0,0 +1,19 @@
1
+ import { NativeModules } from 'react-native';
2
+ import { NativeLocalFileSystem as TNativeLocalFileSystem } from '../types/native';
3
+ import { createProxiedNativeModule } from '../utils/native';
4
+ import { LINKING_ERROR } from './constants';
5
+
6
+ const NativeLocalFileSystem = createProxiedNativeModule<TNativeLocalFileSystem>(
7
+ NativeModules.CloudStorageLocalFileSystem
8
+ );
9
+
10
+ export const localFileSystem =
11
+ NativeLocalFileSystem ??
12
+ (new Proxy(
13
+ {},
14
+ {
15
+ get() {
16
+ throw new Error(LINKING_ERROR);
17
+ },
18
+ }
19
+ ) as TNativeLocalFileSystem);
@@ -0,0 +1,40 @@
1
+ import { NativeCloudStorageErrorCode } from '../types/native';
2
+ import CloudStorageError from './cloud-storage-error';
3
+
4
+ /**
5
+ * Creates a proxied native module that wraps thrown errors in a CloudStorageError.
6
+ * @param nativeModule The native module to proxy.
7
+ * @returns The proxied native module.
8
+ */
9
+ export const createProxiedNativeModule = <T extends object>(nativeModule: T | undefined): T | null => {
10
+ if (!nativeModule) return null;
11
+
12
+ return new Proxy(nativeModule, {
13
+ get(target: T, property: string | symbol, receiver: unknown) {
14
+ const originalValue = Reflect.get(target, property, receiver);
15
+
16
+ if (typeof originalValue === 'function') {
17
+ return async (...arguments_: unknown[]) => {
18
+ try {
19
+ return await Reflect.apply(originalValue, target, arguments_);
20
+ } catch (error: unknown) {
21
+ const error_ =
22
+ typeof error === 'object' &&
23
+ error !== null &&
24
+ 'code' in error &&
25
+ typeof error.code === 'string' &&
26
+ Object.values(NativeCloudStorageErrorCode).includes(error.code as NativeCloudStorageErrorCode)
27
+ ? new CloudStorageError(
28
+ 'message' in error && typeof error.message === 'string' ? error.message : 'Unknown error',
29
+ error.code as NativeCloudStorageErrorCode
30
+ )
31
+ : new CloudStorageError('Unknown error', NativeCloudStorageErrorCode.UNKNOWN, error);
32
+ throw error_;
33
+ }
34
+ };
35
+ }
36
+
37
+ return originalValue;
38
+ },
39
+ });
40
+ };