@unvired/react-native-unvired-sdk 0.0.45 → 0.0.46
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/BuildNo.txt +1 -1
- package/dist/database/DatabaseManager.d.ts +10 -1
- package/dist/database/DatabaseManager.js +22 -2
- package/dist/database/DatabaseManager.web.d.ts +7 -0
- package/dist/database/DatabaseManager.web.js +8 -1
- package/dist/database/index.d.ts +3 -0
- package/dist/database/index.js +3 -0
- package/dist/database/services/DatabaseExpo.d.ts +14 -0
- package/dist/database/services/DatabaseExpo.js +284 -0
- package/dist/database/services/DatabaseNative.d.ts +2 -0
- package/dist/database/services/DatabaseNative.js +134 -41
- package/dist/database/services/DatabaseWeb.d.ts +1 -0
- package/dist/database/services/DatabaseWeb.js +138 -34
- package/dist/device-info/services/DeviceInfoNative.js +50 -3
- package/dist/file-system/FileSystem.d.ts +12 -3
- package/dist/file-system/FileSystem.js +34 -6
- package/dist/file-system/index.d.ts +4 -1
- package/dist/file-system/index.js +3 -0
- package/dist/file-system/services/FileSystemExpo.d.ts +22 -0
- package/dist/file-system/services/FileSystemExpo.js +336 -0
- package/dist/file-system/services/FileSystemNative.d.ts +24 -3
- package/dist/file-system/services/FileSystemNative.js +127 -36
- package/dist/file-system/services/FileSystemWeb.d.ts +1 -1
- package/dist/file-system/services/FileSystemWeb.js +86 -94
- package/dist/local-storage/services/StorageNative.d.ts +1 -0
- package/dist/local-storage/services/StorageNative.js +51 -12
- package/dist/logger/BaseLogger.js +4 -0
- package/dist/main.d.ts +3 -3
- package/dist/main.js +2 -2
- package/dist/push-notification/services/PushNotificationNative.js +182 -68
- package/package.json +21 -1
|
@@ -1,16 +1,26 @@
|
|
|
1
|
-
|
|
1
|
+
let RNFS = null;
|
|
2
|
+
try {
|
|
3
|
+
RNFS = require('react-native-fs');
|
|
4
|
+
}
|
|
5
|
+
catch (e) {
|
|
6
|
+
RNFS = null;
|
|
7
|
+
}
|
|
2
8
|
/**
|
|
3
9
|
* DirectoryReader implementation for React Native
|
|
4
10
|
* Wraps RNFS.readDir to match the Cordova DirectoryReader interface
|
|
5
11
|
*/
|
|
6
12
|
class RNDirectoryReader {
|
|
7
13
|
constructor(dirPath) {
|
|
8
|
-
this.dirPath = dirPath;
|
|
14
|
+
this.dirPath = dirPath.replace(/\/+$/, '');
|
|
9
15
|
}
|
|
10
16
|
readEntries(callback, errorCallback) {
|
|
17
|
+
if (!RNFS) {
|
|
18
|
+
errorCallback(new Error('react-native-fs is not available'));
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
11
21
|
RNFS.readDir(this.dirPath)
|
|
12
|
-
.then(items => {
|
|
13
|
-
const entries = items.map(item => new RNFileEntry(item.path, item.isDirectory()));
|
|
22
|
+
.then((items) => {
|
|
23
|
+
const entries = items.map((item) => new RNFileEntry(item.path, item.isDirectory()));
|
|
14
24
|
callback(entries);
|
|
15
25
|
})
|
|
16
26
|
.catch(errorCallback);
|
|
@@ -22,18 +32,25 @@ class RNDirectoryReader {
|
|
|
22
32
|
*/
|
|
23
33
|
class RNFileEntry {
|
|
24
34
|
constructor(path, isDirectory = false) {
|
|
25
|
-
|
|
26
|
-
this.nativeURL = `file://${path}`;
|
|
35
|
+
const cleanPath = path.replace(/\/+$/, '');
|
|
27
36
|
this.isDirectory = isDirectory;
|
|
28
37
|
this.isFile = !isDirectory;
|
|
29
|
-
|
|
38
|
+
// Cordova spec: directories must end with trailing slash in fullPath and nativeURL
|
|
39
|
+
this.fullPath = isDirectory ? `${cleanPath}/` : cleanPath;
|
|
40
|
+
this.nativeURL = `file://${this.fullPath}`;
|
|
41
|
+
this.name = cleanPath.split('/').pop() || '';
|
|
30
42
|
this.filesystem = null;
|
|
31
43
|
}
|
|
32
44
|
/**
|
|
33
45
|
* Remove a file or empty directory
|
|
34
46
|
*/
|
|
35
47
|
remove(callback, errorCallback) {
|
|
36
|
-
RNFS
|
|
48
|
+
if (!RNFS) {
|
|
49
|
+
errorCallback(new Error('react-native-fs is not available'));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const targetPath = this.fullPath.replace(/\/+$/, '');
|
|
53
|
+
RNFS.unlink(targetPath)
|
|
37
54
|
.then(() => callback())
|
|
38
55
|
.catch(errorCallback);
|
|
39
56
|
}
|
|
@@ -41,8 +58,12 @@ class RNFileEntry {
|
|
|
41
58
|
* Remove a directory and all its contents recursively
|
|
42
59
|
*/
|
|
43
60
|
removeRecursively(callback, errorCallback) {
|
|
44
|
-
|
|
45
|
-
|
|
61
|
+
if (!RNFS) {
|
|
62
|
+
errorCallback(new Error('react-native-fs is not available'));
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const targetPath = this.fullPath.replace(/\/+$/, '');
|
|
66
|
+
RNFS.unlink(targetPath)
|
|
46
67
|
.then(() => callback())
|
|
47
68
|
.catch(errorCallback);
|
|
48
69
|
}
|
|
@@ -56,11 +77,17 @@ class RNFileEntry {
|
|
|
56
77
|
* Get or create a subdirectory
|
|
57
78
|
*/
|
|
58
79
|
getDirectory(path, options, callback, errorCallback) {
|
|
59
|
-
|
|
80
|
+
if (!RNFS) {
|
|
81
|
+
errorCallback(new Error('react-native-fs is not available'));
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const basePath = this.fullPath.replace(/\/+$/, '');
|
|
85
|
+
const relPath = path.replace(/^\/+/, '').replace(/\/+$/, '');
|
|
86
|
+
const dirPath = `${basePath}/${relPath}`;
|
|
60
87
|
RNFS.exists(dirPath)
|
|
61
|
-
.then(exists => {
|
|
88
|
+
.then((exists) => {
|
|
62
89
|
if (exists) {
|
|
63
|
-
return RNFS.stat(dirPath).then(stats => {
|
|
90
|
+
return RNFS.stat(dirPath).then((stats) => {
|
|
64
91
|
if (stats.isDirectory()) {
|
|
65
92
|
callback(new RNFileEntry(dirPath, true));
|
|
66
93
|
}
|
|
@@ -84,11 +111,17 @@ class RNFileEntry {
|
|
|
84
111
|
* Get or create a file
|
|
85
112
|
*/
|
|
86
113
|
getFile(path, options, callback, errorCallback) {
|
|
87
|
-
|
|
114
|
+
if (!RNFS) {
|
|
115
|
+
errorCallback(new Error('react-native-fs is not available'));
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const basePath = this.fullPath.replace(/\/+$/, '');
|
|
119
|
+
const relPath = path.replace(/^\/+/, '');
|
|
120
|
+
const filePath = `${basePath}/${relPath}`;
|
|
88
121
|
RNFS.exists(filePath)
|
|
89
|
-
.then(exists => {
|
|
122
|
+
.then((exists) => {
|
|
90
123
|
if (exists) {
|
|
91
|
-
return RNFS.stat(filePath).then(stats => {
|
|
124
|
+
return RNFS.stat(filePath).then((stats) => {
|
|
92
125
|
if (!stats.isDirectory()) {
|
|
93
126
|
callback(new RNFileEntry(filePath, false));
|
|
94
127
|
}
|
|
@@ -113,16 +146,21 @@ class RNFileEntry {
|
|
|
113
146
|
* Get file metadata
|
|
114
147
|
*/
|
|
115
148
|
file(callback, errorCallback) {
|
|
116
|
-
RNFS
|
|
117
|
-
|
|
149
|
+
if (!RNFS) {
|
|
150
|
+
errorCallback(new Error('react-native-fs is not available'));
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const targetPath = this.fullPath.replace(/\/+$/, '');
|
|
154
|
+
RNFS.stat(targetPath)
|
|
155
|
+
.then((stats) => {
|
|
118
156
|
// Create a File-like object with metadata
|
|
119
157
|
const fileObject = {
|
|
120
|
-
name:
|
|
158
|
+
name: targetPath.split('/').pop() || '',
|
|
121
159
|
size: stats.size,
|
|
122
160
|
type: '', // RNFS doesn't provide MIME type directly
|
|
123
161
|
lastModified: new Date(stats.mtime).getTime(),
|
|
124
162
|
lastModifiedDate: new Date(stats.mtime),
|
|
125
|
-
path:
|
|
163
|
+
path: targetPath,
|
|
126
164
|
};
|
|
127
165
|
callback(fileObject);
|
|
128
166
|
})
|
|
@@ -132,29 +170,35 @@ class RNFileEntry {
|
|
|
132
170
|
* Create a file writer for writing to the file
|
|
133
171
|
*/
|
|
134
172
|
createWriter(callback, errorCallback) {
|
|
135
|
-
|
|
173
|
+
const filePath = this.fullPath.replace(/\/+$/, '');
|
|
136
174
|
const writer = {
|
|
137
|
-
filePath:
|
|
175
|
+
filePath: filePath,
|
|
138
176
|
write: (data) => {
|
|
139
|
-
|
|
177
|
+
if (!RNFS)
|
|
178
|
+
return Promise.reject(new Error('react-native-fs is not available'));
|
|
179
|
+
return RNFS.writeFile(filePath, data, 'utf8')
|
|
140
180
|
.then(() => { if (writer.onwriteend)
|
|
141
181
|
writer.onwriteend(); })
|
|
142
182
|
.catch((err) => { if (writer.onerror)
|
|
143
183
|
writer.onerror(err); });
|
|
144
184
|
},
|
|
145
185
|
append: (data) => {
|
|
146
|
-
|
|
186
|
+
if (!RNFS)
|
|
187
|
+
return Promise.reject(new Error('react-native-fs is not available'));
|
|
188
|
+
return RNFS.appendFile(filePath, data, 'utf8')
|
|
147
189
|
.then(() => { if (writer.onwriteend)
|
|
148
190
|
writer.onwriteend(); })
|
|
149
191
|
.catch((err) => { if (writer.onerror)
|
|
150
192
|
writer.onerror(err); });
|
|
151
193
|
},
|
|
152
194
|
truncate: (size) => {
|
|
195
|
+
if (!RNFS)
|
|
196
|
+
return Promise.reject(new Error('react-native-fs is not available'));
|
|
153
197
|
// Read file, truncate content, and write back
|
|
154
|
-
return RNFS.readFile(
|
|
155
|
-
.then(content => {
|
|
198
|
+
return RNFS.readFile(filePath, 'utf8')
|
|
199
|
+
.then((content) => {
|
|
156
200
|
const truncated = content.substring(0, size);
|
|
157
|
-
return RNFS.writeFile(
|
|
201
|
+
return RNFS.writeFile(filePath, truncated, 'utf8')
|
|
158
202
|
.then(() => { if (writer.onwriteend)
|
|
159
203
|
writer.onwriteend(); });
|
|
160
204
|
})
|
|
@@ -162,8 +206,6 @@ class RNFileEntry {
|
|
|
162
206
|
writer.onerror(err); });
|
|
163
207
|
},
|
|
164
208
|
seek: (offset) => {
|
|
165
|
-
// Not directly supported in RNFS
|
|
166
|
-
// This would need to be implemented with read/write operations
|
|
167
209
|
return Promise.resolve();
|
|
168
210
|
},
|
|
169
211
|
onwriteend: null,
|
|
@@ -178,20 +220,35 @@ class RNFileEntry {
|
|
|
178
220
|
}
|
|
179
221
|
}
|
|
180
222
|
export class FileSystemNative {
|
|
223
|
+
/**
|
|
224
|
+
* Check if react-native-fs is available in runtime
|
|
225
|
+
*/
|
|
226
|
+
static isAvailable() {
|
|
227
|
+
return !!RNFS && !!RNFS.DocumentDirectoryPath;
|
|
228
|
+
}
|
|
181
229
|
getDocumentDirectory() {
|
|
182
|
-
return RNFS.DocumentDirectoryPath;
|
|
230
|
+
return (RNFS === null || RNFS === void 0 ? void 0 : RNFS.DocumentDirectoryPath) || '';
|
|
183
231
|
}
|
|
184
232
|
async resolveLocalFileSystemURL(url) {
|
|
233
|
+
if (!RNFS) {
|
|
234
|
+
throw new Error('react-native-fs is not available');
|
|
235
|
+
}
|
|
185
236
|
return new Promise((resolve, reject) => {
|
|
186
|
-
// Remove file:// prefix if
|
|
187
|
-
|
|
237
|
+
// Remove file:// prefix, decode URI components if encoded, and normalize multiple slashes
|
|
238
|
+
let path = url.replace(/^file:\/\//, '');
|
|
239
|
+
try {
|
|
240
|
+
path = decodeURIComponent(path);
|
|
241
|
+
}
|
|
242
|
+
catch (e) { }
|
|
243
|
+
// Normalize internal multiple slashes to single slash, and strip trailing slash for RNFS.stat/exists
|
|
244
|
+
path = path.replace(/\/+/g, '/').replace(/\/+$/, '');
|
|
188
245
|
RNFS.exists(path)
|
|
189
|
-
.then(exists => {
|
|
246
|
+
.then((exists) => {
|
|
190
247
|
if (!exists) {
|
|
191
248
|
reject(new Error(`Path does not exist: ${path}`));
|
|
192
249
|
return;
|
|
193
250
|
}
|
|
194
|
-
return RNFS.stat(path).then(stats => {
|
|
251
|
+
return RNFS.stat(path).then((stats) => {
|
|
195
252
|
resolve(new RNFileEntry(path, stats.isDirectory()));
|
|
196
253
|
});
|
|
197
254
|
})
|
|
@@ -199,6 +256,9 @@ export class FileSystemNative {
|
|
|
199
256
|
});
|
|
200
257
|
}
|
|
201
258
|
async getFolderBasedOnUserId(userId) {
|
|
259
|
+
if (!RNFS) {
|
|
260
|
+
throw new Error('react-native-fs is not available');
|
|
261
|
+
}
|
|
202
262
|
const userPath = `${RNFS.DocumentDirectoryPath}/${userId}`;
|
|
203
263
|
try {
|
|
204
264
|
const exists = await RNFS.exists(userPath);
|
|
@@ -206,14 +266,18 @@ export class FileSystemNative {
|
|
|
206
266
|
// Create the directory if it doesn't exist
|
|
207
267
|
await RNFS.mkdir(userPath);
|
|
208
268
|
}
|
|
209
|
-
// Return the native URL format
|
|
210
|
-
|
|
269
|
+
// Return the native URL format with trailing slash!
|
|
270
|
+
// Crucial: Core SDK appends 'Attachment/' or 'Inbox/' without a leading slash.
|
|
271
|
+
return `file://${userPath}/`;
|
|
211
272
|
}
|
|
212
273
|
catch (error) {
|
|
213
274
|
throw new Error(`Failed to get/create folder for user ${userId}: ${error}`);
|
|
214
275
|
}
|
|
215
276
|
}
|
|
216
277
|
async deleteUserFolder(userId) {
|
|
278
|
+
if (!RNFS) {
|
|
279
|
+
throw new Error('react-native-fs is not available');
|
|
280
|
+
}
|
|
217
281
|
const userPath = `${RNFS.DocumentDirectoryPath}/${userId}`;
|
|
218
282
|
try {
|
|
219
283
|
const exists = await RNFS.exists(userPath);
|
|
@@ -227,36 +291,63 @@ export class FileSystemNative {
|
|
|
227
291
|
}
|
|
228
292
|
}
|
|
229
293
|
async createDirectory(path) {
|
|
294
|
+
if (!RNFS) {
|
|
295
|
+
throw new Error('react-native-fs is not available');
|
|
296
|
+
}
|
|
230
297
|
const exists = await RNFS.exists(path);
|
|
231
298
|
if (!exists) {
|
|
232
299
|
await RNFS.mkdir(path);
|
|
233
300
|
}
|
|
234
301
|
}
|
|
235
302
|
async readFile(path, encoding = 'utf8') {
|
|
303
|
+
if (!RNFS) {
|
|
304
|
+
throw new Error('react-native-fs is not available');
|
|
305
|
+
}
|
|
236
306
|
return await RNFS.readFile(path, encoding);
|
|
237
307
|
}
|
|
238
308
|
async writeFile(path, content, encoding = 'utf8') {
|
|
309
|
+
if (!RNFS) {
|
|
310
|
+
throw new Error('react-native-fs is not available');
|
|
311
|
+
}
|
|
239
312
|
await RNFS.writeFile(path, content, encoding);
|
|
240
313
|
}
|
|
241
314
|
async deleteFile(path) {
|
|
315
|
+
if (!RNFS) {
|
|
316
|
+
throw new Error('react-native-fs is not available');
|
|
317
|
+
}
|
|
242
318
|
const exists = await RNFS.exists(path);
|
|
243
319
|
if (exists) {
|
|
244
320
|
await RNFS.unlink(path);
|
|
245
321
|
}
|
|
246
322
|
}
|
|
247
323
|
async exists(path) {
|
|
324
|
+
if (!RNFS) {
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
248
327
|
return await RNFS.exists(path);
|
|
249
328
|
}
|
|
250
329
|
async stat(path) {
|
|
330
|
+
if (!RNFS) {
|
|
331
|
+
throw new Error('react-native-fs is not available');
|
|
332
|
+
}
|
|
251
333
|
return await RNFS.stat(path);
|
|
252
334
|
}
|
|
253
335
|
async readDir(path) {
|
|
336
|
+
if (!RNFS) {
|
|
337
|
+
throw new Error('react-native-fs is not available');
|
|
338
|
+
}
|
|
254
339
|
return await RNFS.readDir(path);
|
|
255
340
|
}
|
|
256
341
|
async copyFile(source, destination) {
|
|
342
|
+
if (!RNFS) {
|
|
343
|
+
throw new Error('react-native-fs is not available');
|
|
344
|
+
}
|
|
257
345
|
await RNFS.copyFile(source, destination);
|
|
258
346
|
}
|
|
259
347
|
async moveFile(source, destination) {
|
|
348
|
+
if (!RNFS) {
|
|
349
|
+
throw new Error('react-native-fs is not available');
|
|
350
|
+
}
|
|
260
351
|
await RNFS.moveFile(source, destination);
|
|
261
352
|
}
|
|
262
353
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { IFileEntry, IFileSystem, FileSystemEncoding } from "../FileSystem";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* FileSystem implementation for Web using localStorage
|
|
4
4
|
*/
|
|
5
5
|
export declare class FileSystemWeb implements IFileSystem {
|
|
6
6
|
private baseDir;
|
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
class WebDirectoryReader {
|
|
2
|
-
constructor(
|
|
3
|
-
this.
|
|
2
|
+
constructor(dirPath) {
|
|
3
|
+
this.dirPath = dirPath.replace(/\/+$/, "");
|
|
4
4
|
}
|
|
5
|
-
readEntries(
|
|
5
|
+
readEntries(callback, errorCallback) {
|
|
6
6
|
try {
|
|
7
7
|
const entries = [];
|
|
8
|
-
const prefix = `__vfs_${this.
|
|
8
|
+
const prefix = `__vfs_${this.dirPath}/`;
|
|
9
9
|
if (typeof localStorage !== "undefined") {
|
|
10
10
|
for (let i = 0; i < localStorage.length; i++) {
|
|
11
11
|
const key = localStorage.key(i);
|
|
12
12
|
if (key && key.startsWith(prefix)) {
|
|
13
13
|
const relPath = key.substring(prefix.length);
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
entries.push(new WebFileEntry(
|
|
14
|
+
const name = relPath.split("/")[0];
|
|
15
|
+
const fullPath = `${this.dirPath}/${name}`;
|
|
16
|
+
if (!entries.find((e) => e.name === name)) {
|
|
17
|
+
const isDir = relPath.includes("/");
|
|
18
|
+
entries.push(new WebFileEntry(fullPath, isDir));
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
|
-
|
|
23
|
+
callback(entries);
|
|
24
24
|
}
|
|
25
25
|
catch (e) {
|
|
26
26
|
errorCallback(e);
|
|
@@ -29,16 +29,19 @@ class WebDirectoryReader {
|
|
|
29
29
|
}
|
|
30
30
|
class WebFileEntry {
|
|
31
31
|
constructor(fullPath, isDirectory) {
|
|
32
|
-
|
|
33
|
-
this.name =
|
|
32
|
+
const cleanPath = fullPath.replace(/\/+/g, "/").replace(/\/+$/, "");
|
|
33
|
+
this.name = cleanPath.split("/").filter(Boolean).pop() || "root";
|
|
34
34
|
this.isFile = !isDirectory;
|
|
35
35
|
this.isDirectory = isDirectory;
|
|
36
|
+
// Cordova spec: directories must end with trailing slash in fullPath and nativeURL
|
|
37
|
+
this.fullPath = isDirectory ? `${cleanPath}/` : cleanPath;
|
|
36
38
|
this.nativeURL = `file://${this.fullPath}`;
|
|
37
39
|
}
|
|
38
40
|
remove(callback, errorCallback) {
|
|
39
41
|
try {
|
|
40
42
|
if (typeof localStorage !== "undefined") {
|
|
41
|
-
|
|
43
|
+
const targetPath = this.fullPath.replace(/\/+$/, "");
|
|
44
|
+
localStorage.removeItem(`__vfs_${targetPath}`);
|
|
42
45
|
}
|
|
43
46
|
callback();
|
|
44
47
|
}
|
|
@@ -49,7 +52,8 @@ class WebFileEntry {
|
|
|
49
52
|
removeRecursively(callback, errorCallback) {
|
|
50
53
|
try {
|
|
51
54
|
if (typeof localStorage !== "undefined") {
|
|
52
|
-
const
|
|
55
|
+
const targetPath = this.fullPath.replace(/\/+$/, "");
|
|
56
|
+
const prefix = `__vfs_${targetPath}`;
|
|
53
57
|
const keysToRemove = [];
|
|
54
58
|
for (let i = 0; i < localStorage.length; i++) {
|
|
55
59
|
const key = localStorage.key(i);
|
|
@@ -69,96 +73,71 @@ class WebFileEntry {
|
|
|
69
73
|
return new WebDirectoryReader(this.fullPath);
|
|
70
74
|
}
|
|
71
75
|
getDirectory(path, options, callback, errorCallback) {
|
|
72
|
-
const
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
+
const basePath = this.fullPath.replace(/\/+$/, "");
|
|
77
|
+
const relPath = path.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
78
|
+
const dirPath = `${basePath}/${relPath}`;
|
|
79
|
+
if (options.create) {
|
|
80
|
+
if (typeof localStorage !== "undefined") {
|
|
81
|
+
localStorage.setItem(`__vfs_${dirPath}`, JSON.stringify({ isDir: true }));
|
|
82
|
+
}
|
|
76
83
|
}
|
|
77
|
-
callback(
|
|
84
|
+
callback(new WebFileEntry(dirPath, true));
|
|
78
85
|
}
|
|
79
86
|
getFile(path, options, callback, errorCallback) {
|
|
80
|
-
const
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
87
|
+
const basePath = this.fullPath.replace(/\/+$/, "");
|
|
88
|
+
const relPath = path.replace(/^\/+/, "");
|
|
89
|
+
const filePath = `${basePath}/${relPath}`;
|
|
90
|
+
if (options.create) {
|
|
91
|
+
if (typeof localStorage !== "undefined") {
|
|
92
|
+
if (localStorage.getItem(`__vfs_${filePath}`) === null) {
|
|
93
|
+
localStorage.setItem(`__vfs_${filePath}`, "");
|
|
94
|
+
}
|
|
85
95
|
}
|
|
86
96
|
}
|
|
87
|
-
|
|
88
|
-
callback(entry);
|
|
97
|
+
callback(new WebFileEntry(filePath, false));
|
|
89
98
|
}
|
|
90
99
|
file(callback, errorCallback) {
|
|
91
100
|
try {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
size: content.length,
|
|
99
|
-
type: "text/plain",
|
|
100
|
-
lastModified: Date.now(),
|
|
101
|
-
lastModifiedDate: new Date(),
|
|
102
|
-
path: this.fullPath,
|
|
103
|
-
};
|
|
104
|
-
callback(fileObj);
|
|
101
|
+
const targetPath = this.fullPath.replace(/\/+$/, "");
|
|
102
|
+
const content = typeof localStorage !== "undefined"
|
|
103
|
+
? localStorage.getItem(`__vfs_${targetPath}`) || ""
|
|
104
|
+
: "";
|
|
105
|
+
const blob = new Blob([content], { type: "text/plain" });
|
|
106
|
+
callback(blob);
|
|
105
107
|
}
|
|
106
108
|
catch (e) {
|
|
107
109
|
errorCallback(e);
|
|
108
110
|
}
|
|
109
111
|
}
|
|
110
112
|
createWriter(callback, errorCallback) {
|
|
111
|
-
const filePath = this.fullPath;
|
|
113
|
+
const filePath = this.fullPath.replace(/\/+$/, "");
|
|
112
114
|
const writer = {
|
|
113
|
-
|
|
114
|
-
write: (data) => {
|
|
115
|
-
try {
|
|
116
|
-
if (typeof localStorage !== "undefined") {
|
|
117
|
-
localStorage.setItem(`__vfs_${filePath}`, data);
|
|
118
|
-
}
|
|
119
|
-
if (writer.onwriteend)
|
|
120
|
-
writer.onwriteend();
|
|
121
|
-
return Promise.resolve();
|
|
122
|
-
}
|
|
123
|
-
catch (err) {
|
|
124
|
-
if (writer.onerror)
|
|
125
|
-
writer.onerror(err);
|
|
126
|
-
return Promise.reject(err);
|
|
127
|
-
}
|
|
128
|
-
},
|
|
129
|
-
append: (data) => {
|
|
115
|
+
write: (blob) => {
|
|
130
116
|
try {
|
|
131
|
-
if (
|
|
132
|
-
const
|
|
133
|
-
|
|
117
|
+
if (blob instanceof Blob) {
|
|
118
|
+
const reader = new FileReader();
|
|
119
|
+
reader.onloadend = () => {
|
|
120
|
+
if (typeof localStorage !== "undefined") {
|
|
121
|
+
localStorage.setItem(`__vfs_${filePath}`, reader.result);
|
|
122
|
+
}
|
|
123
|
+
if (writer.onwriteend)
|
|
124
|
+
writer.onwriteend();
|
|
125
|
+
};
|
|
126
|
+
reader.readAsText(blob);
|
|
134
127
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
writer.onerror(err);
|
|
142
|
-
return Promise.reject(err);
|
|
143
|
-
}
|
|
144
|
-
},
|
|
145
|
-
truncate: (size) => {
|
|
146
|
-
try {
|
|
147
|
-
if (typeof localStorage !== "undefined") {
|
|
148
|
-
const existing = localStorage.getItem(`__vfs_${filePath}`) || "";
|
|
149
|
-
localStorage.setItem(`__vfs_${filePath}`, existing.substring(0, size));
|
|
128
|
+
else {
|
|
129
|
+
if (typeof localStorage !== "undefined") {
|
|
130
|
+
localStorage.setItem(`__vfs_${filePath}`, String(blob));
|
|
131
|
+
}
|
|
132
|
+
if (writer.onwriteend)
|
|
133
|
+
writer.onwriteend();
|
|
150
134
|
}
|
|
151
|
-
if (writer.onwriteend)
|
|
152
|
-
writer.onwriteend();
|
|
153
|
-
return Promise.resolve();
|
|
154
135
|
}
|
|
155
|
-
catch (
|
|
136
|
+
catch (e) {
|
|
156
137
|
if (writer.onerror)
|
|
157
|
-
writer.onerror(
|
|
158
|
-
return Promise.reject(err);
|
|
138
|
+
writer.onerror(e);
|
|
159
139
|
}
|
|
160
140
|
},
|
|
161
|
-
seek: () => Promise.resolve(),
|
|
162
141
|
onwriteend: null,
|
|
163
142
|
onerror: null,
|
|
164
143
|
};
|
|
@@ -166,23 +145,29 @@ class WebFileEntry {
|
|
|
166
145
|
}
|
|
167
146
|
}
|
|
168
147
|
/**
|
|
169
|
-
*
|
|
148
|
+
* FileSystem implementation for Web using localStorage
|
|
170
149
|
*/
|
|
171
150
|
export class FileSystemWeb {
|
|
172
151
|
constructor() {
|
|
173
|
-
this.baseDir = "/
|
|
152
|
+
this.baseDir = "/unvired_app_fs";
|
|
174
153
|
}
|
|
175
154
|
getDocumentDirectory() {
|
|
176
155
|
return this.baseDir;
|
|
177
156
|
}
|
|
178
157
|
async resolveLocalFileSystemURL(url) {
|
|
179
|
-
|
|
158
|
+
let path = url.replace(/^file:\/\//, "");
|
|
159
|
+
try {
|
|
160
|
+
path = decodeURIComponent(path);
|
|
161
|
+
}
|
|
162
|
+
catch (e) { }
|
|
163
|
+
path = path.replace(/\/+/g, "/").replace(/\/+$/, "");
|
|
180
164
|
return new WebFileEntry(path, false);
|
|
181
165
|
}
|
|
182
166
|
async getFolderBasedOnUserId(userId) {
|
|
183
167
|
const folder = `${this.baseDir}/users/${userId}`;
|
|
184
168
|
await this.createDirectory(folder);
|
|
185
|
-
|
|
169
|
+
// Return native URL with trailing slash!
|
|
170
|
+
return `file://${folder}/`;
|
|
186
171
|
}
|
|
187
172
|
async deleteUserFolder(userId) {
|
|
188
173
|
const prefix = `__vfs_${this.baseDir}/users/${userId}`;
|
|
@@ -198,38 +183,44 @@ export class FileSystemWeb {
|
|
|
198
183
|
}
|
|
199
184
|
}
|
|
200
185
|
async createDirectory(path) {
|
|
186
|
+
const cleanPath = path.replace(/\/+$/, "");
|
|
201
187
|
if (typeof localStorage !== "undefined") {
|
|
202
|
-
localStorage.setItem(`__vfs_${
|
|
188
|
+
localStorage.setItem(`__vfs_${cleanPath}`, JSON.stringify({ isDir: true }));
|
|
203
189
|
}
|
|
204
190
|
}
|
|
205
191
|
async readFile(path, encoding = "utf8") {
|
|
192
|
+
const cleanPath = path.replace(/\/+$/, "");
|
|
206
193
|
if (typeof localStorage !== "undefined") {
|
|
207
|
-
const val = localStorage.getItem(`__vfs_${
|
|
194
|
+
const val = localStorage.getItem(`__vfs_${cleanPath}`);
|
|
208
195
|
if (val !== null)
|
|
209
196
|
return val;
|
|
210
197
|
}
|
|
211
198
|
throw new Error(`File not found: ${path}`);
|
|
212
199
|
}
|
|
213
200
|
async writeFile(path, content, encoding = "utf8") {
|
|
201
|
+
const cleanPath = path.replace(/\/+$/, "");
|
|
214
202
|
if (typeof localStorage !== "undefined") {
|
|
215
|
-
localStorage.setItem(`__vfs_${
|
|
203
|
+
localStorage.setItem(`__vfs_${cleanPath}`, content);
|
|
216
204
|
}
|
|
217
205
|
}
|
|
218
206
|
async deleteFile(path) {
|
|
207
|
+
const cleanPath = path.replace(/\/+$/, "");
|
|
219
208
|
if (typeof localStorage !== "undefined") {
|
|
220
|
-
localStorage.removeItem(`__vfs_${
|
|
209
|
+
localStorage.removeItem(`__vfs_${cleanPath}`);
|
|
221
210
|
}
|
|
222
211
|
}
|
|
223
212
|
async exists(path) {
|
|
213
|
+
const cleanPath = path.replace(/\/+$/, "");
|
|
224
214
|
if (typeof localStorage !== "undefined") {
|
|
225
|
-
return localStorage.getItem(`__vfs_${
|
|
215
|
+
return localStorage.getItem(`__vfs_${cleanPath}`) !== null;
|
|
226
216
|
}
|
|
227
217
|
return false;
|
|
228
218
|
}
|
|
229
219
|
async stat(path) {
|
|
220
|
+
const cleanPath = path.replace(/\/+$/, "");
|
|
230
221
|
let size = 0;
|
|
231
222
|
if (typeof localStorage !== "undefined") {
|
|
232
|
-
const val = localStorage.getItem(`__vfs_${
|
|
223
|
+
const val = localStorage.getItem(`__vfs_${cleanPath}`);
|
|
233
224
|
if (val !== null)
|
|
234
225
|
size = val.length;
|
|
235
226
|
}
|
|
@@ -242,7 +233,8 @@ export class FileSystemWeb {
|
|
|
242
233
|
};
|
|
243
234
|
}
|
|
244
235
|
async readDir(path) {
|
|
245
|
-
const
|
|
236
|
+
const cleanPath = path.replace(/\/+$/, "");
|
|
237
|
+
const prefix = `__vfs_${cleanPath}/`;
|
|
246
238
|
const results = [];
|
|
247
239
|
if (typeof localStorage !== "undefined") {
|
|
248
240
|
for (let i = 0; i < localStorage.length; i++) {
|
|
@@ -252,7 +244,7 @@ export class FileSystemWeb {
|
|
|
252
244
|
if (!results.find((r) => r.name === name)) {
|
|
253
245
|
results.push({
|
|
254
246
|
name,
|
|
255
|
-
path: `${
|
|
247
|
+
path: `${cleanPath}/${name}`,
|
|
256
248
|
size: (localStorage.getItem(key) || "").length,
|
|
257
249
|
isFile: () => true,
|
|
258
250
|
isDirectory: () => false,
|