react-native-cloud-storage 2.3.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/android/build.gradle +1 -14
- package/android/src/main/java/com/voicekit/CloudStorageLocalFileSystemModule.kt +16 -20
- package/android/src/main/java/com/voicekit/CloudStoragePackage.kt +23 -8
- package/dist/commonjs/cloud-storage.js +66 -31
- package/dist/commonjs/cloud-storage.js.map +1 -1
- package/dist/commonjs/specs/NativeCloudStorageCloudKitIOS.js +9 -0
- package/dist/commonjs/specs/NativeCloudStorageCloudKitIOS.js.map +1 -0
- package/dist/commonjs/specs/NativeCloudStorageLocalFileSystem.js +9 -0
- package/dist/commonjs/specs/NativeCloudStorageLocalFileSystem.js.map +1 -0
- package/dist/commonjs/storages/cloudkit.js +5 -3
- package/dist/commonjs/storages/cloudkit.js.map +1 -1
- package/dist/commonjs/storages/google-drive/client.js +3 -2
- package/dist/commonjs/storages/google-drive/client.js.map +1 -1
- package/dist/commonjs/storages/google-drive/index.js +99 -77
- package/dist/commonjs/storages/google-drive/index.js.map +1 -1
- package/dist/commonjs/types/main.js.map +1 -1
- package/dist/commonjs/utils/constants.js +2 -1
- package/dist/commonjs/utils/constants.js.map +1 -1
- package/dist/commonjs/utils/local-fs.js +3 -2
- package/dist/commonjs/utils/local-fs.js.map +1 -1
- package/dist/commonjs/utils/native.js.map +1 -1
- package/dist/module/cloud-storage.js +68 -33
- package/dist/module/cloud-storage.js.map +1 -1
- package/dist/module/specs/NativeCloudStorageCloudKitIOS.js +5 -0
- package/dist/module/specs/NativeCloudStorageCloudKitIOS.js.map +1 -0
- package/dist/module/specs/NativeCloudStorageLocalFileSystem.js +5 -0
- package/dist/module/specs/NativeCloudStorageLocalFileSystem.js.map +1 -0
- package/dist/module/storages/cloudkit.js +3 -2
- package/dist/module/storages/cloudkit.js.map +1 -1
- package/dist/module/storages/google-drive/client.js +3 -2
- package/dist/module/storages/google-drive/client.js.map +1 -1
- package/dist/module/storages/google-drive/index.js +99 -77
- package/dist/module/storages/google-drive/index.js.map +1 -1
- package/dist/module/types/main.js.map +1 -1
- package/dist/module/utils/constants.js +2 -1
- package/dist/module/utils/constants.js.map +1 -1
- package/dist/module/utils/local-fs.js +2 -2
- package/dist/module/utils/local-fs.js.map +1 -1
- package/dist/module/utils/native.js.map +1 -1
- package/dist/typescript/cloud-storage.d.ts +7 -0
- package/dist/typescript/cloud-storage.d.ts.map +1 -1
- package/dist/typescript/specs/NativeCloudStorageCloudKitIOS.d.ts +30 -0
- package/dist/typescript/specs/NativeCloudStorageCloudKitIOS.d.ts.map +1 -0
- package/dist/typescript/specs/NativeCloudStorageLocalFileSystem.d.ts +24 -0
- package/dist/typescript/specs/NativeCloudStorageLocalFileSystem.d.ts.map +1 -0
- package/dist/typescript/storages/cloudkit.d.ts +3 -0
- package/dist/typescript/storages/cloudkit.d.ts.map +1 -1
- package/dist/typescript/storages/google-drive/client.d.ts +1 -1
- package/dist/typescript/storages/google-drive/client.d.ts.map +1 -1
- package/dist/typescript/storages/google-drive/index.d.ts +7 -1
- package/dist/typescript/storages/google-drive/index.d.ts.map +1 -1
- package/dist/typescript/types/main.d.ts +8 -0
- package/dist/typescript/types/main.d.ts.map +1 -1
- package/dist/typescript/types/native.d.ts +6 -43
- package/dist/typescript/types/native.d.ts.map +1 -1
- package/dist/typescript/utils/constants.d.ts.map +1 -1
- package/dist/typescript/utils/local-fs.d.ts +1 -2
- package/dist/typescript/utils/local-fs.d.ts.map +1 -1
- package/dist/typescript/utils/native.d.ts +1 -1
- package/dist/typescript/utils/native.d.ts.map +1 -1
- package/ios/CloudStorage-Bridging-Header.h +0 -1
- package/ios/CloudStorageCloudKit.swift +15 -14
- package/ios/CloudStorageLocalFileSystem.swift +7 -6
- package/ios/RCTCloudStorageCloudKit.mm +209 -0
- package/ios/RCTCloudStorageLocalFileSystem.mm +149 -0
- package/ios/Utils/CloudKitUtils.swift +8 -2
- package/ios/Utils/FileUtils.swift +2 -4
- package/ios/Utils/Promise.swift +1 -0
- package/ios/Utils/Types.swift +1 -0
- package/ios/react_native_cloud_storage.h +6 -0
- package/package.json +30 -17
- package/react-native-cloud-storage.podspec +2 -0
- package/src/cloud-storage.ts +90 -42
- package/src/specs/NativeCloudStorageCloudKitIOS.ts +33 -0
- package/src/specs/NativeCloudStorageLocalFileSystem.ts +28 -0
- package/src/storages/cloudkit.ts +10 -2
- package/src/storages/google-drive/client.ts +2 -1
- package/src/storages/google-drive/index.ts +126 -120
- package/src/types/main.ts +9 -0
- package/src/types/native.ts +10 -54
- package/src/utils/constants.ts +1 -0
- package/src/utils/local-fs.ts +2 -2
- package/src/utils/native.ts +1 -1
- package/ios/CloudStorageCloudKit.m +0 -24
- package/ios/CloudStorageEventEmitter.m +0 -16
- package/ios/CloudStorageEventEmitter.swift +0 -30
- package/ios/CloudStorageLocalFileSystem.m +0 -15
|
@@ -116,7 +116,7 @@ export default class GoogleDriveApiClient {
|
|
|
116
116
|
return body.join('');
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
public async listFiles(space: GoogleDriveFileSpace): Promise<GoogleDriveFile[]> {
|
|
119
|
+
public async listFiles(space: GoogleDriveFileSpace, query?: string): Promise<GoogleDriveFile[]> {
|
|
120
120
|
const files: GoogleDriveFile[] = [];
|
|
121
121
|
let pageToken: string | undefined;
|
|
122
122
|
const fields = ['id', 'kind', 'mimeType', 'name', 'parents', 'spaces', 'size', 'createdTime', 'modifiedTime'];
|
|
@@ -125,6 +125,7 @@ export default class GoogleDriveApiClient {
|
|
|
125
125
|
fields: `files(${fields.join(',')}),nextPageToken`,
|
|
126
126
|
spaces: space,
|
|
127
127
|
pageToken,
|
|
128
|
+
q: query,
|
|
128
129
|
};
|
|
129
130
|
const response = await this.request<GoogleDriveListOperationResponse>(`/files`, {
|
|
130
131
|
queryParameters,
|
|
@@ -49,13 +49,23 @@ export default class GoogleDrive implements NativeStorage {
|
|
|
49
49
|
case 'documents': {
|
|
50
50
|
return 'drive';
|
|
51
51
|
}
|
|
52
|
+
case 'documents_legacy': {
|
|
53
|
+
return 'drive';
|
|
54
|
+
}
|
|
52
55
|
case 'app_data': {
|
|
53
56
|
return 'appDataFolder';
|
|
54
57
|
}
|
|
55
58
|
}
|
|
56
59
|
}
|
|
57
60
|
|
|
58
|
-
private
|
|
61
|
+
private isRootPath(path: string): boolean {
|
|
62
|
+
return path === '' || path === '/';
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private resolvePathToDirectories(path: string): {
|
|
66
|
+
directories: string[];
|
|
67
|
+
filename: string;
|
|
68
|
+
} {
|
|
59
69
|
if (path.startsWith('/')) path = path.slice(1);
|
|
60
70
|
if (path.endsWith('/')) path = path.slice(0, -1);
|
|
61
71
|
const directories = path.split('/');
|
|
@@ -63,86 +73,90 @@ export default class GoogleDrive implements NativeStorage {
|
|
|
63
73
|
return { directories, filename: actualFilename };
|
|
64
74
|
}
|
|
65
75
|
|
|
66
|
-
private
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
.filter((f) => f.name === directoryTree[0]);
|
|
76
|
+
private escapeDriveQueryValue(value: string): string {
|
|
77
|
+
return value.replaceAll('\\', String.raw`\\`).replaceAll("'", String.raw`\'`);
|
|
78
|
+
}
|
|
70
79
|
|
|
71
|
-
|
|
72
|
-
if (
|
|
73
|
-
|
|
74
|
-
topDirectoryId = possibleTopDirectories[0]!.id;
|
|
75
|
-
} else {
|
|
76
|
-
/* when multiple directories carry the same name, we need to check every one of them if their parent id exists in
|
|
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
|
|
78
|
-
looking for */
|
|
79
|
-
for (const possibleTopDirectory of possibleTopDirectories) {
|
|
80
|
-
if (!files.some((f) => f.id === possibleTopDirectory!.parents![0] && f.mimeType === MimeTypes.FOLDER)) {
|
|
81
|
-
topDirectoryId = possibleTopDirectory!.id;
|
|
82
|
-
break;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
80
|
+
private async getQueryRootParentId(scope: NativeStorageScope): Promise<string> {
|
|
81
|
+
if (scope === 'app_data') {
|
|
82
|
+
return this.getRootDirectory(scope);
|
|
85
83
|
}
|
|
86
84
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
`Could not find top directory with name ${directoryTree[0]}`,
|
|
90
|
-
NativeCloudStorageErrorCode.DIRECTORY_NOT_FOUND
|
|
91
|
-
);
|
|
92
|
-
}
|
|
85
|
+
return this.getRootDirectoryId(scope);
|
|
86
|
+
}
|
|
93
87
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
88
|
+
private async findDirectoryByNameAndParent(
|
|
89
|
+
name: string,
|
|
90
|
+
parentId: string,
|
|
91
|
+
scope: NativeStorageScope
|
|
92
|
+
): Promise<GoogleDriveFile[]> {
|
|
93
|
+
const escapedName = this.escapeDriveQueryValue(name);
|
|
94
|
+
const escapedParentId = this.escapeDriveQueryValue(parentId);
|
|
95
|
+
const query = `name = '${escapedName}' and '${escapedParentId}' in parents and mimeType = '${MimeTypes.FOLDER}' and trashed = false`;
|
|
96
|
+
return this.drive.listFiles(this.getRootDirectory(scope), query);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
private async findFilesByNameAndParent(
|
|
100
|
+
name: string,
|
|
101
|
+
parentId: string,
|
|
102
|
+
scope: NativeStorageScope
|
|
103
|
+
): Promise<GoogleDriveFile[]> {
|
|
104
|
+
const escapedName = this.escapeDriveQueryValue(name);
|
|
105
|
+
const escapedParentId = this.escapeDriveQueryValue(parentId);
|
|
106
|
+
const query = `name = '${escapedName}' and '${escapedParentId}' in parents and trashed = false`;
|
|
107
|
+
return this.drive.listFiles(this.getRootDirectory(scope), query);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
private async findFilesByParent(parentId: string, scope: NativeStorageScope): Promise<GoogleDriveFile[]> {
|
|
111
|
+
const escapedParentId = this.escapeDriveQueryValue(parentId);
|
|
112
|
+
const query = `'${escapedParentId}' in parents and trashed = false`;
|
|
113
|
+
return this.drive.listFiles(this.getRootDirectory(scope), query);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private async findParentDirectoryId(directoryTree: string[], scope: NativeStorageScope): Promise<string | null> {
|
|
117
|
+
let parentDirectoryId = await this.getQueryRootParentId(scope);
|
|
118
|
+
|
|
119
|
+
for (const directoryName of directoryTree) {
|
|
120
|
+
const directories = await this.findDirectoryByNameAndParent(directoryName, parentDirectoryId, scope);
|
|
121
|
+
if (directories.length === 0) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
parentDirectoryId = directories[0]!.id;
|
|
110
126
|
}
|
|
111
127
|
|
|
112
|
-
return
|
|
128
|
+
return parentDirectoryId;
|
|
113
129
|
}
|
|
114
130
|
|
|
115
131
|
/**
|
|
116
132
|
* Gets the Google Drive ID of the root directory for the given scope.
|
|
117
133
|
* @param scope The scope to get the root directory for.
|
|
118
|
-
* @returns A promise that resolves to the ID of the root directory
|
|
134
|
+
* @returns A promise that resolves to the ID of the root directory.
|
|
119
135
|
*/
|
|
120
|
-
private async getRootDirectoryId(scope: NativeStorageScope): Promise<string
|
|
136
|
+
private async getRootDirectoryId(scope: NativeStorageScope): Promise<string> {
|
|
137
|
+
if (scope !== 'app_data') {
|
|
138
|
+
return 'root';
|
|
139
|
+
}
|
|
140
|
+
|
|
121
141
|
const files = await this.drive.listFiles(this.getRootDirectory(scope));
|
|
122
142
|
for (const file of files) {
|
|
123
|
-
|
|
143
|
+
const parentId = file.parents?.[0];
|
|
144
|
+
if (parentId && !files.some((candidate) => candidate.id === parentId)) {
|
|
145
|
+
return parentId;
|
|
146
|
+
}
|
|
124
147
|
}
|
|
125
148
|
|
|
126
|
-
return
|
|
149
|
+
return this.getRootDirectory(scope);
|
|
127
150
|
}
|
|
128
151
|
|
|
129
|
-
private checkIfMultipleFilesWithSameName(
|
|
130
|
-
path: string,
|
|
131
|
-
files: GoogleDriveFile[],
|
|
132
|
-
filename: string,
|
|
133
|
-
parentDirectoryId: string | null
|
|
134
|
-
) {
|
|
152
|
+
private checkIfMultipleFilesWithSameName(path: string, files: GoogleDriveFile[]) {
|
|
135
153
|
const { strictFilenames } = this.options;
|
|
136
154
|
|
|
137
|
-
|
|
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]));
|
|
140
|
-
|
|
141
|
-
if (possibleFiles.length <= 1) return;
|
|
155
|
+
if (files.length <= 1) return;
|
|
142
156
|
|
|
143
157
|
if (strictFilenames) {
|
|
144
158
|
throw new CloudStorageError(
|
|
145
|
-
`Multiple files with the same name found at path ${path}: ${
|
|
159
|
+
`Multiple files with the same name found at path ${path}: ${files.map((f) => f.id).join(', ')}`,
|
|
146
160
|
NativeCloudStorageErrorCode.MULTIPLE_FILES_SAME_NAME
|
|
147
161
|
);
|
|
148
162
|
}
|
|
@@ -154,30 +168,29 @@ export default class GoogleDrive implements NativeStorage {
|
|
|
154
168
|
throwIf: 'directory' | 'file' | false = false
|
|
155
169
|
): Promise<string> {
|
|
156
170
|
try {
|
|
157
|
-
|
|
171
|
+
if (this.isRootPath(path)) {
|
|
172
|
+
if (throwIf === 'directory') {
|
|
173
|
+
throw new CloudStorageError(`Path ${path} is a directory`, NativeCloudStorageErrorCode.PATH_IS_DIRECTORY);
|
|
174
|
+
}
|
|
158
175
|
|
|
159
|
-
if (path === '' || path === '/') {
|
|
160
176
|
const rootDirectoryId = await this.getRootDirectoryId(scope);
|
|
161
|
-
if (
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
);
|
|
177
|
+
if (scope !== 'app_data') {
|
|
178
|
+
await this.drive.getFile(rootDirectoryId);
|
|
179
|
+
}
|
|
180
|
+
|
|
166
181
|
return rootDirectoryId;
|
|
167
182
|
}
|
|
168
183
|
|
|
169
184
|
const { directories, filename } = this.resolvePathToDirectories(path);
|
|
170
|
-
const parentDirectoryId = this.findParentDirectoryId(
|
|
171
|
-
let file: GoogleDriveFile | undefined;
|
|
185
|
+
const parentDirectoryId = await this.findParentDirectoryId(directories, scope);
|
|
172
186
|
if (parentDirectoryId === null) {
|
|
173
|
-
|
|
174
|
-
/* when the file is supposed to be in the root directory, we need to get the file where the name is the filename
|
|
175
|
-
and the first parent has an id which does not exist in the files array */
|
|
176
|
-
file = files.find((f) => f.name === filename && !files.some((f2) => f2.id === f.parents![0]));
|
|
177
|
-
} else {
|
|
178
|
-
this.checkIfMultipleFilesWithSameName(path, files, filename, parentDirectoryId);
|
|
179
|
-
file = files.find((f) => f.name === filename && f.parents![0] === parentDirectoryId);
|
|
187
|
+
throw new CloudStorageError(`File not found`, NativeCloudStorageErrorCode.FILE_NOT_FOUND);
|
|
180
188
|
}
|
|
189
|
+
|
|
190
|
+
const files = await this.findFilesByNameAndParent(filename, parentDirectoryId, scope);
|
|
191
|
+
this.checkIfMultipleFilesWithSameName(path, files);
|
|
192
|
+
|
|
193
|
+
const file = files[0];
|
|
181
194
|
if (!file) throw new CloudStorageError(`File not found`, NativeCloudStorageErrorCode.FILE_NOT_FOUND);
|
|
182
195
|
if (file.mimeType === MimeTypes.FOLDER && throwIf === 'directory') {
|
|
183
196
|
throw new CloudStorageError(`Path ${path} is a directory`, NativeCloudStorageErrorCode.PATH_IS_DIRECTORY);
|
|
@@ -217,7 +230,7 @@ export default class GoogleDrive implements NativeStorage {
|
|
|
217
230
|
let fileId: string | undefined;
|
|
218
231
|
let previousContent = '';
|
|
219
232
|
try {
|
|
220
|
-
fileId = await this.getFileId(path, scope);
|
|
233
|
+
fileId = await this.getFileId(path, scope, 'directory');
|
|
221
234
|
previousContent = await this.drive.getFileText(fileId);
|
|
222
235
|
} catch (error: unknown) {
|
|
223
236
|
if (error instanceof CloudStorageError && error.code === NativeCloudStorageErrorCode.FILE_NOT_FOUND) {
|
|
@@ -233,17 +246,16 @@ export default class GoogleDrive implements NativeStorage {
|
|
|
233
246
|
mimeType: MimeTypes.TEXT,
|
|
234
247
|
});
|
|
235
248
|
} else {
|
|
236
|
-
const files = await this.drive.listFiles(this.getRootDirectory(scope));
|
|
237
249
|
const { directories, filename } = this.resolvePathToDirectories(path);
|
|
238
|
-
const parentDirectoryId = this.findParentDirectoryId(
|
|
250
|
+
const parentDirectoryId = await this.findParentDirectoryId(directories, scope);
|
|
251
|
+
if (parentDirectoryId === null) {
|
|
252
|
+
throw new CloudStorageError(`Directory not found`, NativeCloudStorageErrorCode.DIRECTORY_NOT_FOUND);
|
|
253
|
+
}
|
|
254
|
+
|
|
239
255
|
await this.drive.createFile(
|
|
240
256
|
{
|
|
241
257
|
name: filename,
|
|
242
|
-
parents: parentDirectoryId
|
|
243
|
-
? [parentDirectoryId]
|
|
244
|
-
: scope === 'app_data'
|
|
245
|
-
? [this.getRootDirectory(scope)]
|
|
246
|
-
: undefined,
|
|
258
|
+
parents: [parentDirectoryId],
|
|
247
259
|
},
|
|
248
260
|
{
|
|
249
261
|
body: data,
|
|
@@ -257,7 +269,7 @@ export default class GoogleDrive implements NativeStorage {
|
|
|
257
269
|
let fileId: string | undefined;
|
|
258
270
|
if (overwrite) {
|
|
259
271
|
try {
|
|
260
|
-
fileId = await this.getFileId(path, scope);
|
|
272
|
+
fileId = await this.getFileId(path, scope, 'directory');
|
|
261
273
|
} catch (error: unknown) {
|
|
262
274
|
if (error instanceof CloudStorageError && error.code === NativeCloudStorageErrorCode.FILE_NOT_FOUND) {
|
|
263
275
|
/* do nothing, simply create the file */
|
|
@@ -267,7 +279,7 @@ export default class GoogleDrive implements NativeStorage {
|
|
|
267
279
|
}
|
|
268
280
|
} else {
|
|
269
281
|
try {
|
|
270
|
-
await this.getFileId(path, scope);
|
|
282
|
+
await this.getFileId(path, scope, 'directory');
|
|
271
283
|
throw new CloudStorageError(`File ${path} already exists`, NativeCloudStorageErrorCode.FILE_ALREADY_EXISTS);
|
|
272
284
|
} catch (error: unknown) {
|
|
273
285
|
if (error instanceof CloudStorageError && error.code === NativeCloudStorageErrorCode.FILE_NOT_FOUND) {
|
|
@@ -284,17 +296,16 @@ export default class GoogleDrive implements NativeStorage {
|
|
|
284
296
|
mimeType: MimeTypes.TEXT,
|
|
285
297
|
});
|
|
286
298
|
} else {
|
|
287
|
-
const files = await this.drive.listFiles(this.getRootDirectory(scope));
|
|
288
299
|
const { directories, filename } = this.resolvePathToDirectories(path);
|
|
289
|
-
const parentDirectoryId = this.findParentDirectoryId(
|
|
300
|
+
const parentDirectoryId = await this.findParentDirectoryId(directories, scope);
|
|
301
|
+
if (parentDirectoryId === null) {
|
|
302
|
+
throw new CloudStorageError(`Directory not found`, NativeCloudStorageErrorCode.DIRECTORY_NOT_FOUND);
|
|
303
|
+
}
|
|
304
|
+
|
|
290
305
|
await this.drive.createFile(
|
|
291
306
|
{
|
|
292
307
|
name: filename,
|
|
293
|
-
parents: parentDirectoryId
|
|
294
|
-
? [parentDirectoryId]
|
|
295
|
-
: scope === 'app_data'
|
|
296
|
-
? [this.getRootDirectory(scope)]
|
|
297
|
-
: undefined,
|
|
308
|
+
parents: [parentDirectoryId],
|
|
298
309
|
},
|
|
299
310
|
{
|
|
300
311
|
body: data,
|
|
@@ -305,25 +316,25 @@ export default class GoogleDrive implements NativeStorage {
|
|
|
305
316
|
}
|
|
306
317
|
|
|
307
318
|
async listFiles(path: string, scope: NativeStorageScope): Promise<string[]> {
|
|
308
|
-
const
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
return [...new Set(allFiles.filter((f) => (f.parents ?? [])[0] === rootDirectoryId).map((f) => f.name))];
|
|
312
|
-
} else {
|
|
313
|
-
const fileId = await this.getFileId(path, scope);
|
|
314
|
-
const files = allFiles.filter((f) => (f.parents ?? [])[0] === fileId);
|
|
319
|
+
const parentDirectoryId = this.isRootPath(path)
|
|
320
|
+
? await this.getQueryRootParentId(scope)
|
|
321
|
+
: await this.getFileId(path, scope);
|
|
315
322
|
|
|
316
|
-
|
|
317
|
-
|
|
323
|
+
const files = await this.findFilesByParent(parentDirectoryId, scope);
|
|
324
|
+
return [...new Set(files.map((f) => f.name))];
|
|
318
325
|
}
|
|
319
326
|
|
|
320
327
|
async createDirectory(path: string, scope: NativeStorageScope): Promise<void> {
|
|
328
|
+
if (this.isRootPath(path)) {
|
|
329
|
+
throw new CloudStorageError(`Directory ${path} already exists`, NativeCloudStorageErrorCode.FILE_ALREADY_EXISTS);
|
|
330
|
+
}
|
|
331
|
+
|
|
321
332
|
try {
|
|
322
333
|
await this.getFileId(path, scope);
|
|
323
334
|
throw new CloudStorageError(`File ${path} already exists`, NativeCloudStorageErrorCode.FILE_ALREADY_EXISTS);
|
|
324
335
|
} catch (error: unknown) {
|
|
325
336
|
if (error instanceof CloudStorageError && error.code === NativeCloudStorageErrorCode.FILE_NOT_FOUND) {
|
|
326
|
-
/* do nothing, simply create the
|
|
337
|
+
/* do nothing, simply create the directory */
|
|
327
338
|
} else if (error instanceof CloudStorageError && error.code === NativeCloudStorageErrorCode.PATH_IS_DIRECTORY) {
|
|
328
339
|
throw new CloudStorageError(
|
|
329
340
|
`Directory ${path} already exists`,
|
|
@@ -334,22 +345,20 @@ export default class GoogleDrive implements NativeStorage {
|
|
|
334
345
|
}
|
|
335
346
|
}
|
|
336
347
|
|
|
337
|
-
const files = await this.drive.listFiles(this.getRootDirectory(scope));
|
|
338
348
|
const { directories, filename } = this.resolvePathToDirectories(path);
|
|
339
|
-
const parentDirectoryId = this.findParentDirectoryId(
|
|
349
|
+
const parentDirectoryId = await this.findParentDirectoryId(directories, scope);
|
|
350
|
+
if (parentDirectoryId === null) {
|
|
351
|
+
throw new CloudStorageError(`Directory not found`, NativeCloudStorageErrorCode.DIRECTORY_NOT_FOUND);
|
|
352
|
+
}
|
|
340
353
|
|
|
341
354
|
await this.drive.createDirectory({
|
|
342
355
|
name: filename,
|
|
343
|
-
parents: parentDirectoryId
|
|
344
|
-
? [parentDirectoryId]
|
|
345
|
-
: scope === 'app_data'
|
|
346
|
-
? [this.getRootDirectory(scope)]
|
|
347
|
-
: undefined,
|
|
356
|
+
parents: [parentDirectoryId],
|
|
348
357
|
});
|
|
349
358
|
}
|
|
350
359
|
|
|
351
360
|
async readFile(path: string, scope: NativeStorageScope): Promise<string> {
|
|
352
|
-
const fileId = await this.getFileId(path, scope);
|
|
361
|
+
const fileId = await this.getFileId(path, scope, 'directory');
|
|
353
362
|
const content = await this.drive.getFileText(fileId);
|
|
354
363
|
return content;
|
|
355
364
|
}
|
|
@@ -371,8 +380,7 @@ export default class GoogleDrive implements NativeStorage {
|
|
|
371
380
|
|
|
372
381
|
if (!recursive) {
|
|
373
382
|
// check if the directory is empty
|
|
374
|
-
const
|
|
375
|
-
const filesInDirectory = files.filter((f) => (f.parents ?? [])[0] === fileId);
|
|
383
|
+
const filesInDirectory = await this.findFilesByParent(fileId, scope);
|
|
376
384
|
if (filesInDirectory.length > 0) {
|
|
377
385
|
throw new CloudStorageError(
|
|
378
386
|
`Directory ${path} is not empty`,
|
|
@@ -425,7 +433,7 @@ export default class GoogleDrive implements NativeStorage {
|
|
|
425
433
|
|
|
426
434
|
if (overwrite) {
|
|
427
435
|
try {
|
|
428
|
-
fileId = await this.getFileId(remotePath, scope);
|
|
436
|
+
fileId = await this.getFileId(remotePath, scope, 'directory');
|
|
429
437
|
} catch (error: unknown) {
|
|
430
438
|
if (error instanceof CloudStorageError && error.code === NativeCloudStorageErrorCode.FILE_NOT_FOUND) {
|
|
431
439
|
/* File doesn't exist -> we'll create it below */
|
|
@@ -435,7 +443,7 @@ export default class GoogleDrive implements NativeStorage {
|
|
|
435
443
|
}
|
|
436
444
|
} else {
|
|
437
445
|
try {
|
|
438
|
-
await this.getFileId(remotePath, scope);
|
|
446
|
+
await this.getFileId(remotePath, scope, 'directory');
|
|
439
447
|
throw new CloudStorageError(
|
|
440
448
|
`File ${remotePath} already exists`,
|
|
441
449
|
NativeCloudStorageErrorCode.FILE_ALREADY_EXISTS
|
|
@@ -459,18 +467,16 @@ export default class GoogleDrive implements NativeStorage {
|
|
|
459
467
|
});
|
|
460
468
|
} else {
|
|
461
469
|
// Need to create a new file first
|
|
462
|
-
const files = await this.drive.listFiles(this.getRootDirectory(scope));
|
|
463
470
|
const { directories, filename } = this.resolvePathToDirectories(remotePath);
|
|
464
|
-
const parentDirectoryId = this.findParentDirectoryId(
|
|
471
|
+
const parentDirectoryId = await this.findParentDirectoryId(directories, scope);
|
|
472
|
+
if (parentDirectoryId === null) {
|
|
473
|
+
throw new CloudStorageError(`Directory not found`, NativeCloudStorageErrorCode.DIRECTORY_NOT_FOUND);
|
|
474
|
+
}
|
|
465
475
|
|
|
466
476
|
await this.drive.createFile(
|
|
467
477
|
{
|
|
468
478
|
name: filename,
|
|
469
|
-
parents: parentDirectoryId
|
|
470
|
-
? [parentDirectoryId]
|
|
471
|
-
: scope === 'app_data'
|
|
472
|
-
? [this.getRootDirectory(scope)]
|
|
473
|
-
: undefined,
|
|
479
|
+
parents: [parentDirectoryId],
|
|
474
480
|
},
|
|
475
481
|
{
|
|
476
482
|
mimeType,
|
package/src/types/main.ts
CHANGED
|
@@ -8,6 +8,8 @@ export enum CloudStorageScope {
|
|
|
8
8
|
AppData = 'app_data',
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
export type ICloudDocumentsMode = 'icloud' | 'legacy_sandbox';
|
|
12
|
+
|
|
11
13
|
export interface CloudStorageFileStat {
|
|
12
14
|
size: number;
|
|
13
15
|
birthtimeMs: number;
|
|
@@ -29,6 +31,13 @@ export interface CloudStorageProviderOptions {
|
|
|
29
31
|
* The directory scope to use for iCloud operations. Defaults to 'app_data'.
|
|
30
32
|
*/
|
|
31
33
|
scope?: CloudStorageScope;
|
|
34
|
+
/**
|
|
35
|
+
* The directory mode to use for CloudStorageScope.Documents.
|
|
36
|
+
* `icloud` uses the user-facing iCloud Documents directory, while
|
|
37
|
+
* `legacy_sandbox` uses the local app sandbox Documents directory.
|
|
38
|
+
* Defaults to `icloud`.
|
|
39
|
+
*/
|
|
40
|
+
documentsMode?: ICloudDocumentsMode;
|
|
32
41
|
};
|
|
33
42
|
|
|
34
43
|
[CloudStorageProvider.GoogleDrive]: {
|
package/src/types/native.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
import type {
|
|
2
|
+
CloudStorageFileStat,
|
|
3
|
+
Spec as NativeCloudStorageCloudKitSpec,
|
|
4
|
+
} from '../specs/NativeCloudStorageCloudKitIOS';
|
|
5
|
+
import type { Spec as NativeCloudStorageLocalFileSystemSpec } from '../specs/NativeCloudStorageLocalFileSystem';
|
|
2
6
|
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
mtimeMs: number;
|
|
7
|
-
isDirectory: boolean;
|
|
8
|
-
isFile: boolean;
|
|
9
|
-
}
|
|
7
|
+
export type NativeStorageScope = 'documents' | 'documents_legacy' | 'app_data';
|
|
8
|
+
|
|
9
|
+
export type NativeStorageFileStat = CloudStorageFileStat;
|
|
10
10
|
|
|
11
11
|
export enum NativeCloudStorageErrorCode {
|
|
12
12
|
INVALID_SCOPE = 'ERR_INVALID_SCOPE',
|
|
@@ -29,50 +29,6 @@ export enum NativeCloudStorageErrorCode {
|
|
|
29
29
|
NETWORK_ERROR = 'ERR_NETWORK_ERROR',
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
export
|
|
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
|
-
}
|
|
32
|
+
export type NativeLocalFileSystem = NativeCloudStorageLocalFileSystemSpec;
|
|
57
33
|
|
|
58
|
-
export
|
|
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>;
|
|
76
|
-
isCloudAvailable: () => Promise<boolean>;
|
|
77
|
-
triggerSync: (path: string, scope: NativeStorageScope) => Promise<void>;
|
|
78
|
-
}
|
|
34
|
+
export type NativeStorage = Omit<NativeCloudStorageCloudKitSpec, 'onCloudAvailabilityChanged'>;
|
package/src/utils/constants.ts
CHANGED
|
@@ -10,6 +10,7 @@ export const LINKING_ERROR =
|
|
|
10
10
|
export const DEFAULT_PROVIDER_OPTIONS: DeepRequired<CloudStorageProviderOptions> = {
|
|
11
11
|
[CloudStorageProvider.ICloud]: {
|
|
12
12
|
scope: CloudStorageScope.AppData,
|
|
13
|
+
documentsMode: 'icloud',
|
|
13
14
|
},
|
|
14
15
|
[CloudStorageProvider.GoogleDrive]: {
|
|
15
16
|
scope: CloudStorageScope.AppData,
|
package/src/utils/local-fs.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import NativeCloudStorageLocalFileSystem from '../specs/NativeCloudStorageLocalFileSystem';
|
|
2
2
|
import { NativeLocalFileSystem as TNativeLocalFileSystem } from '../types/native';
|
|
3
3
|
import { createProxiedNativeModule } from '../utils/native';
|
|
4
4
|
import { LINKING_ERROR } from './constants';
|
|
5
5
|
|
|
6
6
|
const NativeLocalFileSystem = createProxiedNativeModule<TNativeLocalFileSystem>(
|
|
7
|
-
|
|
7
|
+
NativeCloudStorageLocalFileSystem as unknown as TNativeLocalFileSystem | null
|
|
8
8
|
);
|
|
9
9
|
|
|
10
10
|
export const localFileSystem =
|
package/src/utils/native.ts
CHANGED
|
@@ -6,7 +6,7 @@ import CloudStorageError from './cloud-storage-error';
|
|
|
6
6
|
* @param nativeModule The native module to proxy.
|
|
7
7
|
* @returns The proxied native module.
|
|
8
8
|
*/
|
|
9
|
-
export const createProxiedNativeModule = <T extends object>(nativeModule: T | undefined): T | null => {
|
|
9
|
+
export const createProxiedNativeModule = <T extends object>(nativeModule: T | null | undefined): T | null => {
|
|
10
10
|
if (!nativeModule) return null;
|
|
11
11
|
|
|
12
12
|
return new Proxy(nativeModule, {
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
#import <React/RCTBridgeModule.h>
|
|
2
|
-
|
|
3
|
-
@interface RCT_EXTERN_MODULE(CloudStorageCloudKit, NSObject)
|
|
4
|
-
|
|
5
|
-
RCT_EXTERN_METHOD(fileExists:(NSString *)path withScope:(NSString *)scope withResolver:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject)
|
|
6
|
-
RCT_EXTERN_METHOD(appendToFile:(NSString *)path withData:(NSString *)data withScope:(NSString *)scope withResolver:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject)
|
|
7
|
-
RCT_EXTERN_METHOD(createFile:(NSString *)path withData:(NSString *)data withScope:(NSString *)scope withOverwrite:(BOOL)overwrite withResolver:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject)
|
|
8
|
-
RCT_EXTERN_METHOD(createDirectory:(NSString *)path withScope:(NSString *)scope withResolver:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject)
|
|
9
|
-
RCT_EXTERN_METHOD(listFiles:(NSString *)path withScope:(NSString *)scope withResolver:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject)
|
|
10
|
-
RCT_EXTERN_METHOD(readFile:(NSString *)path withScope:(NSString *)scope withResolver:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject)
|
|
11
|
-
RCT_EXTERN_METHOD(triggerSync:(NSString *)path withScope:(NSString *)scope withResolver:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject)
|
|
12
|
-
RCT_EXTERN_METHOD(deleteFile:(NSString *)path withScope:(NSString *)scope withResolver:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject)
|
|
13
|
-
RCT_EXTERN_METHOD(deleteDirectory:(NSString *)path withRecursive:(BOOL)recursive withScope:(NSString *)scope withResolver:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject)
|
|
14
|
-
RCT_EXTERN_METHOD(statFile:(NSString *)path withScope:(NSString *)scope withResolver:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject)
|
|
15
|
-
RCT_EXTERN_METHOD(downloadFile:(NSString *)remotePath withLocalPath:(NSString *)localPath withScope:(NSString *)scope withResolver:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject)
|
|
16
|
-
RCT_EXTERN_METHOD(uploadFile:(NSString *)remotePath withLocalPath:(NSString *)localPath withMimeType:(NSString *)mimeType withScope:(NSString *)scope withOverwrite:(BOOL)overwrite withResolver:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject)
|
|
17
|
-
RCT_EXTERN_METHOD(isCloudAvailable:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject)
|
|
18
|
-
|
|
19
|
-
+ (BOOL)requiresMainQueueSetup
|
|
20
|
-
{
|
|
21
|
-
return NO;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
@end
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
#import <React/RCTBridgeModule.h>
|
|
2
|
-
#import <React/RCTEventEmitter.h>
|
|
3
|
-
|
|
4
|
-
@interface RCT_EXTERN_MODULE(CloudStorageEventEmitter, RCTEventEmitter)
|
|
5
|
-
|
|
6
|
-
RCT_EXTERN_METHOD(supportedEvents)
|
|
7
|
-
RCT_EXTERN_METHOD(startObserving)
|
|
8
|
-
RCT_EXTERN_METHOD(stopObserving)
|
|
9
|
-
RCT_EXTERN_METHOD(iCloudIdentityChanged:(NSNotification *)notification)
|
|
10
|
-
|
|
11
|
-
+ (BOOL)requiresMainQueueSetup
|
|
12
|
-
{
|
|
13
|
-
return NO;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
@end
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import Foundation
|
|
2
|
-
|
|
3
|
-
@objc(CloudStorageEventEmitter)
|
|
4
|
-
class CloudStorageEventEmitter: RCTEventEmitter {
|
|
5
|
-
public static var shared: CloudStorageEventEmitter!
|
|
6
|
-
|
|
7
|
-
override init() {
|
|
8
|
-
super.init()
|
|
9
|
-
CloudStorageEventEmitter.shared = self
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
override func supportedEvents() -> [String]! {
|
|
13
|
-
["RNCloudStorage.cloud.availability-changed"]
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
override func startObserving() {
|
|
17
|
-
NotificationCenter.default.addObserver(self, selector: #selector(iCloudIdentityChanged(_:)), name: NSNotification.Name.NSUbiquityIdentityDidChange, object: nil)
|
|
18
|
-
// call this immediately after init to trigger an initial value being sent to the JS side
|
|
19
|
-
iCloudIdentityChanged()
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
override func stopObserving() {
|
|
23
|
-
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.NSUbiquityIdentityDidChange, object: nil)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
@objc func iCloudIdentityChanged(_: Notification? = nil) {
|
|
27
|
-
let isAvailable = CloudKitUtils.isCloudKitAvailable()
|
|
28
|
-
CloudStorageEventEmitter.shared.sendEvent(withName: "RNCloudStorage.cloud.availability-changed", body: ["available": isAvailable])
|
|
29
|
-
}
|
|
30
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
#import <React/RCTBridgeModule.h>
|
|
2
|
-
|
|
3
|
-
@interface RCT_EXTERN_MODULE(CloudStorageLocalFileSystem, NSObject)
|
|
4
|
-
|
|
5
|
-
RCT_EXTERN_METHOD(readFile:(NSString *)path withResolver:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject)
|
|
6
|
-
RCT_EXTERN_METHOD(createFile:(NSString *)path withData:(NSString *)data withResolver:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject)
|
|
7
|
-
RCT_EXTERN_METHOD(downloadFile:(NSString *)remoteUri withLocalPath:(NSString *)localPath withOptions:(NSDictionary *)options withResolver:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject)
|
|
8
|
-
RCT_EXTERN_METHOD(uploadFile:(NSString *)localPath withRemoteUri:(NSString *)remoteUri withOptions:(NSDictionary *)options withResolver:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject)
|
|
9
|
-
|
|
10
|
-
+ (BOOL)requiresMainQueueSetup
|
|
11
|
-
{
|
|
12
|
-
return NO;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
@end
|